<commit_before>/* kate-script
 * author: Dominik Haumann <dhdev@gmx.de>, Milian Wolff <mail@milianw.de>
 * license: LGPL
 * revision: 2
 * kate-version: 3.4
 * type: commands
 * functions: sort, moveLinesDown, moveLinesUp, natsort, uniq, rtrim, ltrim, trim, join, rmblank, unwrap, each, filter, map
 */

function sort()
{
    each(function(lines){return lines.sort()});
}

function uniq()
{
    each(function(lines) {
        for ( var i = 1; i < lines.length; ++i ) {
          for ( var j = i - 1; j >= 0; --j ) {
            if ( lines[i] == lines[j] ) {
              lines.splice(i, 1);
              // gets increased in the for
              --i;
              break;
            }
          }
        }
        return lines;
    });
}

function natsort()
{
    each(function(lines){return lines.sort(natcompare);});
}

function rtrim()
{
    map(function(l){ return l.replace(/\s+$/, ''); });
}

function ltrim()
{
    map(function(l){ return l.replace(/^\s+/, ''); });
}

function trim()
{
    map(function(l){ return l.replace(/^\s+|\s+$/, ''); });
}

function rmblank()
{
    filter(function(l) { return l.length > 0; });
}

///TODO: make it possible to pass a deliminator
///      but this requires a better cmd interpretation
///      in katepart. currently e.g. `join ' | '` passes three
///      arguments (', |, ') to the function, instead one with
///      the whitespaces...
function join()
{
    each(function(lines){
        return [lines.join("")];
    });
}

// unwrap does the opposite of the script word wrap
function unwrap ()
{
    var selectionRange = view.selection();
    if (selectionRange.isValid()) {
        // unwrap all paragraphs in the selection range
        var currentLine = selectionRange.start.line;
        var count = selectionRange.end.line - selectionRange.start.line;

        document.editBegin();
        while (count >= 0) {
            // skip empty lines
            while (count >= 0 && document.firstColumn(currentLine) == -1) {
                --count;
                ++currentLine;
            }

            // find block of text lines to join
            var anchorLine = currentLine;
            while (count >= 0 && document.firstColumn(currentLine) != -1) {
                --count;
                ++currentLine;
            }

            if (currentLine != anchorLine) {
                document.joinLines(anchorLine, currentLine - 1);
                currentLine -= currentLine - anchorLine - 1;
            }
        }
        document.editEnd();
    } else {
        // unwrap paragraph under the cursor
        var cursorPosition = view.cursorPosition();
        if (document.firstColumn(cursorPosition.line) != -1) {
            var startLine = cursorPosition.line;
            while (startLine > 0) {
                if (document.firstColumn(startLine - 1) == -1) {
                    break;
                }
                --startLine;
            }

            var endLine = cursorPosition.line;
            var lineCount = document.lines();
            while (endLine < lineCount) {
                if (document.firstColumn(endLine + 1) == -1) {
                    break;
                }
                ++endLine;
            }

            if (startLine != endLine) {
                document.editBegin();
                document.joinLines(startLine, endLine);
                document.editEnd();
            }
        }
    }
}

function moveLinesDown()
{
    var fromLine = -1;
    var toLine = -1;

    var selectionRange = view.selection();
    if (selectionRange.isValid() && selectionRange.end.line < document.lines() - 1) {
        toLine = selectionRange.start.line;
        fromLine = selectionRange.end.line + 1;
    } else if (view.cursorPosition().line < document.lines() - 1) {
        toLine = view.cursorPosition().line;
        fromLine = toLine + 1;
    }

    if (fromLine != -1 && toLine != -1) {
        var text = document.line(fromLine);

        document.editBegin();
        document.removeLine(fromLine);
        document.insertLine(toLine, text);
        document.editEnd();
    }
}

function moveLinesUp()
{
    var fromLine = -1;
    var toLine = -1;

    var selectionRange = view.selection();
    if (selectionRange.isValid() && selectionRange.start.line > 0) {
        fromLine = selectionRange.start.line - 1;
        toLine = selectionRange.end.line;
    } else if (view.cursorPosition().line > 0) {
        toLine = view.cursorPosition().line;
        fromLine = toLine - 1;
    }

    if (fromLine != -1 && toLine != -1) {
        var text = document.line(fromLine);

        document.editBegin();
        document.removeLine(fromLine);
        document.insertLine(toLine, text);
        document.editEnd();
    }
}

function action(cmd)
{
    var a = new Object();
    if (cmd == "sort") {
        a.text = i18n("Sort Selected Text");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    } else if (cmd == "moveLinesDown") {
        a.text = i18n("Move Lines Down");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    } else if (cmd == "moveLinesUp") {
        a.text = i18n("Move Lines Up");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    }

    return a;
}

function help(cmd)
{
    if (cmd == "sort") {
        return i18n("Sort the selected text or whole document.");
    } else if (cmd == "moveLinesDown") {
        return i18n("Move selected lines down.");
    } else if (cmd == "moveLinesUp") {
        return i18n("Move selected lines up.");
    } else if (cmd == "uniq") {
        return i18n("Remove duplicate lines from the selected text or whole document.");
    } else if (cmd == "natsort") {
        return i18n("Sort the selected text or whole document in natural order.
"
              +"Here's an example to show the difference to the normal sort method:
"
              +"sort(a10, a1, a2) => a1, a10, a2
"
              +"natsort(a10, a1, a2) => a1, a2, a10");
    } else if (cmd == "rtrim") {
        return i18n("Trims trailing whitespace from selection or whole document.");
    } else if (cmd == "ltrim") {
        return i18n("Trims leading whitespace from selection or whole document.");
    } else if (cmd == "trim") {
        return i18n("Trims leading and trailing whitespace from selection or whole document.");
    } else if (cmd == "join") {
        return i18n("Joins selected lines or whole document.");
    } else if (cmd == "rmblank") {
        return i18n("Removes empty lines from selection or whole document.");
    } else if (cmd == "unwrap") {
        return "Unwraps all paragraphs in the text selection, or the paragraph under the text cursor if there is no selected text.";
    } else if (cmd == "each") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines and replace them with the" +
               "return value of that callback.
" +
               "Example (join selected lines):
" +
                "each 'function(lines){return lines.join(\", \"}'");
    } else if (cmd == "filter") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines " +
                    "and remove those where the callback returns false.
" +
                    "Example (see also rmblank):
" +
                    "filter 'function(l){return l.length > 0;}'");
    } else if (cmd == "map") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines " +
                    "and replace the line with the return value of the callback.
" +
                    "Example (see also ltrim):
" +
                    "map 'function(l){return l.replace(/^\s+/, '');}'");
    }
}

/// helper code below:

function each(func)
{
    if ( typeof(func) != "function" ) {
        func = eval("(" + func + ")");
        if ( typeof(func) != "function" ) {
          debug("parameter is not a function: " + typeof(func));
          return;
        } else {
          debug("using argument as evaluated callback")
        }
    }

    var selection = view.selection();
    if (!selection.isValid()) {
        // use whole range
        selection = document.documentRange();
    } else {
        selection.start.column = 0;
        selection.end.column = document.lineLength(selection.end.line);
    }

    var text = document.text(selection);

    var lines = text.split("\n");
    lines = func(lines);
    if ( typeof(lines) == "object" ) {
      text = lines.join("\n");
    } else if ( typeof(lines) == "string" ) {
      text = lines
    } else {
      throw "callback function for each has to return object or array of lines";
    }

    view.clearSelection();

    document.editBegin();
    document.removeText(selection);
    document.insertText(selection.start, text);
    document.editEnd();
}

function filter(func)
{
    if ( typeof(func) == "function" ) {
        each(function(lines) { return lines.filter(func); });
    } else {
        each('function(lines){ return lines.filter(' + func + '); }');
    }
}

function map(func)
{
    if ( typeof(func) == "function" ) {
        each(function(lines) { return lines.map(func); });
    } else {
        each('function(lines){ return lines.map(' + func + '); }');
    }
}

/*
natcompare.js -- Perform 'natural order' comparisons of strings in JavaScript.
Copyright (C) 2005 by SCK-CEN (Belgian Nucleair Research Centre)
Written by Kristof Coomans <kristof[dot]coomans[at]sckcen[dot]be>

Based on the Java version by Pierre-Luc Paour, of which this is more or less a straight conversion.
Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>

The Java version was based on the C version by Martin Pool.
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

function isWhitespaceChar(a)
{
    var charCode;
    charCode = a.charCodeAt(0);

    if ( charCode <= 32 )
    {
        return true;
    }
    else
    {
        return false;
    }
}

function isDigitChar(a)
{
    var charCode;
    charCode = a.charCodeAt(0);

    if ( charCode >= 48  && charCode <= 57 )
    {
        return true;
    }
    else
    {
        return false;
    }
}

function compareRight(a,b)
{
    var bias = 0;
    var ia = 0;
    var ib = 0;

    var ca;
    var cb;

    // The longest run of digits wins.  That aside, the greatest
    // value wins, but we can't know that it will until we've scanned
    // both numbers to know that they have the same magnitude, so we
    // remember it in BIAS.
    for (;; ia++, ib++) {
        ca = a.charAt(ia);
        cb = b.charAt(ib);

        if (!isDigitChar(ca)
                && !isDigitChar(cb)) {
            return bias;
        } else if (!isDigitChar(ca)) {
            return -1;
        } else if (!isDigitChar(cb)) {
            return +1;
        } else if (ca < cb) {
            if (bias == 0) {
                bias = -1;
            }
        } else if (ca > cb) {
            if (bias == 0)
                bias = +1;
        } else if (ca == 0 && cb == 0) {
            return bias;
        }
    }
}

function natcompare(a,b) {

    var ia = 0, ib = 0;
        var nza = 0, nzb = 0;
        var ca, cb;
        var result;

    while (true)
    {
        // only count the number of zeroes leading the last number compared
        nza = nzb = 0;

        ca = a.charAt(ia);
        cb = b.charAt(ib);

        // skip over leading spaces or zeros
        while ( isWhitespaceChar( ca ) || ca =='0' ) {
            if (ca == '0') {
                nza++;
            } else {
                // only count consecutive zeroes
                nza = 0;
            }

            ca = a.charAt(++ia);
        }

        while ( isWhitespaceChar( cb ) || cb == '0') {
            if (cb == '0') {
                nzb++;
            } else {
                // only count consecutive zeroes
                nzb = 0;
            }

            cb = b.charAt(++ib);
        }

        // process run of digits
        if (isDigitChar(ca) && isDigitChar(cb)) {
            if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
                return result;
            }
        }

        if (ca == 0 && cb == 0) {
            // The strings compare the same.  Perhaps the caller
            // will want to call strcmp to break the tie.
            return nza - nzb;
        }

        if (ca < cb) {
            return -1;
        } else if (ca > cb) {
            return +1;
        }

        ++ia; ++ib;
    }
}

// kate: space-indent on; indent-width 4; replace-tabs on;
<commit_msg>mwolff:     it's now possible to pass a proper separator to join() since we use the correct KShell functions sine some time<commit_after>/* kate-script
 * author: Dominik Haumann <dhdev@gmx.de>, Milian Wolff <mail@milianw.de>
 * license: LGPL
 * revision: 2
 * kate-version: 3.4
 * type: commands
 * functions: sort, moveLinesDown, moveLinesUp, natsort, uniq, rtrim, ltrim, trim, join, rmblank, unwrap, each, filter, map
 */

function sort()
{
    each(function(lines){return lines.sort()});
}

function uniq()
{
    each(function(lines) {
        for ( var i = 1; i < lines.length; ++i ) {
          for ( var j = i - 1; j >= 0; --j ) {
            if ( lines[i] == lines[j] ) {
              lines.splice(i, 1);
              // gets increased in the for
              --i;
              break;
            }
          }
        }
        return lines;
    });
}

function natsort()
{
    each(function(lines){return lines.sort(natcompare);});
}

function rtrim()
{
    map(function(l){ return l.replace(/\s+$/, ''); });
}

function ltrim()
{
    map(function(l){ return l.replace(/^\s+/, ''); });
}

function trim()
{
    map(function(l){ return l.replace(/^\s+|\s+$/, ''); });
}

function rmblank()
{
    filter(function(l) { return l.length > 0; });
}

function join(separator)
{
    if (typeof(separator) != "string") {
        separator = "";
    }
    each(function(lines){
        return [lines.join(separator)];
    });
}

// unwrap does the opposite of the script word wrap
function unwrap ()
{
    var selectionRange = view.selection();
    if (selectionRange.isValid()) {
        // unwrap all paragraphs in the selection range
        var currentLine = selectionRange.start.line;
        var count = selectionRange.end.line - selectionRange.start.line;

        document.editBegin();
        while (count >= 0) {
            // skip empty lines
            while (count >= 0 && document.firstColumn(currentLine) == -1) {
                --count;
                ++currentLine;
            }

            // find block of text lines to join
            var anchorLine = currentLine;
            while (count >= 0 && document.firstColumn(currentLine) != -1) {
                --count;
                ++currentLine;
            }

            if (currentLine != anchorLine) {
                document.joinLines(anchorLine, currentLine - 1);
                currentLine -= currentLine - anchorLine - 1;
            }
        }
        document.editEnd();
    } else {
        // unwrap paragraph under the cursor
        var cursorPosition = view.cursorPosition();
        if (document.firstColumn(cursorPosition.line) != -1) {
            var startLine = cursorPosition.line;
            while (startLine > 0) {
                if (document.firstColumn(startLine - 1) == -1) {
                    break;
                }
                --startLine;
            }

            var endLine = cursorPosition.line;
            var lineCount = document.lines();
            while (endLine < lineCount) {
                if (document.firstColumn(endLine + 1) == -1) {
                    break;
                }
                ++endLine;
            }

            if (startLine != endLine) {
                document.editBegin();
                document.joinLines(startLine, endLine);
                document.editEnd();
            }
        }
    }
}

function moveLinesDown()
{
    var fromLine = -1;
    var toLine = -1;

    var selectionRange = view.selection();
    if (selectionRange.isValid() && selectionRange.end.line < document.lines() - 1) {
        toLine = selectionRange.start.line;
        fromLine = selectionRange.end.line + 1;
    } else if (view.cursorPosition().line < document.lines() - 1) {
        toLine = view.cursorPosition().line;
        fromLine = toLine + 1;
    }

    if (fromLine != -1 && toLine != -1) {
        var text = document.line(fromLine);

        document.editBegin();
        document.removeLine(fromLine);
        document.insertLine(toLine, text);
        document.editEnd();
    }
}

function moveLinesUp()
{
    var fromLine = -1;
    var toLine = -1;

    var selectionRange = view.selection();
    if (selectionRange.isValid() && selectionRange.start.line > 0) {
        fromLine = selectionRange.start.line - 1;
        toLine = selectionRange.end.line;
    } else if (view.cursorPosition().line > 0) {
        toLine = view.cursorPosition().line;
        fromLine = toLine - 1;
    }

    if (fromLine != -1 && toLine != -1) {
        var text = document.line(fromLine);

        document.editBegin();
        document.removeLine(fromLine);
        document.insertLine(toLine, text);
        document.editEnd();
    }
}

function action(cmd)
{
    var a = new Object();
    if (cmd == "sort") {
        a.text = i18n("Sort Selected Text");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    } else if (cmd == "moveLinesDown") {
        a.text = i18n("Move Lines Down");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    } else if (cmd == "moveLinesUp") {
        a.text = i18n("Move Lines Up");
        a.icon = "";
        a.category = "";
        a.interactive = false;
        a.shortcut = "";
    }

    return a;
}

function help(cmd)
{
    if (cmd == "sort") {
        return i18n("Sort the selected text or whole document.");
    } else if (cmd == "moveLinesDown") {
        return i18n("Move selected lines down.");
    } else if (cmd == "moveLinesUp") {
        return i18n("Move selected lines up.");
    } else if (cmd == "uniq") {
        return i18n("Remove duplicate lines from the selected text or whole document.");
    } else if (cmd == "natsort") {
        return i18n("Sort the selected text or whole document in natural order.
"
              +"Here's an example to show the difference to the normal sort method:
"
              +"sort(a10, a1, a2) => a1, a10, a2
"
              +"natsort(a10, a1, a2) => a1, a2, a10");
    } else if (cmd == "rtrim") {
        return i18n("Trims trailing whitespace from selection or whole document.");
    } else if (cmd == "ltrim") {
        return i18n("Trims leading whitespace from selection or whole document.");
    } else if (cmd == "trim") {
        return i18n("Trims leading and trailing whitespace from selection or whole document.");
    } else if (cmd == "join") {
        return i18n("Joins selected lines or whole document. Optionally pass a separator to put between each line:
"
                    "join ', ' will e.g. join lines and separate them by a comma.");
    } else if (cmd == "rmblank") {
        return i18n("Removes empty lines from selection or whole document.");
    } else if (cmd == "unwrap") {
        return "Unwraps all paragraphs in the text selection, or the paragraph under the text cursor if there is no selected text.";
    } else if (cmd == "each") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines and replace them with the" +
               "return value of that callback.
" +
               "Example (join selected lines):
" +
                "each 'function(lines){return lines.join(\", \"}'");
    } else if (cmd == "filter") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines " +
                    "and remove those where the callback returns false.
" +
                    "Example (see also rmblank):
" +
                    "filter 'function(l){return l.length > 0;}'");
    } else if (cmd == "map") {
        return i18n("Given a JavaScript function as argument, call that for the list of (selected) lines " +
                    "and replace the line with the return value of the callback.
" +
                    "Example (see also ltrim):
" +
                    "map 'function(l){return l.replace(/^\s+/, '');}'");
    }
}

/// helper code below:

function each(func)
{
    if ( typeof(func) != "function" ) {
        func = eval("(" + func + ")");
        if ( typeof(func) != "function" ) {
          debug("parameter is not a function: " + typeof(func));
          return;
        } else {
          debug("using argument as evaluated callback")
        }
    }

    var selection = view.selection();
    if (!selection.isValid()) {
        // use whole range
        selection = document.documentRange();
    } else {
        selection.start.column = 0;
        selection.end.column = document.lineLength(selection.end.line);
    }

    var text = document.text(selection);

    var lines = text.split("\n");
    lines = func(lines);
    if ( typeof(lines) == "object" ) {
      text = lines.join("\n");
    } else if ( typeof(lines) == "string" ) {
      text = lines
    } else {
      throw "callback function for each has to return object or array of lines";
    }

    view.clearSelection();

    document.editBegin();
    document.removeText(selection);
    document.insertText(selection.start, text);
    document.editEnd();
}

function filter(func)
{
    if ( typeof(func) == "function" ) {
        each(function(lines) { return lines.filter(func); });
    } else {
        each('function(lines){ return lines.filter(' + func + '); }');
    }
}

function map(func)
{
    if ( typeof(func) == "function" ) {
        each(function(lines) { return lines.map(func); });
    } else {
        each('function(lines){ return lines.map(' + func + '); }');
    }
}

/*
natcompare.js -- Perform 'natural order' comparisons of strings in JavaScript.
Copyright (C) 2005 by SCK-CEN (Belgian Nucleair Research Centre)
Written by Kristof Coomans <kristof[dot]coomans[at]sckcen[dot]be>

Based on the Java version by Pierre-Luc Paour, of which this is more or less a straight conversion.
Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>

The Java version was based on the C version by Martin Pool.
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

function isWhitespaceChar(a)
{
    var charCode;
    charCode = a.charCodeAt(0);

    if ( charCode <= 32 )
    {
        return true;
    }
    else
    {
        return false;
    }
}

function isDigitChar(a)
{
    var charCode;
    charCode = a.charCodeAt(0);

    if ( charCode >= 48  && charCode <= 57 )
    {
        return true;
    }
    else
    {
        return false;
    }
}

function compareRight(a,b)
{
    var bias = 0;
    var ia = 0;
    var ib = 0;

    var ca;
    var cb;

    // The longest run of digits wins.  That aside, the greatest
    // value wins, but we can't know that it will until we've scanned
    // both numbers to know that they have the same magnitude, so we
    // remember it in BIAS.
    for (;; ia++, ib++) {
        ca = a.charAt(ia);
        cb = b.charAt(ib);

        if (!isDigitChar(ca)
                && !isDigitChar(cb)) {
            return bias;
        } else if (!isDigitChar(ca)) {
            return -1;
        } else if (!isDigitChar(cb)) {
            return +1;
        } else if (ca < cb) {
            if (bias == 0) {
                bias = -1;
            }
        } else if (ca > cb) {
            if (bias == 0)
                bias = +1;
        } else if (ca == 0 && cb == 0) {
            return bias;
        }
    }
}

function natcompare(a,b) {

    var ia = 0, ib = 0;
        var nza = 0, nzb = 0;
        var ca, cb;
        var result;

    while (true)
    {
        // only count the number of zeroes leading the last number compared
        nza = nzb = 0;

        ca = a.charAt(ia);
        cb = b.charAt(ib);

        // skip over leading spaces or zeros
        while ( isWhitespaceChar( ca ) || ca =='0' ) {
            if (ca == '0') {
                nza++;
            } else {
                // only count consecutive zeroes
                nza = 0;
            }

            ca = a.charAt(++ia);
        }

        while ( isWhitespaceChar( cb ) || cb == '0') {
            if (cb == '0') {
                nzb++;
            } else {
                // only count consecutive zeroes
                nzb = 0;
            }

            cb = b.charAt(++ib);
        }

        // process run of digits
        if (isDigitChar(ca) && isDigitChar(cb)) {
            if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
                return result;
            }
        }

        if (ca == 0 && cb == 0) {
            // The strings compare the same.  Perhaps the caller
            // will want to call strcmp to break the tie.
            return nza - nzb;
        }

        if (ca < cb) {
            return -1;
        } else if (ca > cb) {
            return +1;
        }

        ++ia; ++ib;
    }
}

// kate: space-indent on; indent-width 4; replace-tabs on;
<|endoftext|>


<commit_before>frappe.ui.form.ControlMarkdownEditor = class ControlMarkdownEditor extends frappe.ui.form.ControlCode {
	static editor_class = 'markdown'
	make_ace_editor() {
		super.make_ace_editor();

		this.ace_editor_target.wrap(`<div class="${this.editor_class}-container">`);
		this.markdown_container = this.$input_wrapper.find(`.${this.constructor.editor_class}-container`);

		this.editor.getSession().setUseWrapMode(true);

		this.showing_preview = false;
		this.preview_toggle_btn = $(`<button class="btn btn-default btn-xs ${this.editor_class}-toggle">${__('Preview')}</button>`)
			.click(e => {
				if (!this.showing_preview) {
					this.update_preview();
				}

				const $btn = $(e.target);
				this.markdown_preview.toggle(!this.showing_preview);
				this.ace_editor_target.toggle(this.showing_preview);

				this.showing_preview = !this.showing_preview;

				$btn.text(this.showing_preview ? __('Edit') : __('Preview'));
			});
		this.markdown_container.prepend(this.preview_toggle_btn);

		this.markdown_preview = $(`<div class="${this.editor_class}-preview border rounded">`).hide();
		this.markdown_container.append(this.markdown_preview);
	}

	set_language() {
		this.df.options = 'Markdown';
		super.set_language();
	}

	update_preview() {
		const value = this.get_value() || "";
		this.markdown_preview.html(frappe.markdown(value));
	}

	set_formatted_input(value) {
		super.set_formatted_input(value)
			.then(() => {
				this.update_preview();
			});
	}

	set_disp_area(value) {
		this.disp_area && $(this.disp_area).text(value);
	}
};
<commit_msg>fix(markdown-editor): preview btn<commit_after>frappe.ui.form.ControlMarkdownEditor = class ControlMarkdownEditor extends frappe.ui.form.ControlCode {
	static editor_class = 'markdown'
	make_ace_editor() {
		super.make_ace_editor();
		if (this.markdown_container) return;

		let editor_class = this.constructor.editor_class;
		this.ace_editor_target.wrap(`<div class="${editor_class}-container">`);
		this.markdown_container = this.$input_wrapper.find(`.${editor_class}-container`);

		this.editor.getSession().setUseWrapMode(true);

		this.showing_preview = false;
		this.preview_toggle_btn = $(`<button class="btn btn-default btn-xs ${editor_class}-toggle">${__('Preview')}</button>`)
			.click(e => {
				if (!this.showing_preview) {
					this.update_preview();
				}

				const $btn = $(e.target);
				this.markdown_preview.toggle(!this.showing_preview);
				this.ace_editor_target.toggle(this.showing_preview);

				this.showing_preview = !this.showing_preview;

				$btn.text(this.showing_preview ? __('Edit') : __('Preview'));
			});
		this.markdown_container.prepend(this.preview_toggle_btn);

		this.markdown_preview = $(`<div class="${editor_class}-preview border rounded">`).hide();
		this.markdown_container.append(this.markdown_preview);
	}

	set_language() {
		this.df.options = 'Markdown';
		super.set_language();
	}

	update_preview() {
		const value = this.get_value() || "";
		this.markdown_preview.html(frappe.markdown(value));
	}

	set_formatted_input(value) {
		super.set_formatted_input(value)
			.then(() => {
				this.update_preview();
			});
	}

	set_disp_area(value) {
		this.disp_area && $(this.disp_area).text(value);
	}
};
<|endoftext|>


<commit_before>/*jshint browser:true */
/*global jQuery */
(function($) {
	"use strict";

	var XmlRpcFault = function() {
		Error.apply(this, arguments);
	};
	XmlRpcFault.prototype = new Error();
	XmlRpcFault.prototype.type = 'XML-RPC fault';

	var xmlrpc = $.xmlrpc = function(url, settings) {

		if (arguments.length === 2) {
			settings.url = url;
		} else {
			settings = url;
			url = settings.url;
		}

		settings.dataType = 'xml json';
		settings.type = 'POST';
		settings.contentType = 'text/xml';
		settings.converters = {'xml json': xmlrpc.parseDocument};

		var xmlDoc = xmlrpc.document(settings.methodName, settings.params || []);

		if ("XMLSerializer" in window) {
			settings.data = new window.XMLSerializer().serializeToString(xmlDoc);
		} else {
			// IE does not have XMLSerializer
			settings.data = xmlDoc.xml;
		}

		return $.ajax(settings);
	};

	/**
	* Make an XML document node.
	*/
	xmlrpc.createXMLDocument = function () {

		if (document.implementation && "createDocument" in document.implementation) {
			// Most browsers support createDocument
			return document.implementation.createDocument(null, null, null);

		} else {
			// IE uses ActiveXObject instead of the above.
			var i, length, activeX = [
				"MSXML6.DomDocument", "MSXML3.DomDocument",
				"MSXML2.DomDocument", "MSXML.DomDocument", "Microsoft.XmlDom"
			];
			for (i = 0, length = activeX.length; i < length; i++) {
				try {
					return new ActiveXObject(activeX[i]);
				} catch(_) {}
			}
		}
	};

	/**
	* Make an XML-RPC document from a method name and a set of parameters
	*/
	xmlrpc.document = function(name, params) {
		var doc = xmlrpc.createXMLDocument();

		var $xml = function(name) {
			return $(doc.createElement(name));
		};

		var $methodName = $xml('methodName').text(name);
		var $params = $xml('params').append($.map(params, function(param) {
			var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
			return $xml('param').append($value);
		}));
		var $methodCall = $xml('methodCall').append($methodName, $params);
		doc.appendChild($methodCall.get(0));
		return doc;
	};

	var _isInt = function(x) {
		return (x === parseInt(x, 10)) && !isNaN(x);
	};

	/**
	* Take a JavaScript value, and return an XML node representing the value
	* in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
	* used. Otherwise, a best guess is made as to its type. The best guess is
	* good enough in the vast majority of cases.
	*/
	xmlrpc.toXmlRpc = function(item, $xml) {

		if (item instanceof XmlRpcType) {
			return item.toXmlRpc($xml);
		}

		var types = $.xmlrpc.types;
		var type = $.type(item);

		switch (type) {
			case "undefined":
			case "null":
				return types.nil.encode(item, $xml);

			case "date":
				return types['datetime.iso8601'].encode(item, $xml);

			case "object":
				if (item instanceof ArrayBuffer) {
					return types.base64.encode(item, $xml);
				} else {
					return types.struct.encode(item, $xml);
				}
				break;

			case "number":
				// Ints and Floats encode differently
				if (_isInt(item)) {
					return types['int'].encode(item, $xml);
				} else {
					return types['double'].encode(item, $xml);
				}
				break;

			case "array":
			case "boolean":
			case "string":
				return types[type].encode(item, $xml);

			default:
				throw new Error("Unknown type", item);
		}
	};

	/**
	* Take an XML-RPC document and decode it to an equivalent JavaScript
	* representation.
	*
	* If the XML-RPC document represents a fault, then an equivalent
	* XmlRpcFault will be thrown instead
	*/
	xmlrpc.parseDocument = function(doc) {
		var $doc = $(doc);
		var $response = $doc.children('methodresponse');

		var $fault = $response.find('> fault');
		if ($fault.length === 0) {
			var $params = $response.find('> params > param > value > *');
			var json = $params.toArray().map(xmlrpc.parseNode);
			return json;
		} else {
			var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
			var err = new XmlRpcFault(fault.faultString);
			err.msg = err.message = fault.faultString;
			err.type = err.code = fault.faultCode;
			throw err;
		}
	};

	/*
	* Take an XML-RPC node, and return the JavaScript equivalent
	*/
	xmlrpc.parseNode = function(node) {

		// Some XML-RPC services return empty <value /> elements. This is not
		// legal XML-RPC, but we may as well handle it.
		if (node === undefined) {
			return null;
		}
		var nodename = node.nodeName.toLowerCase();
		if (nodename in xmlrpc.types) {
			return xmlrpc.types[nodename].decode(node);
		} else {
			throw new Error('Unknown type ' + nodename);
		}
	};

	/*
	* Take a <value> node, and return the JavaScript equivalent.
	*/
	xmlrpc.parseValue = function(value) {
		var child = $(value).children()[0];
		if (child) {
			// Child nodes should be decoded.
			return xmlrpc.parseNode(child);
		} else {
			// If no child nodes, the value is a plain text node.
			return $(value).text();
		}
	};

	var XmlRpcType = function() { };

	$.xmlrpc.types = {};

	/**
	* Make a XML-RPC type. We use these to encode and decode values. You can
	* also force a values type using this. See `$.xmlrpc.force()`
	*/
	xmlrpc.makeType = function(tagName, simple, encode, decode) {
		var Type;

		Type = function(value) {
			this.value = value;
		};
		Type.prototype = new XmlRpcType();
		Type.prototype.tagName = tagName;

		if (simple) {
			var simpleEncode = encode, simpleDecode = decode;
			encode = function(value, $xml) {
				var text = simpleEncode(value);
				return $xml(Type.tagName).text(text);
			};
			decode = function(node) {
				return simpleDecode($(node).text(), node);
			};
		}
		Type.prototype.toXmlRpc = function($xml) {
			return Type.encode(this.value, $xml);
		};

		Type.tagName = tagName;
		Type.encode = encode;
		Type.decode = decode;

		xmlrpc.types[tagName.toLowerCase()] = Type;
	};

	// Number types
	var _fromInt = function(value) { return '' + Math.floor(value); };
	var _toInt = function(text, _) { return parseInt(text, 10); };

	xmlrpc.makeType('int', true, _fromInt, _toInt);
	xmlrpc.makeType('i4', true, _fromInt, _toInt);
	xmlrpc.makeType('i8', true, _fromInt, _toInt);
	xmlrpc.makeType('i16', true, _fromInt, _toInt);
	xmlrpc.makeType('i32', true, _fromInt, _toInt);

	xmlrpc.makeType('double', true, String, function(text) {
		return parseFloat(text, 10);
	});

	// String type. Fairly simple
	xmlrpc.makeType('string', true, String, String);

	// Boolean type. True == '1', False == '0'
	xmlrpc.makeType('boolean', true, function(value) {
		return value ? '1' : '0';
	}, function(text) {
		return text === '1';
	});

	// Dates are a little trickier
	var _pad = function(n) { return n<10 ? '0'+n : n; };

	xmlrpc.makeType('dateTime.iso8601', true, function(d) {
		return [
			d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
			_pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
			_pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
		].join('');
	}, function(text) {
		return new Date(text);
	});

	// Go between a base64 string and an ArrayBuffer
	xmlrpc.binary = (function() {
		var pad = '=';
		var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
		var fromChars = toChars.reduce(function(acc, chr, i) {
			acc[chr] = i;
			return acc;
		}, {});

		/*
		* In the following, three bytes are added together into a 24-bit
		* number, which is then split up in to 4 6-bit numbers - or vice versa.
		* That is why there is lots of shifting by multiples of 6 and 8, and
		* the magic numbers 3 and 4.
		*
		* The modulo 64 is for converting to base 64, and the modulo 256 is for
		* converting to 8-bit numbers.
		*/
		return {
			toBase64: function(ab) {
				var acc = [];

				var int8View = new Uint8Array(ab);
				var int8Index = 0, int24;
				for (; int8Index < int8View.length; int8Index += 3) {

					// Grab three bytes
					int24 =
						(int8View[int8Index + 0] << 16) +
						(int8View[int8Index + 1] << 8) +
						(int8View[int8Index + 2] << 0);

					// Push four chars
					acc.push(toChars[(int24 >> 18) % 64]);
					acc.push(toChars[(int24 >> 12) % 64]);
					acc.push(toChars[(int24 >> 6) % 64]);
					acc.push(toChars[(int24 >> 0)% 64]);
				}

				// Set the last few characters to the padding character
				var padChars = 3 - ((ab.byteLength % 3) || 3);
				while (padChars--) {
					acc[acc.length - padChars - 1] = pad;
				}

				return acc.join('');
			},

			fromBase64: function(base64) {
				var base64Len = base64.length;

				// Work out the length of the data, accommodating for padding
				var abLen = (base64Len / 4) * 3;
				if (base64.charAt(base64Len - 1) === pad) { abLen--; }
				if (base64.charAt(base64Len - 2) === pad) { abLen--; }

				// Make the ArrayBuffer, and an Int8Array to work with it
				var ab = new ArrayBuffer(abLen);
				var int8View = new Uint8Array(ab);

				var base64Index = 0, int8Index = 0, int24;
				for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {

					// Grab four chars
					int24 =
						(fromChars[base64[base64Index + 0]] << 18) +
						(fromChars[base64[base64Index + 1]] << 12) +
						(fromChars[base64[base64Index + 2]] << 6) +
						(fromChars[base64[base64Index + 3]] << 0);

					// Push three bytes
					int8View[int8Index + 0] = (int24 >> 16) % 256;
					int8View[int8Index + 1] = (int24 >> 8) % 256;
					int8View[int8Index + 2] = (int24 >> 0) % 256;

				}

				return ab;
			}
		};
	})();

	xmlrpc.makeType('base64', true, function(ab) {
		return xmlrpc.binary.toBase64(ab);
	}, function(text) {
		return xmlrpc.binary.fromBase64(text);
	});

	// Nil/null
	xmlrpc.makeType('nil', false,
		function(val, $xml) { return $xml('nil'); },
		function(_) { return null; }
	);

	// Structs/Objects
	xmlrpc.makeType('struct', false, function(value, $xml) {
		var $struct = $xml('struct');

		$.each(value, function(name, value) {
			var $name = $xml('name').text(name);
			var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
			$struct.append($xml('member').append($name, $value));
		});

		return $struct;

	}, function(node) {
		return $(node)
			.find('> member')
			.toArray()
			.reduce(function(struct, el) {
				var $el = $(el);
				var key = $el.find('> name').text();
				var value = xmlrpc.parseValue($el.find('> value'));

				struct[key] = value;
				return struct;
			}, {});

	});

	// Arrays
	xmlrpc.makeType('array', false, function(value, $xml) {
		var $array = $xml('array');
		var $data = $xml('data');
		$.each(value, function(i, val) {
			$data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
		});
		$array.append($data);
		return $array;
	}, function(node) {
		return $(node).find('> data > value').toArray()
			.map(xmlrpc.parseValue);
	});

	/**
	* Force a value to an XML-RPC type. All the usual XML-RPC types are
	* supported
	*/
	xmlrpc.force = function(type, value) {
		return new xmlrpc.types[type](value);
	};

})(jQuery);
<commit_msg>Fix the date decoder from jquery.xmlrpc<commit_after>/*jshint browser:true */
/*global jQuery */
(function($) {
	"use strict";

	var XmlRpcFault = function() {
		Error.apply(this, arguments);
	};
	XmlRpcFault.prototype = new Error();
	XmlRpcFault.prototype.type = 'XML-RPC fault';

	var xmlrpc = $.xmlrpc = function(url, settings) {

		if (arguments.length === 2) {
			settings.url = url;
		} else {
			settings = url;
			url = settings.url;
		}

		settings.dataType = 'xml json';
		settings.type = 'POST';
		settings.contentType = 'text/xml';
		settings.converters = {'xml json': xmlrpc.parseDocument};

		var xmlDoc = xmlrpc.document(settings.methodName, settings.params || []);

		if ("XMLSerializer" in window) {
			settings.data = new window.XMLSerializer().serializeToString(xmlDoc);
		} else {
			// IE does not have XMLSerializer
			settings.data = xmlDoc.xml;
		}

		return $.ajax(settings);
	};

	/**
	* Make an XML document node.
	*/
	xmlrpc.createXMLDocument = function () {

		if (document.implementation && "createDocument" in document.implementation) {
			// Most browsers support createDocument
			return document.implementation.createDocument(null, null, null);

		} else {
			// IE uses ActiveXObject instead of the above.
			var i, length, activeX = [
				"MSXML6.DomDocument", "MSXML3.DomDocument",
				"MSXML2.DomDocument", "MSXML.DomDocument", "Microsoft.XmlDom"
			];
			for (i = 0, length = activeX.length; i < length; i++) {
				try {
					return new ActiveXObject(activeX[i]);
				} catch(_) {}
			}
		}
	};

	/**
	* Make an XML-RPC document from a method name and a set of parameters
	*/
	xmlrpc.document = function(name, params) {
		var doc = xmlrpc.createXMLDocument();

		var $xml = function(name) {
			return $(doc.createElement(name));
		};

		var $methodName = $xml('methodName').text(name);
		var $params = $xml('params').append($.map(params, function(param) {
			var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
			return $xml('param').append($value);
		}));
		var $methodCall = $xml('methodCall').append($methodName, $params);
		doc.appendChild($methodCall.get(0));
		return doc;
	};

	var _isInt = function(x) {
		return (x === parseInt(x, 10)) && !isNaN(x);
	};

	/**
	* Take a JavaScript value, and return an XML node representing the value
	* in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
	* used. Otherwise, a best guess is made as to its type. The best guess is
	* good enough in the vast majority of cases.
	*/
	xmlrpc.toXmlRpc = function(item, $xml) {

		if (item instanceof XmlRpcType) {
			return item.toXmlRpc($xml);
		}

		var types = $.xmlrpc.types;
		var type = $.type(item);

		switch (type) {
			case "undefined":
			case "null":
				return types.nil.encode(item, $xml);

			case "date":
				return types['datetime.iso8601'].encode(item, $xml);

			case "object":
				if (item instanceof ArrayBuffer) {
					return types.base64.encode(item, $xml);
				} else {
					return types.struct.encode(item, $xml);
				}
				break;

			case "number":
				// Ints and Floats encode differently
				if (_isInt(item)) {
					return types['int'].encode(item, $xml);
				} else {
					return types['double'].encode(item, $xml);
				}
				break;

			case "array":
			case "boolean":
			case "string":
				return types[type].encode(item, $xml);

			default:
				throw new Error("Unknown type", item);
		}
	};

	/**
	* Take an XML-RPC document and decode it to an equivalent JavaScript
	* representation.
	*
	* If the XML-RPC document represents a fault, then an equivalent
	* XmlRpcFault will be thrown instead
	*/
	xmlrpc.parseDocument = function(doc) {
		var $doc = $(doc);
		var $response = $doc.children('methodresponse');

		var $fault = $response.find('> fault');
		if ($fault.length === 0) {
			var $params = $response.find('> params > param > value > *');
			var json = $params.toArray().map(xmlrpc.parseNode);
			return json;
		} else {
			var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
			var err = new XmlRpcFault(fault.faultString);
			err.msg = err.message = fault.faultString;
			err.type = err.code = fault.faultCode;
			throw err;
		}
	};

	/*
	* Take an XML-RPC node, and return the JavaScript equivalent
	*/
	xmlrpc.parseNode = function(node) {

		// Some XML-RPC services return empty <value /> elements. This is not
		// legal XML-RPC, but we may as well handle it.
		if (node === undefined) {
			return null;
		}
		var nodename = node.nodeName.toLowerCase();
		if (nodename in xmlrpc.types) {
			return xmlrpc.types[nodename].decode(node);
		} else {
			throw new Error('Unknown type ' + nodename);
		}
	};

	/*
	* Take a <value> node, and return the JavaScript equivalent.
	*/
	xmlrpc.parseValue = function(value) {
		var child = $(value).children()[0];
		if (child) {
			// Child nodes should be decoded.
			return xmlrpc.parseNode(child);
		} else {
			// If no child nodes, the value is a plain text node.
			return $(value).text();
		}
	};

	var XmlRpcType = function() { };

	$.xmlrpc.types = {};

	/**
	* Make a XML-RPC type. We use these to encode and decode values. You can
	* also force a values type using this. See `$.xmlrpc.force()`
	*/
	xmlrpc.makeType = function(tagName, simple, encode, decode) {
		var Type;

		Type = function(value) {
			this.value = value;
		};
		Type.prototype = new XmlRpcType();
		Type.prototype.tagName = tagName;

		if (simple) {
			var simpleEncode = encode, simpleDecode = decode;
			encode = function(value, $xml) {
				var text = simpleEncode(value);
				return $xml(Type.tagName).text(text);
			};
			decode = function(node) {
				return simpleDecode($(node).text(), node);
			};
		}
		Type.prototype.toXmlRpc = function($xml) {
			return Type.encode(this.value, $xml);
		};

		Type.tagName = tagName;
		Type.encode = encode;
		Type.decode = decode;

		xmlrpc.types[tagName.toLowerCase()] = Type;
	};

	// Number types
	var _fromInt = function(value) { return '' + Math.floor(value); };
	var _toInt = function(text, _) { return parseInt(text, 10); };

	xmlrpc.makeType('int', true, _fromInt, _toInt);
	xmlrpc.makeType('i4', true, _fromInt, _toInt);
	xmlrpc.makeType('i8', true, _fromInt, _toInt);
	xmlrpc.makeType('i16', true, _fromInt, _toInt);
	xmlrpc.makeType('i32', true, _fromInt, _toInt);

	xmlrpc.makeType('double', true, String, function(text) {
		return parseFloat(text, 10);
	});

	// String type. Fairly simple
	xmlrpc.makeType('string', true, String, String);

	// Boolean type. True == '1', False == '0'
	xmlrpc.makeType('boolean', true, function(value) {
		return value ? '1' : '0';
	}, function(text) {
		return text === '1';
	});

	// Dates are a little trickier
	var _pad = function(n) { return n<10 ? '0'+n : n; };

	xmlrpc.makeType('dateTime.iso8601', true, function(d) {
		return [
			d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
			_pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
			_pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
		].join('');
	}, function(text) {
		var dateArray = [text.slice(0, 4), text.slice(4, 6),
			     text.slice(6, 8)];
		var time = text.slice(8);
		var dateString = dateArray.join('-') + time;
		return new Date(dateString);
	});

	// Go between a base64 string and an ArrayBuffer
	xmlrpc.binary = (function() {
		var pad = '=';
		var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
		var fromChars = toChars.reduce(function(acc, chr, i) {
			acc[chr] = i;
			return acc;
		}, {});

		/*
		* In the following, three bytes are added together into a 24-bit
		* number, which is then split up in to 4 6-bit numbers - or vice versa.
		* That is why there is lots of shifting by multiples of 6 and 8, and
		* the magic numbers 3 and 4.
		*
		* The modulo 64 is for converting to base 64, and the modulo 256 is for
		* converting to 8-bit numbers.
		*/
		return {
			toBase64: function(ab) {
				var acc = [];

				var int8View = new Uint8Array(ab);
				var int8Index = 0, int24;
				for (; int8Index < int8View.length; int8Index += 3) {

					// Grab three bytes
					int24 =
						(int8View[int8Index + 0] << 16) +
						(int8View[int8Index + 1] << 8) +
						(int8View[int8Index + 2] << 0);

					// Push four chars
					acc.push(toChars[(int24 >> 18) % 64]);
					acc.push(toChars[(int24 >> 12) % 64]);
					acc.push(toChars[(int24 >> 6) % 64]);
					acc.push(toChars[(int24 >> 0)% 64]);
				}

				// Set the last few characters to the padding character
				var padChars = 3 - ((ab.byteLength % 3) || 3);
				while (padChars--) {
					acc[acc.length - padChars - 1] = pad;
				}

				return acc.join('');
			},

			fromBase64: function(base64) {
				var base64Len = base64.length;

				// Work out the length of the data, accommodating for padding
				var abLen = (base64Len / 4) * 3;
				if (base64.charAt(base64Len - 1) === pad) { abLen--; }
				if (base64.charAt(base64Len - 2) === pad) { abLen--; }

				// Make the ArrayBuffer, and an Int8Array to work with it
				var ab = new ArrayBuffer(abLen);
				var int8View = new Uint8Array(ab);

				var base64Index = 0, int8Index = 0, int24;
				for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {

					// Grab four chars
					int24 =
						(fromChars[base64[base64Index + 0]] << 18) +
						(fromChars[base64[base64Index + 1]] << 12) +
						(fromChars[base64[base64Index + 2]] << 6) +
						(fromChars[base64[base64Index + 3]] << 0);

					// Push three bytes
					int8View[int8Index + 0] = (int24 >> 16) % 256;
					int8View[int8Index + 1] = (int24 >> 8) % 256;
					int8View[int8Index + 2] = (int24 >> 0) % 256;

				}

				return ab;
			}
		};
	})();

	xmlrpc.makeType('base64', true, function(ab) {
		return xmlrpc.binary.toBase64(ab);
	}, function(text) {
		return xmlrpc.binary.fromBase64(text);
	});

	// Nil/null
	xmlrpc.makeType('nil', false,
		function(val, $xml) { return $xml('nil'); },
		function(_) { return null; }
	);

	// Structs/Objects
	xmlrpc.makeType('struct', false, function(value, $xml) {
		var $struct = $xml('struct');

		$.each(value, function(name, value) {
			var $name = $xml('name').text(name);
			var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
			$struct.append($xml('member').append($name, $value));
		});

		return $struct;

	}, function(node) {
		return $(node)
			.find('> member')
			.toArray()
			.reduce(function(struct, el) {
				var $el = $(el);
				var key = $el.find('> name').text();
				var value = xmlrpc.parseValue($el.find('> value'));

				struct[key] = value;
				return struct;
			}, {});

	});

	// Arrays
	xmlrpc.makeType('array', false, function(value, $xml) {
		var $array = $xml('array');
		var $data = $xml('data');
		$.each(value, function(i, val) {
			$data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
		});
		$array.append($data);
		return $array;
	}, function(node) {
		return $(node).find('> data > value').toArray()
			.map(xmlrpc.parseValue);
	});

	/**
	* Force a value to an XML-RPC type. All the usual XML-RPC types are
	* supported
	*/
	xmlrpc.force = function(type, value) {
		return new xmlrpc.types[type](value);
	};

})(jQuery);
<|endoftext|>


<commit_before>(function(){

if(!window.Prism) {
	return;
}

function $$(expr, con) {
	return Array.prototype.slice.call((con || document).querySelectorAll(expr));
}

function hasClass(element, className) {
  className = " " + className + " ";
  return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
}

var CRLF = crlf = /\r?\n|\r/g;

function highlightLines(pre, lines, classes) {
	var ranges = lines.replace(/\s+/g, '').split(','),
	    offset = +pre.getAttribute('data-line-offset') || 0;

	var lineHeight = parseFloat(getComputedStyle(pre).lineHeight);

	for (var i=0, range; range = ranges[i++];) {
		range = range.split('-');

		var start = +range[0],
		    end = +range[1] || start;

		var line = document.createElement('div');

		line.textContent = Array(end - start + 2).join(' \n');
		line.className = (classes || '') + ' line-highlight';

    //if the line-numbers plugin is enabled, then there is no reason for this plugin to display the line numbers
    if(!hasClass(pre, 'line-numbers')) {
      line.setAttribute('data-start', start);

      if(end > start) {
        line.setAttribute('data-end', end);
      }
    }

		line.style.top = (start - offset - 1) * lineHeight + 'px';

    //allow this to play nicely with the line-numbers plugin
    if(hasClass(pre, 'line-numbers')) {
      //need to attack to pre as when line-numbers is enabled, the code tag is relatively which screws up the positioning
      pre.appendChild(line);
    } else {
      (pre.querySelector('code') || pre).appendChild(line);
    }
	}
}

function applyHash() {
	var hash = location.hash.slice(1);

	// Remove pre-existing temporary lines
	$$('.temporary.line-highlight').forEach(function (line) {
		line.parentNode.removeChild(line);
	});

	var range = (hash.match(/\.([\d,-]+)$/) || [,''])[1];

	if (!range || document.getElementById(hash)) {
		return;
	}

	var id = hash.slice(0, hash.lastIndexOf('.')),
	    pre = document.getElementById(id);

	if (!pre) {
		return;
	}

	if (!pre.hasAttribute('data-line')) {
		pre.setAttribute('data-line', '');
	}

	highlightLines(pre, range, 'temporary ');

	document.querySelector('.temporary.line-highlight').scrollIntoView();
}

var fakeTimer = 0; // Hack to limit the number of times applyHash() runs

Prism.hooks.add('after-highlight', function(env) {
	var pre = env.element.parentNode;
	var lines = pre && pre.getAttribute('data-line');

	if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
		return;
	}

	clearTimeout(fakeTimer);

	$$('.line-highlight', pre).forEach(function (line) {
		line.parentNode.removeChild(line);
	});

	highlightLines(pre, lines);

	fakeTimer = setTimeout(applyHash, 1);
});

addEventListener('hashchange', applyHash);

})();
<commit_msg>run line highlight plugin in complete hook<commit_after>(function(){

if(!window.Prism) {
	return;
}

function $$(expr, con) {
	return Array.prototype.slice.call((con || document).querySelectorAll(expr));
}

function hasClass(element, className) {
  className = " " + className + " ";
  return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
}

var CRLF = crlf = /\r?\n|\r/g;

function highlightLines(pre, lines, classes) {
	var ranges = lines.replace(/\s+/g, '').split(','),
	    offset = +pre.getAttribute('data-line-offset') || 0;

	var lineHeight = parseFloat(getComputedStyle(pre).lineHeight);

	for (var i=0, range; range = ranges[i++];) {
		range = range.split('-');

		var start = +range[0],
		    end = +range[1] || start;

		var line = document.createElement('div');

		line.textContent = Array(end - start + 2).join(' \n');
		line.className = (classes || '') + ' line-highlight';

    //if the line-numbers plugin is enabled, then there is no reason for this plugin to display the line numbers
    if(!hasClass(pre, 'line-numbers')) {
      line.setAttribute('data-start', start);

      if(end > start) {
        line.setAttribute('data-end', end);
      }
    }

		line.style.top = (start - offset - 1) * lineHeight + 'px';

    //allow this to play nicely with the line-numbers plugin
    if(hasClass(pre, 'line-numbers')) {
      //need to attack to pre as when line-numbers is enabled, the code tag is relatively which screws up the positioning
      pre.appendChild(line);
    } else {
      (pre.querySelector('code') || pre).appendChild(line);
    }
	}
}

function applyHash() {
	var hash = location.hash.slice(1);

	// Remove pre-existing temporary lines
	$$('.temporary.line-highlight').forEach(function (line) {
		line.parentNode.removeChild(line);
	});

	var range = (hash.match(/\.([\d,-]+)$/) || [,''])[1];

	if (!range || document.getElementById(hash)) {
		return;
	}

	var id = hash.slice(0, hash.lastIndexOf('.')),
	    pre = document.getElementById(id);

	if (!pre) {
		return;
	}

	if (!pre.hasAttribute('data-line')) {
		pre.setAttribute('data-line', '');
	}

	highlightLines(pre, range, 'temporary ');

	document.querySelector('.temporary.line-highlight').scrollIntoView();
}

var fakeTimer = 0; // Hack to limit the number of times applyHash() runs

Prism.hooks.add('complete', function(env) {
	var pre = env.element.parentNode;
	var lines = pre && pre.getAttribute('data-line');

	if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
		return;
	}

	clearTimeout(fakeTimer);

	$$('.line-highlight', pre).forEach(function (line) {
		line.parentNode.removeChild(line);
	});

	highlightLines(pre, lines);

	fakeTimer = setTimeout(applyHash, 1);
});

addEventListener('hashchange', applyHash);

})();
<|endoftext|>


<commit_before>;(function () {
  'use strict'

  /* imports */
  var funTest = require('fun-test')
  var funAssert = require('fun-assert')
  var specifier = require('specifier')

  var isFunction = funAssert.type('Function')

  var apiSpec = {
    evaluate: [
      isFunction
    ],
    and: [
      isFunction
    ],
    or: [
      isFunction
    ],
    not: [
      isFunction
    ],
    same: [
      isFunction
    ]
  }

  var apiSpecChecker = specifier(apiSpec)

  /* exports */
  module.exports = [
    testConstructor()
  ]

  function testConstructor () {
    function testInput () {
      return true
    }

    var test = funTest({
      input: testInput,
      verifier: function verifier (error, result) {
        funAssert.falsey(error)

        apiSpecChecker(result)
      },
      sync: true
    })

    test.description = 'Should construct object with full api'

    return test
  }
})()

<commit_msg>[PATCH][ADD] tests (100% coverage)<commit_after>;(function () {
  'use strict'

  /* imports */
  var funTest = require('fun-test')
  var funAssert = require('fun-assert')
  var specifier = require('specifier')

  var isFunction = funAssert.type('Function')

  var apiSpec = {
    evaluate: [
      isFunction
    ],
    and: [
      isFunction
    ],
    or: [
      isFunction
    ],
    not: [
      isFunction
    ],
    same: [
      isFunction
    ]
  }

  var apiSpecChecker = specifier(apiSpec)

  /* exports */
  module.exports = [
    testConstructor(),
    testClassMethods()
  ]

  function testConstructor () {
    function testInput () {
      return true
    }

    var test = funTest({
      input: testInput,
      verifier: function verifier (error, result) {
        funAssert.falsey(error)

        apiSpecChecker(result)
      },
      sync: true
    })

    test.description = 'Should construct object with full api'

    return test
  }

  function testClassMethods () {
    var test = funTest({
      verifier: function verifier (error, Predicate) {
        funAssert.falsey(error)

        funAssert.truthy(Predicate.yes())
        funAssert.falsey(Predicate.no())
      },
      transformer: transformer,
      sync: true
    })

    test.description = 'Class methods should work properly'

    return test

    function transformer (funPredicate) {
      return function () {
        return funPredicate
      }
    }
  }

  module.exports = module.exports.concat([
    {
      input: function equal5 (subject) {
        return subject === 5
      },
      willPass: 5,
      willFail: 4
    }
  ].map(tests))

  function tests (options) {
    var test = funTest({
      input: options.input,
      verifier: function (error, predicate) {
        funAssert.falsey(error)

        testPredicate(predicate, options.willPass, options.willFail)
        testSame(predicate, options.willPass, options.willFail)
        testNot(predicate, options.willPass, options.willFail)
        testOr(predicate, options.willPass, options.willFail)
        testAnd(predicate, options.willPass, options.willFail)
      },
      sync: true
    })

    test.descriptiPass = options.input.name + ' should pass for ' +
      options.willFail + ' and fail for ' + options.willFail

    return test
  }

  function testPredicate (predicate, willPass, willFail) {
    console.log()
    console.log('# test predicate')
    assertTrueAndComment(predicate, willPass)
    assertFalseAndComment(predicate, willFail)
  }

  function testSame (predicate, willPass, willFail) {
    console.log()
    console.log('# test predicate.same')
    assertTrueAndComment(predicate.same(), willPass)
    assertFalseAndComment(predicate.same(), willFail)
  }

  function testNot (predicate, willPass, willFail) {
    console.log()
    console.log('# test predicate.not')
    assertTrueAndComment(predicate.not(), willFail)
    assertFalseAndComment(predicate.not(), willPass)
  }

  function testOr (predicate, subject) {
    console.log()
    console.log('# test predicate.or')
    var p1 = predicate.not().or(predicate.not())
    var p2 = predicate.not().or(predicate)
    var p3 = predicate.or(predicate.not())
    var p4 = predicate.or(predicate)

    assertFalseAndComment(p1, subject)
    assertTrueAndComment(p2, subject)
    assertTrueAndComment(p3, subject)
    assertTrueAndComment(p4, subject)
  }

  function testAnd (predicate, subject) {
    console.log()
    console.log('# test predicate.and')
    var p1 = predicate.not().and(predicate.not())
    var p2 = predicate.not().and(predicate)
    var p3 = predicate.and(predicate.not())
    var p4 = predicate.and(predicate)

    assertFalseAndComment(p1, subject)
    assertFalseAndComment(p2, subject)
    assertFalseAndComment(p3, subject)
    assertTrueAndComment(p4, subject)
  }

  function assertTrueAndComment (predicate, subject) {
    console.log('# true:  ' + predicate.toString(subject))
    funAssert.truthy(predicate.evaluate(subject))
  }

  function assertFalseAndComment (predicate, subject) {
    console.log('# false: ' + predicate.toString(subject))
    funAssert.falsey(predicate.evaluate(subject))
  }
})()

<|endoftext|>


<commit_before>/* this file contains all grammatical productions in ECMA-262 edition 5.1 */
// most unicode and whitespace characters have been omitted

var $, _, \u0078, x$, x_, x\u0030, xa, x0, x0a, x0123456789,
  qwertyuiopasdfghjklzxcvbnm, QWERTYUIOPASDFGHJKLZXCVBNM;

null; true; false;

0; 00; 1234567890; 01234567;
0.; 0.00; 10.00; .0; .00
0e0; 0.e0; 0.00e+0; .00e-0;
0E0; 0.E0; 0.00E+0; .00E-0;
0x0; 0x0123456789abcdefABCDEF;
0X0; 0X0123456789abcdefABCDEF;

""; "'"; "\'\"\\\b\f\n\r\t\v\0";
"\x01\x23\x45\x67\x89\xAB\xCD\xEF";
"\u0123\u4567\u89AB\uCDEF";

''; '"'; '\'\"\\\b\f\n\r\t\v\0';
'\x01\x23\x45\x67\x89\xAB\xCD\xEF';
'\u0123\u4567\u89AB\uCDEF';

/x/; /|/; /|||/;
/^$\b\B/; /(?=(?!(?:(.))))/;
/a.\f\n\r\t\v\0\[\-\/\\\x00\u0000/; /\d\D\s\S\w\W/;
/\ca\cb\cc\cd\ce\cf\cg\ch\ci\cj\ck\cl\cm\cn\co\cp\cq\cr\cs\ct\cu\cv\cw\cx\cy\cz/;
/\cA\cB\cC\cD\cE\cF\cG\cH\cI\cJ\cK\cL\cM\cN\cO\cP\cQ\cR\cS\cT\cU\cV\cW\cX\cY\cZ/;
/[a-z-]/; /[^\b\-^]/; /[/\]\\]/;
/./i; /./g; /./m; /./igm;
/.*/; /.*?/; /.+/; /.+?/; /.?/; /.??/;
/.{0}/; /.{0,}/; /.{0,0}/;

this;

x;

[]; [,]; [0]; [0,]; [0,0]; [0,0,]; [0,,0]; [,,];

({}); ({x:0}); ({x:0,y:0}); ({x:0,}); ({x:0,y:0,});
({0:0}); ({0.:0}); ({0.0:0}); ({.0:0}); ({0e0:0}); ({0x0:0});
({get x() {}, set x(y) {}});

0..a; .0.a; 0.0.a; 0e0.a; 0x0.a;
"".a; ''.a; /x/.a; /x/i.a;
this.a; [].a; ({}).a; (function(){}).a;

0[0]; 0.[0]; .0[a]; 0.0[0]; 0e0[0]; 0x0[0];
""[0]; ''[0]; /x/[0]; /x/i[0];
this[0]; [][0]; {}[0]; (function(){})[0];

x = function f(){ return f; }; x[0] = x; x.a = x;
new x(); new new x()(); new new new x()()();
new x[0](); new x.a(); new x[0].a(); new x.a[0]();

new x; new new x; new new x();

new new x().a; new new x()[0];

x++; x--;

delete x; void x; typeof x; ++x; --x;
+x; + +x; -x; - -x; ~x; ~~x; !x; !!x;
+x++; -x.a--; + ++x; - --x; +-+-x;

0*0; 0/0; 0%0;

0+0; 0-0; 0- -0; 0+ +0; 0 - - --x; 0 + + ++x;

0<<0; 0>>0; 0>>>0;

0<0; 0>0; 0<=0; 0>=0;
0 instanceof function(){};
0 in {};

0==0; 0!=0; 0===0; 0!==0;

0&0; 0^0; 0|0; 0&&0; 0||0;

0?0:0; 0?0?0:0:0; 0||0?x=0:x=0;

x=0; x*=0; x/=0; x%=0; x+=0; x-=0;
x<<=0; x>>=0; x>>>=0; x&=0; x^=0; x|=0;

0,0; 0,0,0; x=0,x=0;

{} {;} {0} {0;} {0;0} {0;0;}

var x; var x,y; var x,y,z;
var x=0; var x=0,y; var x,y=0; var x=0,y=0;

;

if(0); if(0);else;

do;while(0);
while(0);
for(;;)break; for(0;0;0); for((0 in[]);0;);
for(var a;;)break; for(var a,b;0;0);
for(var a=0;;)break; for(var a=(0 in[]);0;);
for(x in{}); for(var x in{});
for(var x=[]in{}); for(var x=(0 in[])in{});

for(;0;)continue; x:for(;0;)continue x;

for(;;)break; x:for(;;)break x;
switch(0){case 0:break;}

function f(){ return; }
function f(){ return 0; }

with(0);

switch(0){} switch(0){case 0:} switch(0){case 0:case 0:}
switch(0){default:} switch(0){case 0:default:case 0:}
switch(0){case 0:;} switch(0){case 0:;;}
switch(0){default:;} switch(0){default:;;}

x:; x:y:;

try { throw 0; }catch(x){}

try{}catch(x){}
try{}finally{}
try{}catch(x){}finally{}

debugger;

function f(){}
function f(x){}
function f(x,y){}
function f(){ function f(){} }

(function(){});
(function(x){});
(function(x,y){});
(function(){ function f(){} });
(function f(){});
(function f(x){});
(function f(x,y){});
(function f(){ function f(){} });
<commit_msg>add OctalEscapeSequence coverage<commit_after>/* this file contains all grammatical productions in ECMA-262 edition 5.1 */
// most unicode and whitespace characters have been omitted

var $, _, \u0078, x$, x_, x\u0030, xa, x0, x0a, x0123456789,
  qwertyuiopasdfghjklzxcvbnm, QWERTYUIOPASDFGHJKLZXCVBNM;

null; true; false;

0; 00; 1234567890; 01234567;
0.; 0.00; 10.00; .0; .00
0e0; 0.e0; 0.00e+0; .00e-0;
0E0; 0.E0; 0.00E+0; .00E-0;
0x0; 0x0123456789abcdefABCDEF;
0X0; 0X0123456789abcdefABCDEF;

""; "'"; "\'\"\\\b\f\n\r\t\v\0";
"\1\00\400\000";
"\x01\x23\x45\x67\x89\xAB\xCD\xEF";
"\u0123\u4567\u89AB\uCDEF";

''; '"'; '\'\"\\\b\f\n\r\t\v\0';
'\1\00\400\000';
'\x01\x23\x45\x67\x89\xAB\xCD\xEF';
'\u0123\u4567\u89AB\uCDEF';

/x/; /|/; /|||/;
/^$\b\B/; /(?=(?!(?:(.))))/;
/a.\f\n\r\t\v\0\[\-\/\\\x00\u0000/; /\d\D\s\S\w\W/;
/\ca\cb\cc\cd\ce\cf\cg\ch\ci\cj\ck\cl\cm\cn\co\cp\cq\cr\cs\ct\cu\cv\cw\cx\cy\cz/;
/\cA\cB\cC\cD\cE\cF\cG\cH\cI\cJ\cK\cL\cM\cN\cO\cP\cQ\cR\cS\cT\cU\cV\cW\cX\cY\cZ/;
/[a-z-]/; /[^\b\-^]/; /[/\]\\]/;
/./i; /./g; /./m; /./igm;
/.*/; /.*?/; /.+/; /.+?/; /.?/; /.??/;
/.{0}/; /.{0,}/; /.{0,0}/;

this;

x;

[]; [,]; [0]; [0,]; [0,0]; [0,0,]; [0,,0]; [,,];

({}); ({x:0}); ({x:0,y:0}); ({x:0,}); ({x:0,y:0,});
({0:0}); ({0.:0}); ({0.0:0}); ({.0:0}); ({0e0:0}); ({0x0:0});
({get x() {}, set x(y) {}});

0..a; .0.a; 0.0.a; 0e0.a; 0x0.a;
"".a; ''.a; /x/.a; /x/i.a;
this.a; [].a; ({}).a; (function(){}).a;

0[0]; 0.[0]; .0[a]; 0.0[0]; 0e0[0]; 0x0[0];
""[0]; ''[0]; /x/[0]; /x/i[0];
this[0]; [][0]; {}[0]; (function(){})[0];

x = function f(){ return f; }; x[0] = x; x.a = x;
new x(); new new x()(); new new new x()()();
new x[0](); new x.a(); new x[0].a(); new x.a[0]();

new x; new new x; new new x();

new new x().a; new new x()[0];

x++; x--;

delete x; void x; typeof x; ++x; --x;
+x; + +x; -x; - -x; ~x; ~~x; !x; !!x;
+x++; -x.a--; + ++x; - --x; +-+-x;

0*0; 0/0; 0%0;

0+0; 0-0; 0- -0; 0+ +0; 0 - - --x; 0 + + ++x;

0<<0; 0>>0; 0>>>0;

0<0; 0>0; 0<=0; 0>=0;
0 instanceof function(){};
0 in {};

0==0; 0!=0; 0===0; 0!==0;

0&0; 0^0; 0|0; 0&&0; 0||0;

0?0:0; 0?0?0:0:0; 0||0?x=0:x=0;

x=0; x*=0; x/=0; x%=0; x+=0; x-=0;
x<<=0; x>>=0; x>>>=0; x&=0; x^=0; x|=0;

0,0; 0,0,0; x=0,x=0;

{} {;} {0} {0;} {0;0} {0;0;}

var x; var x,y; var x,y,z;
var x=0; var x=0,y; var x,y=0; var x=0,y=0;

;

if(0); if(0);else;

do;while(0);
while(0);
for(;;)break; for(0;0;0); for((0 in[]);0;);
for(var a;;)break; for(var a,b;0;0);
for(var a=0;;)break; for(var a=(0 in[]);0;);
for(x in{}); for(var x in{});
for(var x=[]in{}); for(var x=(0 in[])in{});

for(;0;)continue; x:for(;0;)continue x;

for(;;)break; x:for(;;)break x;
switch(0){case 0:break;}

function f(){ return; }
function f(){ return 0; }

with(0);

switch(0){} switch(0){case 0:} switch(0){case 0:case 0:}
switch(0){default:} switch(0){case 0:default:case 0:}
switch(0){case 0:;} switch(0){case 0:;;}
switch(0){default:;} switch(0){default:;;}

x:; x:y:;

try { throw 0; }catch(x){}

try{}catch(x){}
try{}finally{}
try{}catch(x){}finally{}

debugger;

function f(){}
function f(x){}
function f(x,y){}
function f(){ function f(){} }

(function(){});
(function(x){});
(function(x,y){});
(function(){ function f(){} });
(function f(){});
(function f(x){});
(function f(x,y){});
(function f(){ function f(){} });
<|endoftext|>


<commit_before>const { chain, some } = require("lodash");

const checkSubjectWith = ({ subject = {} }) => {
  const {
    mustHaveLengthBetween,
    mustMatch,
    mustNotMatch
  } = subject;
  const [ maxSubjectLen, minSubjectLen ] = mustHaveLengthBetween || [];

  return ({ commit: { message }, sha }) => {
    const shortSha = sha.slice(0, 7);
    const commitSubject = message.split("\n")[0];

    if (mustHaveLengthBetween) {
      if (commitSubject.length > maxSubjectLen) { return `Subject for ${shortSha} is too long.`; }
      if (commitSubject.length < minSubjectLen) {
        return `Subject for ${shortSha} is too short.`;
      }
    }
    if (
      mustMatch && !mustMatch.test(commitSubject) ||
      mustNotMatch && mustNotMatch.test(commitSubject)
    ) {
      return `Subject for ${shortSha} does not meet content requirements.`
    }
  };
};

const checkSubjects = (commitConfig, commitEntries) => chain(commitEntries)
  .map(checkSubjectWith(commitConfig))
  .find()
  .value();

const checkMessageWith = ({ subject = {}, message = {} }) => {
  const {
    maxLines,
    minLines,
    enforceEmptySecondLine,
    linesMustHaveLengthBetween
  } = message;

  return ({ commit: { message }, sha }) => {
    const shortSha = sha.slice(0, 7);

    const commitLines = message.split("\n");

    if (maxLines && commitLines.length > maxLines) {
      return `Commit message for ${shortSha} is too long.`
    }
    if (minLines && commitLines.length < minLines) {
      return `Commit message for ${shortSha} is too short.`
    }
    if (enforceEmptySecondLine && commitLines.length > 1 && commitLines[1].trim !== "") {
      return `Commit message for ${shortSha} must have empty second line.`;
    }
    if (linesMustHaveLengthBetween) {
      const [ minLineLen, maxLineLen ] = linesMustHaveLengthBetween;
      const hasExtraLongLine = some(lines.slice(1), line => line.length > maxLineLen);
      const hasTooShortLine = some(lines.slice(2), line => line.length < minLineLen);

      if (hasExtraLongLine) {
        return `Commit message lines much be shorter than ${maxLineLen} for ${shortSha}.`;
      }
      if (hasTooShortLine) {
        return `Commit message lines much be longer than ${minLineLen} for ${shortSha}.`;
      }
    }
  };
};

const checkMessages = (commitConfig, commitEntries) => chain(commitEntries)
  .map(checkMessageWith(commitConfig))
  .find()
  .value();

module.exports = async (pullRequestData, config, gh) => {
  if (!config.commit) { return; }

  const { repoPath, pullRequestNumber, sha } = pullRequestData;
  const commitEntries = await gh.getPullRequestCommits(repoPath, pullRequestNumber);

  const subjectFailureMessage = checkSubjects(config.commit, commitEntries);
  await gh.updateCommit(repoPath, sha, "subject", subjectFailureMessage);

  const messageFailureMessage = checkMessages(config.commit, commitEntries);
  await gh.updateCommit(repoPath, sha, "msg", messageFailureMessage);
};
<commit_msg>Fix subject length checks.<commit_after>const { chain, some } = require("lodash");

const checkSubjectWith = ({ subject = {} }) => {
  const {
    mustHaveLengthBetween,
    mustMatch,
    mustNotMatch
  } = subject;
  const [ minSubjectLen, maxSubjectLen ] = mustHaveLengthBetween || [];

  return ({ commit: { message }, sha }) => {
    const shortSha = sha.slice(0, 7);
    const commitSubject = message.split("\n")[0];

    if (mustHaveLengthBetween) {
      if (commitSubject.length > maxSubjectLen) { return `Subject for ${shortSha} is too long.`; }
      if (commitSubject.length < minSubjectLen) {
        return `Subject for ${shortSha} is too short.`;
      }
    }
    if (
      mustMatch && !mustMatch.test(commitSubject) ||
      mustNotMatch && mustNotMatch.test(commitSubject)
    ) {
      return `Subject for ${shortSha} does not meet content requirements.`
    }
  };
};

const checkSubjects = (commitConfig, commitEntries) => chain(commitEntries)
  .map(checkSubjectWith(commitConfig))
  .find()
  .value();

const checkMessageWith = ({ subject = {}, message = {} }) => {
  const {
    maxLines,
    minLines,
    enforceEmptySecondLine,
    linesMustHaveLengthBetween
  } = message;

  return ({ commit: { message }, sha }) => {
    const shortSha = sha.slice(0, 7);

    const commitLines = message.split("\n");

    if (maxLines && commitLines.length > maxLines) {
      return `Commit message for ${shortSha} is too long.`
    }
    if (minLines && commitLines.length < minLines) {
      return `Commit message for ${shortSha} is too short.`
    }
    if (enforceEmptySecondLine && commitLines.length > 1 && commitLines[1].trim !== "") {
      return `Commit message for ${shortSha} must have empty second line.`;
    }
    if (linesMustHaveLengthBetween) {
      const [ minLineLen, maxLineLen ] = linesMustHaveLengthBetween;
      const hasExtraLongLine = some(lines.slice(1), line => line.length > maxLineLen);
      const hasTooShortLine = some(lines.slice(2), line => line.length < minLineLen);

      if (hasExtraLongLine) {
        return `Commit message lines much be shorter than ${maxLineLen} for ${shortSha}.`;
      }
      if (hasTooShortLine) {
        return `Commit message lines much be longer than ${minLineLen} for ${shortSha}.`;
      }
    }
  };
};

const checkMessages = (commitConfig, commitEntries) => chain(commitEntries)
  .map(checkMessageWith(commitConfig))
  .find()
  .value();

module.exports = async (pullRequestData, config, gh) => {
  if (!config.commit) { return; }

  const { repoPath, pullRequestNumber, sha } = pullRequestData;
  const commitEntries = await gh.getPullRequestCommits(repoPath, pullRequestNumber);

  const subjectFailureMessage = checkSubjects(config.commit, commitEntries);
  await gh.updateCommit(repoPath, sha, "subject", subjectFailureMessage);

  const messageFailureMessage = checkMessages(config.commit, commitEntries);
  await gh.updateCommit(repoPath, sha, "msg", messageFailureMessage);
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import colors from '../constants/colors';
import HashLink from 'react-scrollchor';
import Logo from './Logo';
import { FormattedMessage, defineMessages } from 'react-intl';
import Link from './Link';
import Button from './Button';
import { get, maxBy } from 'lodash';
import withIntl from '../lib/withIntl';
import { formatCurrency } from '../lib/utils';

class GoalsCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    LoggedInUser: PropTypes.object
  }

  constructor(props) {
    super(props);
    const { intl, collective } = props;
    this.renderGoal = this.renderGoal.bind(this);
    this.nodes = {};
    this.state = {
      goals: {},
      styles: {
        balance: { textAlign: 'left', left: 0 },
        yearlyBudget: { textAlign: 'right', right: 0 }
      }
    };

    this.messages = defineMessages({
      'admin': { id: 'menu.admin', defaultMessage: "admin" },
      'backer': { id: 'menu.backer', defaultMessage: "backer" },
      'attendee': { id: 'menu.attendee', defaultMessage: "attendee" },
      'fundraiser': { id: 'menu.fundraiser', defaultMessage: "fundraiser" },
      'parenting': { id: 'menu.parenting', defaultMessage: "member collectives" },
      'about': { id: 'menu.about', defaultMessage: "about" },
      'events': { id: 'menu.events', defaultMessage: "events" },
      'updates': { id: 'menu.updates', defaultMessage: "updates" },
      'budget': { id: 'menu.budget', defaultMessage: "budget" },
      'contributors': { id: 'menu.contributors', defaultMessage: "contributors" },
      'menu.edit.collective': { id: 'menu.edit.collective', defaultMessage: "edit collective" },
      'menu.edit.user': { id: 'menu.edit.user', defaultMessage: "edit profile" },
      'menu.edit.organization': { id: 'menu.edit.organization', defaultMessage: "edit organization" },
      'menu.edit.event': { id: 'menu.edit.event', defaultMessage: "edit event" },
      'bar.balance': { id: 'cover.bar.balance', defaultMessage: "Today's Balance" },
      'bar.yearlyBudget': { id: 'cover.bar.yearlyBudget', defaultMessage: "Estimated Annual Budget" }
    });

    this.goals = [
      {
        slug: 'balance',
        animate: true,
        title: intl.formatMessage(this.messages['bar.balance']),
        amount: get(collective, 'stats.balance'),
        precision: 2,
        position: 'below'
      },
      {
        slug: 'yearlyBudget',
        title: intl.formatMessage(this.messages['bar.yearlyBudget']),
        amount: get(collective, 'stats.yearlyBudget'),
        precision: 0,
        position: 'below'
      },
      ... get(collective, 'settings.goals') || []
    ].sort((a, b) => a.amount > b.amount);

    const lastGoalAtPosition = {};
    for (let i=0; i<this.goals.length; i++) {
      const goal = { ...this.goals[i] };
      goal.slug = goal.slug || `goal${i}`;
      goal.position = goal.position || "above";
      lastGoalAtPosition[goal.position] = goal;
      goal.style = {
        textAlign: lastGoalAtPosition[goal.position] ? 'right' : 'left'
      }
      this.goals[i] = goal;
    }
    this.maxAmount = maxBy(this.goals, g => g.amount).amount;
  }

  componentDidMount() {
    const { collective } = this.props;
    const state = this.state;
    const previous = {};
    const barLength = this.nodes.barContainer.offsetWidth;
    this.goals.forEach((goal, index) => {
      const pos = { level: 0, amount: goal.amount };
      const { slug, position } = goal;
      if (this.nodes[slug]) {
        pos.posX = this.nodes[slug].offsetWidth;
        pos.width = this.nodes[slug].querySelector('.label').offsetWidth;
        pos.height = '20px';
      }
      // if the previous item's position is overlapping, we change the level
      if (previous[position] && pos.posX < previous[position].posX + previous[position].width) {
        pos.level = previous[position].level === 1 ? 0 : 1;

        // Make sure we don't overlap with previous goal on the same level (previous' previous)
        if (previous[position].previous) {
          const overlap = previous[position].previous.posX - pos.posX + pos.width;
          if (overlap > 0) {
            pos.opacity = 0.2;
            pos.posX = pos.posX + overlap;
          }
        }

        if (position === 'above' && pos.level === 1) {
          pos.height = '60px';
          state.styles.barContainer = { marginTop: '10rem' };
        }
      }
      if (goal.animate) {
        const width =  Math.ceil(goal.amount / this.maxAmount * barLength);
        pos.posX = width;
      }
      pos.slug = slug;
      pos.previous = previous[position];
      state.goals[slug] = pos;
      previous[position] = pos;
    });
    this.setState(state);
  }

  renderGoal(goal, index) {
    if (!goal.title) return;
    const { collective, intl } = this.props;
    const title = goal.title || (this.messages[goal.slug] && intl.formatMessage(this.messages[goal.slug]));
    const isLast = index === this.goals.length - 1;
    const posX = goal.animate ? 0 : `${Math.round(goal.amount / this.maxAmount * 100)}%`;
    const slug = goal.slug || `goal${index}`;
    const zIndex = (20 - index) * 10;
    const amount = formatCurrency(goal.animate ? (get(this.state, `goals.${slug}.amount`) || 0) : goal.amount, collective.currency, { precision: goal.precision || 0 });
    const position = goal.position || "above";
    const level = get(this.state, `goals.${slug}.level`) || 0;
    const style = {
      width: get(this.state, `goals.${slug}.posX`) || posX,
      opacity: get(this.state, `goals.${slug}.opacity`) || 1,
      zIndex
    };

    if (position === 'below' && level === 1) {
      style.paddingTop = '4rem';
      style.height = '60px';
    }

    // We animate the goals except last one
    if (position === 'above' && !isLast) {
      style.height = get(this.state, `goals.${slug}.height`) || '0px';
    }

    return (
      <div className={`goal bar ${slug} ${position}`} style={style} ref={node => this.nodes[slug] = node}>

{goal.title}

            {amount}
            { goal.type === 'yearlyBudget' &&

                <FormattedMessage
                  id="tier.interval"
                  defaultMessage="per {interval, select, month {month} year {year} other {}}"
                  values={{ interval: 'year' }}
                  />

            }

    )
  }

  render() {
    const { collective, intl } = this.props;

    if (!collective) {
      return (
);
    }

    return (

          { get(collective, 'stats.backers.all') > 0 &&

              <FormattedMessage id="cover.budget.text" defaultMessage="Thanks to your financial contributions, we are operating on an estimated annual budget of  {yearlyBudget}." values={{ yearlyBudget: formatCurrency(get(collective, 'stats.yearlyBudget'), collective.currency, { precision: 0 })}} />

          }

 this.nodes.barContainer = node}>

              { this.goals.map(this.renderGoal) }

    )
  }

}

export default withIntl(GoalsCover);<commit_msg>fix issue with goal bar with safari<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import colors from '../constants/colors';
import HashLink from 'react-scrollchor';
import Logo from './Logo';
import { FormattedMessage, defineMessages } from 'react-intl';
import Link from './Link';
import Button from './Button';
import { get, maxBy } from 'lodash';
import withIntl from '../lib/withIntl';
import { formatCurrency } from '../lib/utils';

class GoalsCover extends React.Component {

  static propTypes = {
    collective: PropTypes.object.isRequired,
    LoggedInUser: PropTypes.object
  }

  constructor(props) {
    super(props);
    const { intl, collective } = props;
    this.renderGoal = this.renderGoal.bind(this);
    this.nodes = {};
    this.state = {
      goals: {},
      styles: {
        balance: { textAlign: 'left', left: 0 },
        yearlyBudget: { textAlign: 'right', right: 0 }
      }
    };

    this.messages = defineMessages({
      'admin': { id: 'menu.admin', defaultMessage: "admin" },
      'backer': { id: 'menu.backer', defaultMessage: "backer" },
      'attendee': { id: 'menu.attendee', defaultMessage: "attendee" },
      'fundraiser': { id: 'menu.fundraiser', defaultMessage: "fundraiser" },
      'parenting': { id: 'menu.parenting', defaultMessage: "member collectives" },
      'about': { id: 'menu.about', defaultMessage: "about" },
      'events': { id: 'menu.events', defaultMessage: "events" },
      'updates': { id: 'menu.updates', defaultMessage: "updates" },
      'budget': { id: 'menu.budget', defaultMessage: "budget" },
      'contributors': { id: 'menu.contributors', defaultMessage: "contributors" },
      'menu.edit.collective': { id: 'menu.edit.collective', defaultMessage: "edit collective" },
      'menu.edit.user': { id: 'menu.edit.user', defaultMessage: "edit profile" },
      'menu.edit.organization': { id: 'menu.edit.organization', defaultMessage: "edit organization" },
      'menu.edit.event': { id: 'menu.edit.event', defaultMessage: "edit event" },
      'bar.balance': { id: 'cover.bar.balance', defaultMessage: "Today's Balance" },
      'bar.yearlyBudget': { id: 'cover.bar.yearlyBudget', defaultMessage: "Estimated Annual Budget" }
    });

    this.goals = [
      {
        slug: 'balance',
        animate: true,
        title: intl.formatMessage(this.messages['bar.balance']),
        amount: get(collective, 'stats.balance'),
        precision: 2,
        position: 'below'
      },
      {
        slug: 'yearlyBudget',
        title: intl.formatMessage(this.messages['bar.yearlyBudget']),
        amount: get(collective, 'stats.yearlyBudget'),
        precision: 0,
        position: 'below'
      },
      ... get(collective, 'settings.goals') || []
    ].sort((a, b) => a.amount > b.amount);

    const lastGoalAtPosition = {};
    for (let i=0; i<this.goals.length; i++) {
      const goal = { ...this.goals[i] };
      goal.slug = goal.slug || `goal${i}`;
      goal.position = goal.position || "above";
      lastGoalAtPosition[goal.position] = goal;
      goal.style = {
        textAlign: lastGoalAtPosition[goal.position] ? 'right' : 'left'
      }
      this.goals[i] = goal;
    }
    this.maxAmount = maxBy(this.goals, g => g.amount).amount;
  }

  componentDidMount() {
    const { collective } = this.props;
    const state = this.state;
    const previous = {};
    const barLength = this.nodes.barContainer.offsetWidth;
    this.goals.forEach((goal, index) => {
      const pos = { level: 0, amount: goal.amount };
      const { slug, position } = goal;
      if (this.nodes[slug]) {
        pos.posX = this.nodes[slug].offsetWidth;
        pos.width = this.nodes[slug].querySelector('.label').offsetWidth;
        pos.height = '20px';
      }
      // if the previous item's position is overlapping, we change the level
      if (previous[position] && pos.posX < previous[position].posX + previous[position].width) {
        pos.level = previous[position].level === 1 ? 0 : 1;

        // Make sure we don't overlap with previous goal on the same level (previous' previous)
        if (previous[position].previous) {
          const overlap = previous[position].previous.posX - pos.posX + pos.width;
          if (overlap > 0) {
            pos.opacity = 0.2;
            pos.posX = pos.posX + overlap;
          }
        }

        if (position === 'above' && pos.level === 1) {
          pos.height = '60px';
          state.styles.barContainer = { marginTop: '10rem' };
        }
      }
      if (goal.animate) {
        const width =  Math.ceil(goal.amount / this.maxAmount * barLength);
        pos.posX = width;
      }
      pos.slug = slug;
      pos.previous = previous[position];
      state.goals[slug] = pos;
      previous[position] = pos;
    });
    this.setState(state);
  }

  renderGoal(goal, index) {
    if (!goal.title) return;
    const { collective, intl } = this.props;
    const title = goal.title || (this.messages[goal.slug] && intl.formatMessage(this.messages[goal.slug]));
    const isLast = index === this.goals.length - 1;
    const posX = goal.animate ? 0 : `${Math.round(goal.amount / this.maxAmount * 100)}%`;
    const slug = goal.slug || `goal${index}`;
    const zIndex = (20 - index) * 10;
    const amount = formatCurrency(goal.animate ? (get(this.state, `goals.${slug}.amount`) || 0) : goal.amount, collective.currency, { precision: goal.precision || 0 });
    const position = goal.position || "above";
    const level = get(this.state, `goals.${slug}.level`) || 0;
    const style = {
      width: get(this.state, `goals.${slug}.posX`) || posX,
      opacity: get(this.state, `goals.${slug}.opacity`) || 1,
      zIndex
    };

    if (position === 'below' && level === 1) {
      style.paddingTop = '4rem';
      style.height = '60px';
    }

    return (
      <div className={`goal bar ${slug} ${position}`} style={style} ref={node => this.nodes[slug] = node}>

{goal.title}

            {amount}
            { goal.type === 'yearlyBudget' &&

                <FormattedMessage
                  id="tier.interval"
                  defaultMessage="per {interval, select, month {month} year {year} other {}}"
                  values={{ interval: 'year' }}
                  />

            }

    )
  }

  render() {
    const { collective, intl } = this.props;

    if (!collective) {
      return (
);
    }

    return (

          { get(collective, 'stats.backers.all') > 0 &&

              <FormattedMessage id="cover.budget.text" defaultMessage="Thanks to your financial contributions, we are operating on an estimated annual budget of  {yearlyBudget}." values={{ yearlyBudget: formatCurrency(get(collective, 'stats.yearlyBudget'), collective.currency, { precision: 0 })}} />

          }

 this.nodes.barContainer = node}>

              { this.goals.map(this.renderGoal) }

    )
  }

}

export default withIntl(GoalsCover);<|endoftext|>


<commit_before>'use strict';

/**
*	Endpoints for managing data pipes connectors
*	@Author: David Taieb
*/

var fs = require('fs');
var global = require('bluemix-helper-config').global;
var pipesSDK = require('simple-data-pipe-sdk');
var connector = pipesSDK.connector;
var _ = require('lodash');
var path = require("path");
var pipesDb = pipesSDK.pipesDb;

function connectorAPI(){

	var connectors = null;

	/**
	 * loadConnector: private API for loading a connector from a specified path
	 */
	var loadConnector = function( connectorPath ){
		try{
			var connector = require( connectorPath );
			//Determine the path for the connector which is the parent directory of the module main js file
			connector.path = path.join( require.resolve( connectorPath ), "..");
			//Read any custom tab controller provided by the connector
			readCustomControllers( path.join(connector.path, "controllers"), connector );
			return connector;
		}catch(e){
			console.log("Invalid connector found at location %s. Make sure to export an object that inherit from connector object", connectorPath );
			console.log(e.stack);
			return null;
		}
	}

	/**
	 * readCustomController: private API for reading controllers provided by a custom connector
	 */
	var readCustomControllers = function( controllersPath, connector ){
		var customControllers = connector.getOption("customControllers") || {};
		if ( fs.existsSync(controllersPath) ){
			var files = fs.readdirSync( controllersPath);
			files = _.chain( files )
			.filter( function( file ){
				var ret = fs.lstatSync( path.join( controllersPath, file ) ).isFile() && _.endsWith(file, ".js");
				return ret;
			})
			.map(function(file){
				//Extract the tab name from the file name. convention is <<tab>.js
				var tabName = file.substring(0, file.length - ".js".length );
				customControllers[tabName] = fs.readFileSync(path.join( controllersPath, file ), "utf8" );
			})
			.value()

			connector.setOption("customControllers", customControllers);
		}
	}

	//load connectors with source installed within the main app
	var connectorPath = path.join( __dirname, "connectors" );
	fs.readdir( connectorPath, function(err, files){

		if (! err ){
			console.log('Embedded connector repository is present. Searching for connectors.');
			// Load pre-packaged connectors
			connectors = _.chain( files )
				.filter( function(file) {
											return fs.lstatSync(path.join(connectorPath, file)).isDirectory();
						})
				.map( function( file ){
										//Load the connector
										var connector = loadConnector( path.join(connectorPath, file) );
										if ( connector ){
											console.log("Loaded built-in connector", connector.getId());
										}
										return connector;
						})
				.filter( function( connector ){
										//One more pass to remove any invalid connector
										return connector != null;
						})
				.value();
		}

		//Load connectors installed as dependent node modules
		var npm = require("npm");
		npm.load({ parseable: true, depth:0 }, function (err, npm) {
			if (err) {
				return console.log("Unable to load npm programmatically: ", err);
			}
			npm.commands.ls([], true, function( err, data, lite){
				if (err) {
					return console.log("Error running npm ls: ", err);
				}
				//console.log("ls data: ", data)
				_.forEach(data.dependencies, function(value, key ){
					if ( value.hasOwnProperty("pipes-connector-name") ){
						//Found a pipe connector module, load it now
						var connector = loadConnector( value.path );
						if ( connector ){
							console.log("Loaded npm dependency connector", connector.getId());
							connectors.push( connector );
						}
					}
				})
			})
		});
	});

	//Public APIs
	/**
	 * InitEndPoints
	 * @param app: express app
	 */
	this.initEndPoints = function( app ){
		/**
		 * return list of registered connectors
		 */
		app.get('/connectors', function(req, res) {
			if ( !connectors ){
				return global.jsonError( res, "Unable to load registered connectors");
			}
			return res.json( connectors );
		});
	};

	/**
	 * getConnectorForPipeId: return the connector for the pipe identified by its id
	 * @param pipeId: id for the pipe
	 * @param callback(err, connector)
	 */
	this.getConnectorForPipeId = function( pipeId, callback ){
		pipesDb.getPipe( pipeId, function( err, pipe ){
			if ( err ){
				return callback(err);
			}
			var connector = this.getConnector( pipe );
			if ( !connector ){
				return callback( "Unable to get Connector for " + pipeId );
			}
			return callback( null, connector );
		}.bind(this));
	}

	/**
	 * getConnector: return the connector associated with a pipe
	 */
	this.getConnector = function(pipe){
		var connectorId = _.isString( pipe ) ? pipe : pipe.connectorId;
		return _.find( connectors, function( connector ){
			return connector.getId() === connectorId;
		});
	};
}

//Singleton
module.exports = new connectorAPI();<commit_msg> Allow for missing local connector repository (2)<commit_after>'use strict';

/**
*	Endpoints for managing data pipes connectors
*	@Author: David Taieb
*/

var fs = require('fs');
var global = require('bluemix-helper-config').global;
var pipesSDK = require('simple-data-pipe-sdk');
var connector = pipesSDK.connector;
var _ = require('lodash');
var path = require("path");
var pipesDb = pipesSDK.pipesDb;

function connectorAPI(){

	var connectors = [];

	/**
	 * loadConnector: private API for loading a connector from a specified path
	 */
	var loadConnector = function( connectorPath ){
		try{
			var connector = require( connectorPath );
			//Determine the path for the connector which is the parent directory of the module main js file
			connector.path = path.join( require.resolve( connectorPath ), "..");
			//Read any custom tab controller provided by the connector
			readCustomControllers( path.join(connector.path, "controllers"), connector );
			return connector;
		}catch(e){
			console.log("Invalid connector found at location %s. Make sure to export an object that inherit from connector object", connectorPath );
			console.log(e.stack);
			return null;
		}
	}

	/**
	 * readCustomController: private API for reading controllers provided by a custom connector
	 */
	var readCustomControllers = function( controllersPath, connector ){
		var customControllers = connector.getOption("customControllers") || {};
		if ( fs.existsSync(controllersPath) ){
			var files = fs.readdirSync( controllersPath);
			files = _.chain( files )
			.filter( function( file ){
				var ret = fs.lstatSync( path.join( controllersPath, file ) ).isFile() && _.endsWith(file, ".js");
				return ret;
			})
			.map(function(file){
				//Extract the tab name from the file name. convention is <<tab>.js
				var tabName = file.substring(0, file.length - ".js".length );
				customControllers[tabName] = fs.readFileSync(path.join( controllersPath, file ), "utf8" );
			})
			.value()

			connector.setOption("customControllers", customControllers);
		}
	}

	//load connectors with source installed within the main app
	var connectorPath = path.join( __dirname, "connectors" );
	fs.readdir( connectorPath, function(err, files){

		if (! err ){
			console.log('Embedded connector repository is present. Searching for connectors.');
			// Load pre-packaged connectors
			connectors = _.chain( files )
				.filter( function(file) {
											return fs.lstatSync(path.join(connectorPath, file)).isDirectory();
						})
				.map( function( file ){
										//Load the connector
										var connector = loadConnector( path.join(connectorPath, file) );
										if ( connector ){
											console.log("Loaded built-in connector", connector.getId());
										}
										return connector;
						})
				.filter( function( connector ){
										//One more pass to remove any invalid connector
										return connector != null;
						})
				.value();
		}

		//Load connectors installed as dependent node modules
		var npm = require("npm");
		npm.load({ parseable: true, depth:0 }, function (err, npm) {
			if (err) {
				return console.log("Unable to load npm programmatically: ", err);
			}
			npm.commands.ls([], true, function( err, data, lite){
				if (err) {
					return console.log("Error running npm ls: ", err);
				}
				//console.log("ls data: ", data)
				_.forEach(data.dependencies, function(value, key ){
					if ( value.hasOwnProperty("pipes-connector-name") ){
						//Found a pipe connector module, load it now
						var connector = loadConnector( value.path );
						if ( connector ){
							console.log("Loaded npm dependency connector", connector.getId());
							connectors.push( connector );
						}
					}
				})
			})
		});
	});

	//Public APIs
	/**
	 * InitEndPoints
	 * @param app: express app
	 */
	this.initEndPoints = function( app ){
		/**
		 * return list of registered connectors
		 */
		app.get('/connectors', function(req, res) {
			if ( !connectors ){
				return global.jsonError( res, "Unable to load registered connectors");
			}
			return res.json( connectors );
		});
	};

	/**
	 * getConnectorForPipeId: return the connector for the pipe identified by its id
	 * @param pipeId: id for the pipe
	 * @param callback(err, connector)
	 */
	this.getConnectorForPipeId = function( pipeId, callback ){
		pipesDb.getPipe( pipeId, function( err, pipe ){
			if ( err ){
				return callback(err);
			}
			var connector = this.getConnector( pipe );
			if ( !connector ){
				return callback( "Unable to get Connector for " + pipeId );
			}
			return callback( null, connector );
		}.bind(this));
	}

	/**
	 * getConnector: return the connector associated with a pipe
	 */
	this.getConnector = function(pipe){
		var connectorId = _.isString( pipe ) ? pipe : pipe.connectorId;
		return _.find( connectors, function( connector ){
			return connector.getId() === connectorId;
		});
	};
}

//Singleton
module.exports = new connectorAPI();<|endoftext|>