<commit_before>
var   haxe_code = require('../utils/haxe-code')

var REGEX_ENDS_WITH_BEFORE_CALL_CHAR = /[a-zA-Z0-9_\]\)]\s*$/
var REGEX_ENDS_WITH_KEY = /([a-zA-Z0-9_]+)\s*\:$/
var REGEX_ENDS_WITH_ALPHANUMERIC = /([a-zA-Z0-9_]+)$/

module.exports = {

        // Parse a composed haxe type (that can be a whole function signature)
        // and return an object with all the informations walkable recursively
        // A function type will have an `args` value next to the `type` value
        // while a regular type will only have a `type` value.
        // In case the type is itself named inside another function signature, a `name` value
        // Will be added to it. Unknown types are replaced with null.
    parse_composed_type: function(raw_composed_type, ctx) {

        var info = {};
        var len = raw_composed_type.length;

        var current_item = '';
        var items = [];
        var c;

        if (ctx == null) {
            ctx = {i: 0};
        }

            // Iterate over each characters and parse groups recursively
        while (ctx.i < len) {
            c = raw_composed_type.charAt(ctx.i);

            if (c === '(') {
                ctx.i++;
                if (current_item.length > 0 && current_item.charAt(current_item.length - 1) === ':') {
                        // New group, continue parsing in a sub call until the end of the group
                    items.push({
                        name: current_item.slice(0, current_item.length-1),
                        type: this.parse_composed_type(raw_composed_type, ctx)
                    });
                } else {
                    items.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                current_item = '';
            }
            else if (c === ')') {
                ctx.i++;
                break;
            }
            else if (c === '-' && raw_composed_type.charAt(ctx.i + 1) === '>') {
                if (current_item.length > 0) {
                        // Parse the current item as a composed type in case there are
                        // nested groups inside
                    items.push(this.parse_composed_type(current_item));
                }
                current_item = '';
                ctx.i += 2;
            }
            else if (c.trim() === '') {
                ctx.i++;
            }
            else {
                current_item += c;
                ctx.i++;
            }
        }

        if (current_item.length > 0) {
            if (current_item.indexOf('->') != -1) {
                    // Parse the current item as a composed type as there as still
                    // nested groups inside
                items.push(this.parse_composed_type(current_item));
            }
            else {
                items.push(this.parse_type(current_item));
            }
        }

        if (items.length > 1) {
                // If multiple items were parsed, that means it is a function signature
                // Extract arguments and return type
            info.args = [].concat(items);
            info.type = info.args.pop();
            if (info.args.length === 1 && info.args[0].type === 'Void') {
                info.args = [];
            }
        }
        else if (items.length === 1) {
                // If only 1 item was parsed, this is a simple type
            info = items[0];
        }

        return info;
    },

        // Parse haxe type / haxe named argument
        // It will return an object with a `type` value or with both a `type` and `name` values
    parse_type: function(raw_type) {

            // Replace Unknown<*> with null as we probably don't want to use these types directly nor display them
            // And because it is more consistent with signatures that already have null types when none is provided
        var parts = raw_type.split(':');
        if (parts.length === 2) {
            if (parts[1].slice(0, 8) === 'Unknown<') {
                parts[1] = null;
            }
            return {
                type: parts[1],
                name: parts[0]
            };

        } else {
            if (parts[0].slice(0, 8) === 'Unknown<') {
                return {
                    type: null
                };
            }
            return {
                type: parts[0]
            };
        }
    },

        // Get string from parsed haxe type
        // It may be useful to stringify a sub-type (group)
        // of a previously parsed type
    string_from_parsed_type: function(parsed_type) {
        if (parsed_type == null) {
            return '';
        }

        if (typeof(parsed_type) == 'object') {
            if (parsed_type.args != null) {
                var str_args;
                if (parsed_type.args.length > 0) {
                    var arg_items = [];
                    for (var i = 0; i < parsed_type.args.length; i++) {
                        arg_items.push(this.string_from_parsed_type(parsed_type.args[i]));
                    }
                    str_args = arg_items.join('->');
                }
                else {
                    str_args = 'Void';
                }

                if (parsed_type.type != null && parsed_type.type.args != null) {
                    return str_args + '->(' + this.string_from_parsed_type(parsed_type.type) + ')';
                } else {
                    return str_args + '->' + this.string_from_parsed_type(parsed_type.type)
                }
            }
            else {
                return this.string_from_parsed_type(parsed_type.type);
            }
        }

        return String(parsed_type);
    },

        // Try to match a partial function call from the given
        // text and index position and return info if succeeded or null.
        // The provided info are:
        //  `signature_start`   the index of the opening parenthesis starting the function call signature
        //  `number_of_args`    the number of arguments between the signature start and the given index
        //  `key_path`          (optional) an array of keys, in case the index is inside an anonymous structure given as argument
        //  `partial_key`       (optional) a string of the key being written at the given index if inside an anonymous structure given as argument
    parse_partial_call: function(text, index) {
            // Cleanup text
        text = haxe_code.code_with_empty_comments_and_strings(text.slice(0, index));

        var i = index - 1;
        var number_of_args = 0;
        var number_of_parens = 0;
        var number_of_braces = 0;
        var number_of_unclosed_parens = 0;
        var number_of_unclosed_braces = 0;
        var signature_start = -1;
        var c, arg, m;

            // A key path will be detected when giving
            // anonymous structure as argument. The key path will allow to
            // know exactly which key or value we are currently writing.
            // Coupled with typedefs, it can allow to compute suggestions for
            // anonymous structure keys and values
        var can_set_colon_index = true;
        var colon_index = -1;
        var key_path = [];
        var partial_key = null;

        while (i > 0) {
            c = text.charAt(i);

            if (c === '"' || c === '\'') {
                    // Continue until we reach the beginning of the string
                while (i >= 0) {
                    i--;
                    if (text.charAt(i) === c) {
                        i--;
                        break;
                    }
                }
            }
            else if (c === ',') {
                if (number_of_parens === 0 && number_of_braces === 0) {
                    can_set_colon_index = false;
                    if (number_of_args == 0) {
                        arg = text.slice(i + 1, index);
                        if (arg.length > 0) {
                            number_of_args++;
                        }
                    }
                    else {
                        number_of_args++;
                    }
                }
                i--;
            }
            else if (c === ')') {
                number_of_parens++;
                i--;
            }
            else if (c === '}') {
                number_of_braces++;
                i--;
            }
            else if (c === ':') {
                if (can_set_colon_index && number_of_braces === 0) {
                    colon_index = i;
                    can_set_colon_index = false;
                }
                i--;
            }
            else if (c === '{') {
                if (number_of_braces === 0) {
                        // Reset number of arguments because we found that
                        // all the already parsed text is inside an unclosed brace token
                    number_of_args = 0;
                    number_of_unclosed_braces++;
                    can_set_colon_index = true;

                        // Match key
                    if (colon_index != -1) {
                        REGEX_ENDS_WITH_KEY.lastIndex = -1;
                        if (m = text.slice(0, colon_index + 1).match(REGEX_ENDS_WITH_KEY)) {
                            key_path.unshift(m[1]);
                        }
                    }
                    else if (key_path.length === 0) {
                        REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
                        if (m = text.slice(0, index).match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
                            partial_key = m[1];
                        } else {
                            partial_key = '';
                        }
                    }
                }
                else {
                    number_of_braces--;
                }
                i--;
            }
            else if (c === '(') {
                if (number_of_parens > 0) {
                    number_of_parens--;
                    i--;
                }
                else {
                    REGEX_ENDS_WITH_BEFORE_CALL_CHAR.lastIndex = -1;
                    if (REGEX_ENDS_WITH_BEFORE_CALL_CHAR.test(text.slice(0, i))) {
                        number_of_args++;
                        signature_start = i;
                        break;
                    }
                    else {
                            // Reset number of arguments because we found that
                            // all the already parsed text is inside an unclosed paren token
                        number_of_args = 0;
                        number_of_unclosed_parens++;
                        i--;
                    }
                }
            }
            else {
                i--;
            }
        }

        if (signature_start === -1) {
            return null;
        }

        var result = {
            signature_start: signature_start,
            number_of_args: number_of_args
        };

        if (number_of_unclosed_braces > 0) {
            result.key_path = key_path;
            result.partial_key = partial_key;
        }

        return result;
    },

//Based on
// https://github.com/nadako/hxsublime/blob/master/src/SignatureHelper.hx
//Not done, etc.

      //String->String
    getCloseChar:function( c ) {
        var r = '';
        switch (c) {
            case "(": r = ")"; break;
            case "<": r = ">"; break;
            case "{": r = "}"; break;
            default: break;
        }
        return r;
    },

      //String->String
    parseType: function(_intype) {

        var parenRegex = /^\((.*)\)$/;
        var argNameRegex = /^(\??\w+) : /;
        var monomorphRegex = /^Unknown<\d+>$/;

        // replace arrows to ease parsing ">" in type params
        var type = _intype.replace(/ -> /g, "%");

        // prepare a simple toplevel signature without nested arrows
        // nested arrow can be in () or <> and we don't need to modify them,
        // so we store them separately in `groups` map and replace their occurence
        // with a group name in the toplevel string
        var toplevel = [];//StringBuf
        var groups = {}; //Map
        var closeStack = [];//new haxe.ds.GenericStack();
        var depth = 0;
        var groupId = 0;

        for(var i = 0; i < type.length; ++i) {
            var char = type.charAt(i);
            if (char == "(" || char == "<" || char == "{") {
                depth++;
                closeStack.push(this.getCloseChar(char));
                if (depth == 1) {
                    groupId++;
                    groups[groupId] = [];
                    toplevel.push(char);
                    toplevel.push('$'+groupId);
                    continue;
                }
            } else if (char == closeStack[closeStack.length-1]) {
                closeStack.pop();
                depth--;
            }

            if (depth == 0) {
                toplevel.push(char);
            } else {
                groups[groupId].push(char);
            }

        } //for

        // process a sigle type entry, replacing inner content from groups
        // and removing unnecessary parentheses, String->String
        var processType = function(_in_ptype) {

            var ptype = _in_ptype;
            var groupRegex = /\$(\d)+/g;
            var gr = groupRegex.exec(ptype);
            if(gr) {

              var swapgr = true;
              var gridx = 1;
              while(swapgr) {

                var grid = gr[gridx];
                if(grid) {
                  var groupId = parseInt(grid);
                  var groupStr = groups[groupId].join('');
                  var idstr = '\\$'+groupId;
                  var idreplace = new RegExp(idstr, 'g');
                  ptype = ptype.replace(idreplace, groupStr);
                  ptype = ptype.replace(/%/g, "->");
                  gridx++;
                } else {
                  swapgr = false;
                }

              } //while swapping
            } //gr

            var pr = parenRegex.exec(ptype);
            if(pr) {
                ptype = pr[1];
            }

            return ptype;

        } //processType

        // split toplevel signature by the "%" (which is actually "->")
        var parts = toplevel.join('').split("%");

        // get a return or variable type
        var returnType = processType(parts.pop());

        // if there is only the return type, it's a variable
        // otherwise `parts` contains function arguments
        var isFunction = parts.length > 0;

        // format function arguments
        var args = [];
        for(var i = 0; i < parts.length; ++i) {
            var part = parts[i];

            // get argument name and type
            // if function is not a method, argument name is generated by its position
            var argname = '';
            var argtype = '';
            var ar = argNameRegex.exec(part);
            if(ar) {
                argname = ar[1];
                argtype = part.substr(ar[0].length, part.length);
            } else {
                argname = 'arg'+i;
                argtype = part;
            }

            argtype = processType(argtype);

            // we don't need to include the Void argument
            // because it represents absence of arguments
            if (argtype == "Void") {
                continue;
            }

            // if type is unknown, include only the argument name
            if(monomorphRegex.test(argtype)) {
                args.push(argname);
            } else {
                args.push(argname+':'+argtype);
            }

        } //each part

        // finally generate the signature
        var result = [];
        var res = { pre:_intype, sig:'', args:null, ret:null };
        if (isFunction) {
            res.sig += "(";
            res.sig += args.join(", ");
            res.sig += ")";
            res.args = args;
            res.ret = returnType;
        } else {
            res.sig = returnType;
        }

        return res;
    }

} //sig_helper
<commit_msg>parsing; ignore keys inside parenthesis<commit_after>
var   haxe_code = require('../utils/haxe-code')

var REGEX_ENDS_WITH_BEFORE_CALL_CHAR = /[a-zA-Z0-9_\]\)]\s*$/
var REGEX_ENDS_WITH_KEY = /([a-zA-Z0-9_]+)\s*\:$/
var REGEX_ENDS_WITH_ALPHANUMERIC = /([a-zA-Z0-9_]+)$/

module.exports = {

        // Parse a composed haxe type (that can be a whole function signature)
        // and return an object with all the informations walkable recursively
        // A function type will have an `args` value next to the `type` value
        // while a regular type will only have a `type` value.
        // In case the type is itself named inside another function signature, a `name` value
        // Will be added to it. Unknown types are replaced with null.
    parse_composed_type: function(raw_composed_type, ctx) {

        var info = {};
        var len = raw_composed_type.length;

        var current_item = '';
        var items = [];
        var c;

        if (ctx == null) {
            ctx = {i: 0};
        }

            // Iterate over each characters and parse groups recursively
        while (ctx.i < len) {
            c = raw_composed_type.charAt(ctx.i);

            if (c === '(') {
                ctx.i++;
                if (current_item.length > 0 && current_item.charAt(current_item.length - 1) === ':') {
                        // New group, continue parsing in a sub call until the end of the group
                    items.push({
                        name: current_item.slice(0, current_item.length-1),
                        type: this.parse_composed_type(raw_composed_type, ctx)
                    });
                } else {
                    items.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                current_item = '';
            }
            else if (c === ')') {
                ctx.i++;
                break;
            }
            else if (c === '-' && raw_composed_type.charAt(ctx.i + 1) === '>') {
                if (current_item.length > 0) {
                        // Parse the current item as a composed type in case there are
                        // nested groups inside
                    items.push(this.parse_composed_type(current_item));
                }
                current_item = '';
                ctx.i += 2;
            }
            else if (c.trim() === '') {
                ctx.i++;
            }
            else {
                current_item += c;
                ctx.i++;
            }
        }

        if (current_item.length > 0) {
            if (current_item.indexOf('->') != -1) {
                    // Parse the current item as a composed type as there as still
                    // nested groups inside
                items.push(this.parse_composed_type(current_item));
            }
            else {
                items.push(this.parse_type(current_item));
            }
        }

        if (items.length > 1) {
                // If multiple items were parsed, that means it is a function signature
                // Extract arguments and return type
            info.args = [].concat(items);
            info.type = info.args.pop();
            if (info.args.length === 1 && info.args[0].type === 'Void') {
                info.args = [];
            }
        }
        else if (items.length === 1) {
                // If only 1 item was parsed, this is a simple type
            info = items[0];
        }

        return info;
    },

        // Parse haxe type / haxe named argument
        // It will return an object with a `type` value or with both a `type` and `name` values
    parse_type: function(raw_type) {

            // Replace Unknown<*> with null as we probably don't want to use these types directly nor display them
            // And because it is more consistent with signatures that already have null types when none is provided
        var parts = raw_type.split(':');
        if (parts.length === 2) {
            if (parts[1].slice(0, 8) === 'Unknown<') {
                parts[1] = null;
            }
            return {
                type: parts[1],
                name: parts[0]
            };

        } else {
            if (parts[0].slice(0, 8) === 'Unknown<') {
                return {
                    type: null
                };
            }
            return {
                type: parts[0]
            };
        }
    },

        // Get string from parsed haxe type
        // It may be useful to stringify a sub-type (group)
        // of a previously parsed type
    string_from_parsed_type: function(parsed_type) {
        if (parsed_type == null) {
            return '';
        }

        if (typeof(parsed_type) == 'object') {
            if (parsed_type.args != null) {
                var str_args;
                if (parsed_type.args.length > 0) {
                    var arg_items = [];
                    for (var i = 0; i < parsed_type.args.length; i++) {
                        arg_items.push(this.string_from_parsed_type(parsed_type.args[i]));
                    }
                    str_args = arg_items.join('->');
                }
                else {
                    str_args = 'Void';
                }

                if (parsed_type.type != null && parsed_type.type.args != null) {
                    return str_args + '->(' + this.string_from_parsed_type(parsed_type.type) + ')';
                } else {
                    return str_args + '->' + this.string_from_parsed_type(parsed_type.type)
                }
            }
            else {
                return this.string_from_parsed_type(parsed_type.type);
            }
        }

        return String(parsed_type);
    },

        // Try to match a partial function call from the given
        // text and index position and return info if succeeded or null.
        // The provided info are:
        //  `signature_start`   the index of the opening parenthesis starting the function call signature
        //  `number_of_args`    the number of arguments between the signature start and the given index
        //  `key_path`          (optional) an array of keys, in case the index is inside an anonymous structure given as argument
        //  `partial_key`       (optional) a string of the key being written at the given index if inside an anonymous structure given as argument
    parse_partial_call: function(text, index) {
            // Cleanup text
        text = haxe_code.code_with_empty_comments_and_strings(text.slice(0, index));

        var i = index - 1;
        var number_of_args = 0;
        var number_of_parens = 0;
        var number_of_braces = 0;
        var number_of_unclosed_parens = 0;
        var number_of_unclosed_braces = 0;
        var signature_start = -1;
        var c, arg, m;

            // A key path will be detected when giving
            // anonymous structure as argument. The key path will allow to
            // know exactly which key or value we are currently writing.
            // Coupled with typedefs, it can allow to compute suggestions for
            // anonymous structure keys and values
        var can_set_colon_index = true;
        var colon_index = -1;
        var key_path = [];
        var partial_key = null;

        while (i > 0) {
            c = text.charAt(i);

            if (c === '"' || c === '\'') {
                    // Continue until we reach the beginning of the string
                while (i >= 0) {
                    i--;
                    if (text.charAt(i) === c) {
                        i--;
                        break;
                    }
                }
            }
            else if (c === ',') {
                if (number_of_parens === 0 && number_of_braces === 0) {
                    can_set_colon_index = false;
                    if (number_of_args == 0) {
                        arg = text.slice(i + 1, index);
                        if (arg.length > 0) {
                            number_of_args++;
                        }
                    }
                    else {
                        number_of_args++;
                    }
                }
                i--;
            }
            else if (c === ')') {
                number_of_parens++;
                i--;
            }
            else if (c === '}') {
                number_of_braces++;
                i--;
            }
            else if (c === ':') {
                if (can_set_colon_index && number_of_braces === 0 && number_of_parens == 0) {
                    colon_index = i;
                    can_set_colon_index = false;
                }
                i--;
            }
            else if (c === '{') {
                if (number_of_braces === 0) {
                        // Reset number of arguments because we found that
                        // all the already parsed text is inside an unclosed brace token
                    number_of_args = 0;
                    number_of_unclosed_braces++;
                    can_set_colon_index = true;

                        // Match key
                    if (colon_index != -1) {
                        REGEX_ENDS_WITH_KEY.lastIndex = -1;
                        if (m = text.slice(0, colon_index + 1).match(REGEX_ENDS_WITH_KEY)) {
                            key_path.unshift(m[1]);
                        }
                    }
                    else if (key_path.length === 0) {
                        REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
                        if (m = text.slice(0, index).match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
                            partial_key = m[1];
                        } else {
                            partial_key = '';
                        }
                    }
                }
                else {
                    number_of_braces--;
                }
                i--;
            }
            else if (c === '(') {
                if (number_of_parens > 0) {
                    number_of_parens--;
                    i--;
                }
                else {
                    REGEX_ENDS_WITH_BEFORE_CALL_CHAR.lastIndex = -1;
                    if (REGEX_ENDS_WITH_BEFORE_CALL_CHAR.test(text.slice(0, i))) {
                        number_of_args++;
                        signature_start = i;
                        break;
                    }
                    else {
                            // Reset number of arguments because we found that
                            // all the already parsed text is inside an unclosed paren token
                        number_of_args = 0;

                            // Reset key path also if needed
                        can_set_colon_index = true;
                        colon_index = -1;

                        number_of_unclosed_parens++;
                        i--;
                    }
                }
            }
            else {
                i--;
            }
        }

        if (signature_start === -1) {
            return null;
        }

        var result = {
            signature_start: signature_start,
            number_of_args: number_of_args
        };

        if (number_of_unclosed_braces > 0) {
            result.key_path = key_path;
            result.partial_key = partial_key;
        }

        return result;
    },

//Based on
// https://github.com/nadako/hxsublime/blob/master/src/SignatureHelper.hx
//Not done, etc.

      //String->String
    getCloseChar:function( c ) {
        var r = '';
        switch (c) {
            case "(": r = ")"; break;
            case "<": r = ">"; break;
            case "{": r = "}"; break;
            default: break;
        }
        return r;
    },

      //String->String
    parseType: function(_intype) {

        var parenRegex = /^\((.*)\)$/;
        var argNameRegex = /^(\??\w+) : /;
        var monomorphRegex = /^Unknown<\d+>$/;

        // replace arrows to ease parsing ">" in type params
        var type = _intype.replace(/ -> /g, "%");

        // prepare a simple toplevel signature without nested arrows
        // nested arrow can be in () or <> and we don't need to modify them,
        // so we store them separately in `groups` map and replace their occurence
        // with a group name in the toplevel string
        var toplevel = [];//StringBuf
        var groups = {}; //Map
        var closeStack = [];//new haxe.ds.GenericStack();
        var depth = 0;
        var groupId = 0;

        for(var i = 0; i < type.length; ++i) {
            var char = type.charAt(i);
            if (char == "(" || char == "<" || char == "{") {
                depth++;
                closeStack.push(this.getCloseChar(char));
                if (depth == 1) {
                    groupId++;
                    groups[groupId] = [];
                    toplevel.push(char);
                    toplevel.push('$'+groupId);
                    continue;
                }
            } else if (char == closeStack[closeStack.length-1]) {
                closeStack.pop();
                depth--;
            }

            if (depth == 0) {
                toplevel.push(char);
            } else {
                groups[groupId].push(char);
            }

        } //for

        // process a sigle type entry, replacing inner content from groups
        // and removing unnecessary parentheses, String->String
        var processType = function(_in_ptype) {

            var ptype = _in_ptype;
            var groupRegex = /\$(\d)+/g;
            var gr = groupRegex.exec(ptype);
            if(gr) {

              var swapgr = true;
              var gridx = 1;
              while(swapgr) {

                var grid = gr[gridx];
                if(grid) {
                  var groupId = parseInt(grid);
                  var groupStr = groups[groupId].join('');
                  var idstr = '\\$'+groupId;
                  var idreplace = new RegExp(idstr, 'g');
                  ptype = ptype.replace(idreplace, groupStr);
                  ptype = ptype.replace(/%/g, "->");
                  gridx++;
                } else {
                  swapgr = false;
                }

              } //while swapping
            } //gr

            var pr = parenRegex.exec(ptype);
            if(pr) {
                ptype = pr[1];
            }

            return ptype;

        } //processType

        // split toplevel signature by the "%" (which is actually "->")
        var parts = toplevel.join('').split("%");

        // get a return or variable type
        var returnType = processType(parts.pop());

        // if there is only the return type, it's a variable
        // otherwise `parts` contains function arguments
        var isFunction = parts.length > 0;

        // format function arguments
        var args = [];
        for(var i = 0; i < parts.length; ++i) {
            var part = parts[i];

            // get argument name and type
            // if function is not a method, argument name is generated by its position
            var argname = '';
            var argtype = '';
            var ar = argNameRegex.exec(part);
            if(ar) {
                argname = ar[1];
                argtype = part.substr(ar[0].length, part.length);
            } else {
                argname = 'arg'+i;
                argtype = part;
            }

            argtype = processType(argtype);

            // we don't need to include the Void argument
            // because it represents absence of arguments
            if (argtype == "Void") {
                continue;
            }

            // if type is unknown, include only the argument name
            if(monomorphRegex.test(argtype)) {
                args.push(argname);
            } else {
                args.push(argname+':'+argtype);
            }

        } //each part

        // finally generate the signature
        var result = [];
        var res = { pre:_intype, sig:'', args:null, ret:null };
        if (isFunction) {
            res.sig += "(";
            res.sig += args.join(", ");
            res.sig += ")";
            res.args = args;
            res.ret = returnType;
        } else {
            res.sig = returnType;
        }

        return res;
    }

} //sig_helper
<|endoftext|>


<commit_before>var d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests = {

	'jQuery': {
		icon: 'jquery',
		url: 'http://jquery.com',
		test: function(win) {
			var jq = win.jQuery || win.$ || win.$jq || win.$j;
			if(jq && jq.fn && jq.fn.jquery) {
				return { version: jq.fn.jquery };
			}
			return false;
		}
	},

	'jQuery UI': {
		icon: 'jquery_ui',
		url: 'http://jqueryui.com',
		test: function(win) {
			var jq = win.jQuery || win.$ || win.$jq || win.$j;
			if(jq && jq.fn && jq.fn.jquery && jq.ui) {
				var plugins = 'accordion,datepicker,dialog,draggable,droppable,progressbar,resizable,selectable,slider,menu,grid,tabs'.split(','), concat = [];
				for (var i=0; i < plugins.length; i++) { if(jq.ui[plugins[i]]) concat.push(plugins[i].substr(0,1).toUpperCase() + plugins[i].substr(1)); };
				return { version: jq.ui.version, details: concat.length ? 'Plugins used: '+concat.join(',') : '' };
			}
			return false;
		}
	},

	'Dojo': {
		icon: 'dojo',
		url: 'http://dojotoolkit.org',
		test: function(win) {
			if(win.dojo) {
				return { version: win.dojo.version.toString(), details: 'Details: '+(win.dijit ? 'Uses Dijit' : 'none') };
			}
			return false;
		}
	},

	'Prototype': {
		icon: 'prototype',
		url: 'http://prototypejs.org',
		test: function(win) {
			if(win.Prototype && win.Prototype.Version) {
				return { version: win.Prototype.Version };
			}
			return false;
		}
	},

	'Scriptaculous': {
		icon: 'scriptaculous',
		url: 'http://script.aculo.us',
		test: function(win) {
			if(win.Scriptaculous && win.Scriptaculous.Version) {
				return { version: win.Scriptaculous.Version };
			}
			return false;
		}
	},

	'MooTools': {
		icon: 'mootools',
		url: 'http://mootools.net',
		test: function(win) {
			if(win.MooTools && win.MooTools.version) {
				return { version: win.MooTools.version };
			}
			return false;
		}
	},

	'Spry': {
		icon: 'spry',
		url: 'http://labs.adobe.com/technologies/spry',
		test: function(win) {
			if(win.Spry) {
				return { version: '(not detectable)' };
			}
			return false;
		}
	},

	'YUI': {
		icon: 'yui',
		url: 'http://developer.yahoo.com/yui',
		test: function(win) {
			if(win.YAHOO && win.YAHOO.VERSION) {
				return { version: win.YAHOO.VERSION };
			}
			return false;
		}
	},

	'Qooxdoo': {
		icon: 'qooxdoo',
		url: 'http://qooxdoo.org',
		test: function(win) {
			if(win.qx && win.qx.Bootstrap) {
				return { version: '(not detectable)' };
			}
			return false;
		}
	},

	'Ext JS': {
		icon: 'extjs',
		url: 'http://www.sencha.com/products/extjs',
		test: function(win) {
			if(win.Ext && win.Ext.version) {
				return { version: win.Ext.version };
			}
			return false;
		}
	},

	'base2': {
		icon: 'base2',
		url: 'http://code.google.com/p/base2',
		test: function(win) {
			if(win.base2 && win.base2.version) {
				return { version: win.base2.version };
			}
			return false;
		}
	},

	'Closure': {
		icon: 'closure',
		url: 'http://code.google.com/closure',
		test: function(win) {
			if(win.goog) {
				return { version: '2.0' };
			}
			return false;
		}
	},

    'Raphael': {
		icon: 'raphael',
		url: 'http://raphaeljs.com',
		test: function(win) {
			if(win.Raphael) {
				return { version: win.Raphael.version };
			}
			return false;
		}
	},

	'Modernizr': {
		icon: 'modernizr',
		url: 'http://www.modernizr.com',
		test: function(win) {
			if(win.Modernizr) {
				return { version: Modernizr._version };
			}
			return false;
		}
	},

	'Processing.js': {
		icon: 'processingjs',
		url: 'http://processingjs.org',
		test: function(win) {
			if(win.Processing) {
				return { version: '(version not detectable)' };
			}
			return false;
		}
	},

	'Backbone.js': {
		icon: 'backbone', //@todo add in icon
		url: 'http://documentcloud.github.com/backbone',
		test: function(win) {
			if (win.Backbone) {
				return {version: win.Backbone.VERSION};
			}
			return false;
		}
	},

	'Underscore.js': {
		icon: 'underscore', //@todo add in icon
		url: 'http://documentcloud.github.com/underscore',
		test: function(win) {
			// *should* be safeish for sites that have assigned a generic "_" to something else
			if (win._ && win._.VERSION && typeof win._.tap === 'function') {
				return {version: win._.VERSION};
			}
			return false;
		}
	},

	'Sammy.js': {
		icon: 'sammy',
		url: 'http://sammyjs.org',
		test: function(win) {
			if (win.Sammy && win.Sammy.VERSION) {
				return {version: win.Sammy.VERSION};
			}
			return false;
		}
	},

	'Rico': {
		icon: 'rico',
		url: 'http://openrico.org',
		test:  function(win) {
			if (win.Rico && win.Rico.Version) {
				return {version: win.Rico.Version};
			}
			return false;
		}
	}

};<commit_msg>Removed comment<commit_after>var d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests = {

	'jQuery': {
		icon: 'jquery',
		url: 'http://jquery.com',
		test: function(win) {
			var jq = win.jQuery || win.$ || win.$jq || win.$j;
			if(jq && jq.fn && jq.fn.jquery) {
				return { version: jq.fn.jquery };
			}
			return false;
		}
	},

	'jQuery UI': {
		icon: 'jquery_ui',
		url: 'http://jqueryui.com',
		test: function(win) {
			var jq = win.jQuery || win.$ || win.$jq || win.$j;
			if(jq && jq.fn && jq.fn.jquery && jq.ui) {
				var plugins = 'accordion,datepicker,dialog,draggable,droppable,progressbar,resizable,selectable,slider,menu,grid,tabs'.split(','), concat = [];
				for (var i=0; i < plugins.length; i++) { if(jq.ui[plugins[i]]) concat.push(plugins[i].substr(0,1).toUpperCase() + plugins[i].substr(1)); };
				return { version: jq.ui.version, details: concat.length ? 'Plugins used: '+concat.join(',') : '' };
			}
			return false;
		}
	},

	'Dojo': {
		icon: 'dojo',
		url: 'http://dojotoolkit.org',
		test: function(win) {
			if(win.dojo) {
				return { version: win.dojo.version.toString(), details: 'Details: '+(win.dijit ? 'Uses Dijit' : 'none') };
			}
			return false;
		}
	},

	'Prototype': {
		icon: 'prototype',
		url: 'http://prototypejs.org',
		test: function(win) {
			if(win.Prototype && win.Prototype.Version) {
				return { version: win.Prototype.Version };
			}
			return false;
		}
	},

	'Scriptaculous': {
		icon: 'scriptaculous',
		url: 'http://script.aculo.us',
		test: function(win) {
			if(win.Scriptaculous && win.Scriptaculous.Version) {
				return { version: win.Scriptaculous.Version };
			}
			return false;
		}
	},

	'MooTools': {
		icon: 'mootools',
		url: 'http://mootools.net',
		test: function(win) {
			if(win.MooTools && win.MooTools.version) {
				return { version: win.MooTools.version };
			}
			return false;
		}
	},

	'Spry': {
		icon: 'spry',
		url: 'http://labs.adobe.com/technologies/spry',
		test: function(win) {
			if(win.Spry) {
				return { version: '(not detectable)' };
			}
			return false;
		}
	},

	'YUI': {
		icon: 'yui',
		url: 'http://developer.yahoo.com/yui',
		test: function(win) {
			if(win.YAHOO && win.YAHOO.VERSION) {
				return { version: win.YAHOO.VERSION };
			}
			return false;
		}
	},

	'Qooxdoo': {
		icon: 'qooxdoo',
		url: 'http://qooxdoo.org',
		test: function(win) {
			if(win.qx && win.qx.Bootstrap) {
				return { version: '(not detectable)' };
			}
			return false;
		}
	},

	'Ext JS': {
		icon: 'extjs',
		url: 'http://www.sencha.com/products/extjs',
		test: function(win) {
			if(win.Ext && win.Ext.version) {
				return { version: win.Ext.version };
			}
			return false;
		}
	},

	'base2': {
		icon: 'base2',
		url: 'http://code.google.com/p/base2',
		test: function(win) {
			if(win.base2 && win.base2.version) {
				return { version: win.base2.version };
			}
			return false;
		}
	},

	'Closure': {
		icon: 'closure',
		url: 'http://code.google.com/closure',
		test: function(win) {
			if(win.goog) {
				return { version: '2.0' };
			}
			return false;
		}
	},

    'Raphael': {
		icon: 'raphael',
		url: 'http://raphaeljs.com',
		test: function(win) {
			if(win.Raphael) {
				return { version: win.Raphael.version };
			}
			return false;
		}
	},

	'Modernizr': {
		icon: 'modernizr',
		url: 'http://www.modernizr.com',
		test: function(win) {
			if(win.Modernizr) {
				return { version: Modernizr._version };
			}
			return false;
		}
	},

	'Processing.js': {
		icon: 'processingjs',
		url: 'http://processingjs.org',
		test: function(win) {
			if(win.Processing) {
				return { version: '(version not detectable)' };
			}
			return false;
		}
	},

	'Backbone.js': {
		icon: 'backbone',
		url: 'http://documentcloud.github.com/backbone',
		test: function(win) {
			if (win.Backbone) {
				return {version: win.Backbone.VERSION};
			}
			return false;
		}
	},

	'Underscore.js': {
		icon: 'underscore',
		url: 'http://documentcloud.github.com/underscore',
		test: function(win) {
			// *should* be safeish for sites that have assigned a generic "_" to something else
			if (win._ && win._.VERSION && typeof win._.tap === 'function') {
				return {version: win._.VERSION};
			}
			return false;
		}
	},

	'Sammy.js': {
		icon: 'sammy',
		url: 'http://sammyjs.org',
		test: function(win) {
			if (win.Sammy && win.Sammy.VERSION) {
				return {version: win.Sammy.VERSION};
			}
			return false;
		}
	},

	'Rico': {
		icon: 'rico',
		url: 'http://openrico.org',
		test:  function(win) {
			if (win.Rico && win.Rico.Version) {
				return {version: win.Rico.Version};
			}
			return false;
		}
	}

};<|endoftext|>


<commit_before>var Part = require('./part');
var Rels = require('./rels');
var util = require('util');
var utilities = require('../utilities');

function PartWithRels() {
    Part.apply(this, arguments);
}
module.exports = PartWithRels;

util.inherits(PartWithRels, Part);

Object.defineProperties(
    PartWithRels.prototype,
    utilities.unsettable('rels', {
        getter: function() {
            return this._rels || (this._rels = new Rels(this));
        }
    })
);<commit_msg>code formatting<commit_after>var Part = require('./part');
var Rels = require('./rels');
var util = require('util');
var utilities = require('../utilities');

function PartWithRels() {
    Part.apply(this, arguments);
}
module.exports = PartWithRels;

util.inherits(PartWithRels, Part);

Object.defineProperties(PartWithRels.prototype,
    utilities.unsettable('rels', {
        getter: function() {
            return this._rels || (this._rels = new Rels(this));
        }
    })
);<|endoftext|>


<commit_before>/** @babel */
/** @jsx etch.dom */

const etch = require('etch')

/**
 * An Etch component to display a list of packages
 *
 * @param props should have a key 'packages' which is a list of package
 *        names to display
 */
export default class PkgListComponent {
  constructor (props, children) {
    if (!props.hasOwnProperty('packages')) {
      throw new Error('Component requires property "packages"')
    }
    this.props = props
    this.children = children
    this.packages = this.props['packages']
    this.element =

    etch.initialize(this)
  }

  // Required: The `render` method returns a virtual DOM tree representing the
  // current state of the component. Etch will call `render` to build and update
  // the component's associated DOM element. Babel is instructed to call the
  // `etch.dom` helper in compiled JSX expressions by the `@jsx` pragma above.
  /**
   * Returns the package list in

 structure
   */
  render () {
    let litems = []
    for (let pkg of this.packages) {
      litems.push(
{pkg}
)
    }
    this.element =

        {litems}

    return this.element
  }

  // Required: Update the component with new properties and children.
  update (props, children) {
    if (!props.hasOwnProperty('packages')) {
      throw new Error('Component requires property "packages"')
    }
    this.props = props
    this.children = children
    this.packages = this.props['packages']
    // perform custom update logic here...
    // then call `etch.update`, which is async and returns a promise
    return etch.update(this)
  }

  get count () {
    if (this.packages !== undefined) {
      return this.packages.length
    }
    return 0
  }

  // Optional: Destroy the component. Async/await syntax is pretty but optional.
  async destroy () {
    // call etch.destroy to remove the element and destroy child components
    await etch.destroy(this)
    // then perform custom teardown logic here...
  }
}
<commit_msg>Handle missing values, show placeholder if no packages to list<commit_after>/** @babel */
/** @jsx etch.dom */

const etch = require('etch')

/**
 * An Etch component to display a list of packages
 *
 * @param props should have a key 'packages' which is a list of package
 *        names to display
 */
export default class PkgListComponent {
  constructor (props, children) {
    this.props = props
    this.children = children
    this.element =

    etch.initialize(this)
  }

  // Required: The `render` method returns a virtual DOM tree representing the
  // current state of the component. Etch will call `render` to build and update
  // the component's associated DOM element. Babel is instructed to call the
  // `etch.dom` helper in compiled JSX expressions by the `@jsx` pragma above.
  /**
   * Returns the package list in

 structure
   */
  render () {
    let litems = []
    const pkgs = this.props.packages || []
    for (let pkg of pkgs) {
      litems.push(
{pkg}
)
    }
    if (litems.length === 0) {
      this.element =
Loading packages…

    } else {
      this.element =

          {litems}

    }
    return this.element
  }

  // Required: Update the component with new properties and children.
  update (props, children) {
    this.props = props
    this.children = children
    return etch.update(this)
  }

  get count () {
    const pkgs = this.props.packages || []
    return pkgs.length
  }

  async destroy () {
    await etch.destroy(this)
    this.props = null
    this.children = null
  }
}
<|endoftext|>


<commit_before>/*jslint node: true, plusplus: true, devel: true, nomen: true, vars: true, indent: 4, maxerr: 50 */

"use strict";

var Cursor      = require("../cursor"),
    Provider    = require("../provider"),
    Strings     = require("../strings"),
    url         = require("url"),
    util        = require("util"),
    https       = require("https"),
    _           = require("lodash");

function EverliveCursor(provider, query, options) {

    Cursor.call(this, provider, query, options);
}

util.inherits(EverliveCursor, Cursor);

function EverliveProvider(connStr, options) {
    if (!connStr || !_.isString(connStr)) {
        throw new Error("Missing connStr (connection string) argument.");
    }

    if (options) {
        if (options.typeName) {
            connStr = url.resolve(connStr, options.typeName);
        }
        this._reqOpts = url.parse(connStr);
        this._reqOpts.headers = { "Content-Type": "application/json" };

        if (options.authorization) {
            this._reqOpts.headers.Authorization = options.authorization;
        }
    }

    Provider.call(this, connStr, options);
}

util.inherits(EverliveProvider, Provider);

EverliveProvider.prototype._handleResponse = function (res, callback) {
    var that = this;

    res.setEncoding("utf8");
    if (res.statusCode === 200) {
        if (res.headers["Content-Length"]) {
            res.on('data', function(data) {
                callback(null, JSON.parse(data));
            });
        } else {
            callback();
        }
    } else {
        if (res.headers["Content-Length"]) {
            res.on("data", function (data) {
                that.handleError(data, callback);
            });
        } else {
            that.handleError("Request failed with status code: " + res.statusCode, callback);
        }
    }
};

EverliveProvider.prototype._insert = function (item, callback) {
    var payload = JSON.stringify(item),
        opts    = _.cloneDeep(this._reqOpts),
        that    = this,
        req;

    opts.method = "POST";
    opts.headers["Content-Length"] = payload.length;

    req = https.request(opts, function(res) {
        that._handleResponse(res, callback);
    });

    req.on("error", function(err) {
        that.handleError(err, callback);
    });

    req.write(payload);
    req.end();
};

EverliveProvider.prototype._update = function (item, callback) {
    var that = this;

//    that._get(item, function (err, res) {
//        if (err) {
//            return that.handleError(err, callback);
//        }
//
//        var mrg         = that.merge(item, res),
//            id          = that._getId(mrg),
//            filePath    = path.join(that.dir, id + ".json");
//
//        fs.writeFile(filePath, JSON.stringify(mrg), function (err) {
//            if (err) {
//                that.handleError(err, callback);
//            } else {
//                callback(null, mrg);
//            }
//        });
//    });
};

EverliveProvider.prototype._get = function (item, callback) {
    var that        = this,
        opts        = _.cloneDeep(this._reqOpts),
        id          = that._getId(item),
        req;

    if (!id) {
        return that.handleError(Strings.MISSING_ID, callback);
    }

    opts.method = "GET";
    opts.path += "/" + id;

    req = https.request(opts, function(res) {
        that._handleResponse(res, callback);
    });

    req.on('error', function(err) {
        that.handleError(err, callback);
    });

    req.end();
};
<commit_msg>Everlive partial implementation.<commit_after>/*jslint node: true, plusplus: true, devel: true, nomen: true, vars: true, indent: 4, maxerr: 50 */

"use strict";

var Cursor      = require("../cursor"),
    Provider    = require("../provider"),
    Strings     = require("../strings"),
    url         = require("url"),
    util        = require("util"),
    https       = require("https"),
    _           = require("lodash");

function EverliveCursor(provider, query, options) {

    Cursor.call(this, provider, query, options);
}

util.inherits(EverliveCursor, Cursor);

function EverliveProvider(connStr, options) {
    if (!connStr || !_.isString(connStr)) {
        throw new Error("Missing connStr (connection string) argument.");
    }

    if (options) {
        if (options.typeName) {
            connStr = url.resolve(connStr, options.typeName);
        }
        this._reqOpts = url.parse(connStr);
        this._reqOpts.headers = { "Content-Type": "application/json" };

        if (options.authorization) {
            this._reqOpts.headers.Authorization = options.authorization;
        }
    }

    Provider.call(this, connStr, options);
}

util.inherits(EverliveProvider, Provider);

EverliveProvider.prototype._handleResponse = function (res, callback) {
    var that = this;

    res.setEncoding("utf8");
    if (res.statusCode === 200) {
        if (res.headers["Content-Length"]) {
            res.on('data', function(data) {
                callback(null, JSON.parse(data));
            });
        } else {
            callback();
        }
    } else {
        if (res.headers["Content-Length"]) {
            res.on("data", function (data) {
                that.handleError(data, callback);
            });
        } else {
            that.handleError("Request failed with status code: " + res.statusCode, callback);
        }
    }
};

EverliveProvider.prototype._insert = function (item, callback) {
    var payload = JSON.stringify(item),
        opts    = _.cloneDeep(this._reqOpts),
        that    = this,
        req;

    opts.method = "POST";
    opts.headers["Content-Length"] = payload.length;

    req = https.request(opts, function(res) {
        that._handleResponse(res, callback);
    });

    req.on("error", function(err) {
        that.handleError(err, callback);
    });

    req.write(payload);
    req.end();
};

EverliveProvider.prototype._update = function (item, callback) {
    var payload = JSON.stringify(item),
        opts    = _.cloneDeep(this._reqOpts),
        id      = that._getId(item),
        that    = this,
        req;

    opts.method = "PUT";
    opts.headers["Content-Length"] = payload.length;
    opts.path += "/" + id;

    req = https.request(opts, function(res) {
        that._handleResponse(res, callback);
    });

    req.on("error", function(err) {
        that.handleError(err, callback);
    });

    req.write(payload);
    req.end();
};

EverliveProvider.prototype._upsert = function (item, callback) {
    var that = this;

    this._get(item, function (err, data) {
        if (data) {
            that._update(item, callback);
        } else {
            that._insert(item, callback);
        }
    });
};

EverliveProvider.prototype._get = function (item, callback) {
    var that        = this,
        opts        = _.cloneDeep(this._reqOpts),
        id          = that._getId(item),
        req;

    if (!id) {
        return that.handleError(Strings.MISSING_ID, callback);
    }

    opts.method = "GET";
    opts.path += "/" + id;

    req = https.request(opts, function(res) {
        that._handleResponse(res, callback);
    });

    req.on('error', function(err) {
        that.handleError(err, callback);
    });

    req.end();
};
<|endoftext|>


<commit_before>const create = require('./create')
const createState = create.state
const getParent = require('./parent')
const fragment = require('../fragment')
const findParent = fragment.findParent
const isFragment = fragment.isFragment

const getRemove = t => t.remove || t.inherits && getRemove(t.inherits)

const hasRemove = t => t.emitters && getRemove(t.emitters) ||
  t.inherits && hasRemove(t.inherits)

exports.props = {
  staticIndex: true,
  _cachedNode: true
}

global.cnt = 0

exports.render = {
  static: create.static,
  state (t, s, type, subs, tree, id, pid, order) {
    var node = tree._ && tree._[id]
    var pnode
    if (type === 'remove') {
      if (node) {
        pnode = getParent(tree, pid)
        if (pnode) {
          if (t.tag === 'fragment') {
            if (isFragment(pnode)) {
              pnode = findParent(pnode)
            }
            removeFragmentChild(node, pnode)
          } else if (!hasRemove(t)) {
            if (isFragment(pnode)) {
              // add tests for this
              for (let i = 0, len = pnode.length; i < len; i++) {
                if (pnode[i] === node) {
                  pnode.splice(i, 1)
                  break
                }
              }
              pnode = pnode[0]
            }
            if (isFragment(pnode)) {
              pnode = findParent(pnode)
            }
            global.cnt++
            pnode.removeChild(node)
          }
        }
        delete tree._[id]
      }
    } else if (!node) {
      node = createState(t, s, type, subs, tree, id, pid, order)
    }
    return node
  }
}

function removeFragmentChild (node, pnode) {
  for (let i = 1, len = node.length; i < len; i++) {
    if (isFragment(node[i])) {
      removeFragmentChild(node[i], pnode)
    } else {
      pnode.removeChild(node[i])
    }
  }
}
<commit_msg>remove cnt<commit_after>const create = require('./create')
const createState = create.state
const getParent = require('./parent')
const fragment = require('../fragment')
const findParent = fragment.findParent
const isFragment = fragment.isFragment

const getRemove = t => t.remove || t.inherits && getRemove(t.inherits)

const hasRemove = t => t.emitters && getRemove(t.emitters) ||
  t.inherits && hasRemove(t.inherits)

exports.props = {
  staticIndex: true,
  _cachedNode: true
}

exports.render = {
  static: create.static,
  state (t, s, type, subs, tree, id, pid, order) {
    var node = tree._ && tree._[id]
    var pnode
    if (type === 'remove') {
      if (node) {
        pnode = getParent(tree, pid)
        if (pnode) {
          if (t.tag === 'fragment') {
            if (isFragment(pnode)) {
              pnode = findParent(pnode)
            }
            removeFragmentChild(node, pnode)
          } else if (!hasRemove(t)) {
            if (isFragment(pnode)) {
              // add tests for this
              for (let i = 0, len = pnode.length; i < len; i++) {
                if (pnode[i] === node) {
                  pnode.splice(i, 1)
                  break
                }
              }
              pnode = pnode[0]
            }
            if (isFragment(pnode)) {
              pnode = findParent(pnode)
            }
            pnode.removeChild(node)
          }
        }
        delete tree._[id]
      }
    } else if (!node) {
      node = createState(t, s, type, subs, tree, id, pid, order)
    }
    return node
  }
}

function removeFragmentChild (node, pnode) {
  for (let i = 1, len = node.length; i < len; i++) {
    if (isFragment(node[i])) {
      removeFragmentChild(node[i], pnode)
    } else {
      pnode.removeChild(node[i])
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Resource routing. Handles mapping Models & Collections into routes.
 */
var _ = require('lodash');
var express = require('express');
var app = express.application;
var async = require('async');
var debug = require('debug')('serverbone:resource');
var Backbone = require('backbone');
var middleware = require('../middleware');
var serverbone = require('../');

var Resource = module.exports = function (name, options) {
  this.name = name;
  this.app = express();
  this.relations = {};

  // custom routes are added before this.app.router this we use the router before any.
  // it is set in initMiddlewares
  this.router = express();

  this.routes = {};
  options = options || {};
  // initialize this resouce's Collection & Model (both are required)

  this.CollectionClass = options.collection;
  if (!this.CollectionClass) throw new Error('collection must be defined in options');
  this.ModelClass = options.model || this.CollectionClass.prototype.model;
  this.initializeResource(options);
  this.delegateEvents();
};

_.extend(Resource.prototype, Backbone.Events);

Resource.prototype.delegateEvents = function (events) {
  if (!(events || (events = _.result(this, 'events')))) return this;
  debug('delegating events %s', JSON.stringify(events));
  for (var key in events) {
    var method = events[key];

    if (!_.isFunction(method)) method = this[events[key]];
    if (!method) continue;

    var match = key.match(/^(\S+)\s*(.*)$/),
      eventName = match[1],
      selector = match[2];

    method = _.bind(method, this);
    if (selector === '') {
      debug('Listening event %s on self', eventName);
      this.on(eventName, method);
    } else if (this[eventName] && _.isFunction(this[eventName].on)) {
      debug('Listening event %s on self.%s.on', selector, eventName);
      this[eventName].on(selector, method);
    }
  }
  return this;
};

/**
 * addRoute
 *
 * Add custom route to this resource, it will be handled before the created model routes
 * @param method {String} enum GET,POST,PUT,DELETE,PATCH,HEAD
 * @param route {String}
 * @param middleare list
 *
 * example:
 *
 * addRoute("GET", "/some_route", middleware, function(req, res, next) {
 *   res.send("HI!")
 * })
 *
 * @api public
 **/
Resource.prototype.addRoute = function () {
  debug('Add route %s', JSON.stringify(arguments));
  var method = arguments[0];
  var route = arguments[1];
  // GET, route_path, mw1, mw2 ....
  var fnList = [route].concat([].slice.call(arguments).slice(2));
  this.router[method.toLowerCase()].apply(this.router, fnList);
};

// middlewares are processed for each request
Resource.prototype.initMiddlewares = function (options) {
  // setup error domain first, always.
  this.app.use(function (req, res, next) {
    next();
  });
  this.app.use(middleware.domain());
  this.app.use(this.router);
};

Resource.prototype.initializeResource = function (options) {
  options = options || {};
  var self = this;
  var apiHandlers = {};
  var buildModel = _.bind(this.buildModel, this);
  var buildCollection = _.bind(this.buildCollection, this);
  var update = _.bind(this.update, this);
  var create = _.bind(this.create, this);
  var save = _.bind(this.save, this);
  var remove = _.bind(this.remove, this);
  var send = serverbone.utils.response.sendJson;

  //init middlewares etc. first
  this.initMiddlewares();
  this._addParameterHandler();

  // Setup crud API routes
  // Chained method handlers per method
  apiHandlers = {
    list: [buildCollection, send],
    get: [buildModel, send],
    post: [buildModel, create, send],
    put: [buildModel, update, save, send],
    patch: [buildModel, update, save, send],
    'delete': [buildModel, remove, send],
  };

  apiHandlers = _.chain(apiHandlers)
    .extend(options.apiHandlers)
    .value();

  this.apiHandlers = apiHandlers;

  // Setup custom routes if any
  if (options.customRoutes) {
    options.customRoutes.forEach(function (config) {
      self._addCustomRoute(config);
    });
  }

  // bind each array of handlers to the proper HTTP verb.
  Object.keys(apiHandlers).forEach(function (method) {
    self._addRoute(method);
  });

  if (options.mountRelations === true) {
    // mount collection relations
    this._mountRelations();
  }

  // error handling must be last
  this._addErrorHandler();
};

/**
 * _mountRelations
 *
 * Goes trough relations specified in the model schema and mounts all relations as
 * subresources. Schema should set {mountRelations: true} option and
 * relation must have set option {mount: true}, if it should be mounted.
 */
Resource.prototype._mountRelations = function () {
  var self = this;

  var schema = this.ModelClass.prototype.schema;
  if (!schema) return;

  var properties = schema.properties;
  if (!properties) return;

  Object.keys(properties).forEach(function (propertyName) {
    var property = properties[propertyName];
    var mountRelation = typeof property === 'object'
        && typeof property.collection !==  'undefined'
        && property.mount === true;

    if (mountRelation) {
      var relationCollection = property.collection;
      var mountName = property.name || propertyName;

      var ResourceClass;

      if (_.isString(property.resourceType)) {
        if (property.resourceType === 'list') {
          ResourceClass = serverbone.resources.ListResource;
        }
      }

      if (_.isObject(property.resourceType)) {
        ResourceClass = property.resourceType;
      }

      if (!ResourceClass) ResourceClass = Resource;

      var resource = new ResourceClass(self.name + '_' + mountName, {
        collection: relationCollection
      });

      resource._constructCollection = function (req) {
        return req.model.get(propertyName);
      };

      self.relations[mountName] = resource;

      self.app.all('/:id/' + mountName, function (req, res, next) {
        req.originalUrl = req.url;
        req.url = req.url.replace(req.params.id + '/' + mountName, '');
        delete req.params;
        resource.app.call(resource.app, req, res, next);
      });

      self.app.all('/:id/' + mountName + '/:relId', function (req, res, next) {
        req.originalUrl = req.url;
        req.url = req.url.replace(req.params.id + '/' + mountName + '/', '');
        delete req.params;
        resource.app.call(resource.app, req, res, next);
      });

    }
  });
};

/**
 * Adds express app.param handler for this routes model.idAttribute
 *
 * The param handler creates a model instance to req.model and sets the models idAttribute to the captured param.
 * req.model.fetch() will be called and the returned promise set to req.model.promise then calls next().
 *
 * example:
 *
 * req.model.promise.then(function() {
 *   res.send(req.model);
 * });
 *
 * @api private
 */
Resource.prototype._addParameterHandler = function () {
  var self = this;
  this.app.param('id', function(req, res, next, id) {
    switch (req.method) {
      case 'GET':
      case 'DELETE':
      case 'PUT':
      case 'PATCH':
      case 'POST':
        req.model = self._constructModel(req);
        req.model.set(req.model.idAttribute, id);

        if (id && id !== 'undefined') { //TODO fix this bug
          req.model.promise = req.model.fetch({
            actor: req.actor
          });
        } else {
          console.warn('id param missing', req.url);
        }
        break;
      default:
        return next(new Error('Unknown request method: ' + req.method));
    }
    next();
  });
};

/**
 * _addErrorHandler
 *
 * Adds express application error handler also adds this application under its own error domain that handles any
 * uncaught exceptions relating to this resource.
 *
 * this.app middleware chain is already ran using domain that forwards all uncaught exceptions here.
 */
Resource.prototype._addErrorHandler = function () {
  debug('Adding express errorhandler');
  this.app.use(function (err, req, res, next) {
    return serverbone.utils.response.sendError(req, res, err);
  });
};

/**
 * _addRoute adds route to express application
 */
Resource.prototype._addRoute = function (method) {
  var methodHandlers = this.apiHandlers[method];
  var route = '/';

  if (method !== 'post' && method !== 'list') route += ':id';
  if (method === 'list') method = 'get';

  this.routes[method] = {
    method: method,
    path: route
  };
  var args = [route].concat(methodHandlers);
  debug('%s AddRoute: %s %s', this.name, method, route);
  this.app[method].apply(this.app, args);
};

/**
 * Adds a custom route to Resource
 * @param {Object} routeConfig, fmt:
 * {path: '/foo', method: 'get', handlers: [fn1, fn2]}
 */
Resource.prototype._addCustomRoute = function (routeConfig) {
  var route = routeConfig.path;
  var args = [route].concat(routeConfig.handlers);
  this.app[routeConfig.method].apply(this.app, args);
};

// Instantiate new Model if POST, otherwise fetch existing
Resource.prototype.buildModel = function(req, res, next) {
  debug('buildModel method: %s, url: %s', req.method, req.url);
  switch (req.method) {
    case 'POST':
      res.locals.resource = this._constructModel(req, req.body);
      debug('model added to res.local.resource: %s', JSON.stringify(res.locals.resource));
      next();
      break;
    default:
      req.model.promise.done(
        function() {
          return next();
        },
        function handleError(err) {
          return next(err);
        }
      );
  }
};

Resource.prototype._getModelClass = function (req) {
  var Class;
  if (this.ModelClass) {
    Class = this.ModelClass;
  } else if (this.CollectionClass && this.CollectionClass.prototype.model) {
    Class = this.CollectionClass.prototype.model;
  } else {
    Class = this._constructCollection(req).model;
  }

  return Class;
};

Resource.prototype._constructModel = function (req, data) {
  var Class = this._getModelClass(req);
  return new Class(data, {actor: req.actor});
};

Resource.prototype._constructCollection = function (req) {
  return new this.CollectionClass(null, {actor: req.actor});
};

// Fetch collection with given params
Resource.prototype.buildCollection = function (req, res, next) {
  res.locals.resource = req.collection = this._constructCollection(req);
  var fetchOptions = this._parseFetchOptions(req.query);
  fetchOptions.actor = fetchOptions.actor || req.actor;
  req.collection
    .fetch(fetchOptions)
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};

// Parse & convert options for collection.fetch
Resource.prototype._parseFetchOptions = function (sentOptions) {
  var opts = {};
  if (sentOptions.sort) opts.sort = sentOptions.sort;
  if (sentOptions.limit) opts.limit = parseInt(sentOptions.limit, 10);
  if (sentOptions.offset) opts.offset = parseInt(sentOptions.offset, 10);
  if (sentOptions.before_id) opts.before_id = sentOptions.before_id;
  if (sentOptions.after_id) opts.after_id = sentOptions.after_id;
  return opts;
};

// Update Model attributes
Resource.prototype.update = function (req, res, next) {
  var attrs = req.body;
  req.model.set(attrs);
  next();
};

// Create new model
Resource.prototype.create = function (req, res, next) {
  var collection = this._constructCollection(req);
  collection
    .create(res.locals.resource.toJSON({actor: req.actor}), {wait: true, actor: req.actor})
    .done(function (model) {
      res.locals.resource = model;
      next();
    }, function(err) {
      next(err);
    });
};

// Save updated model or update changed attributes with PATCH
Resource.prototype.save = function (req, res, next) {
  //TODO: PATCH support
  req.model
    .save(null, {actor: req.actor})
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};

// Destroy Model
Resource.prototype.remove = function (req, res, next) {
  req.model
    .destroy({actor: req.actor})
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};<commit_msg>Set actor for sub collection<commit_after>/**
 * Resource routing. Handles mapping Models & Collections into routes.
 */
var _ = require('lodash');
var express = require('express');
var app = express.application;
var async = require('async');
var debug = require('debug')('serverbone:resource');
var Backbone = require('backbone');
var middleware = require('../middleware');
var serverbone = require('../');

var Resource = module.exports = function (name, options) {
  this.name = name;
  this.app = express();
  this.relations = {};

  // custom routes are added before this.app.router this we use the router before any.
  // it is set in initMiddlewares
  this.router = express();

  this.routes = {};
  options = options || {};
  // initialize this resouce's Collection & Model (both are required)

  this.CollectionClass = options.collection;
  if (!this.CollectionClass) throw new Error('collection must be defined in options');
  this.ModelClass = options.model || this.CollectionClass.prototype.model;
  this.initializeResource(options);
  this.delegateEvents();
};

_.extend(Resource.prototype, Backbone.Events);

Resource.prototype.delegateEvents = function (events) {
  if (!(events || (events = _.result(this, 'events')))) return this;
  debug('delegating events %s', JSON.stringify(events));
  for (var key in events) {
    var method = events[key];

    if (!_.isFunction(method)) method = this[events[key]];
    if (!method) continue;

    var match = key.match(/^(\S+)\s*(.*)$/),
      eventName = match[1],
      selector = match[2];

    method = _.bind(method, this);
    if (selector === '') {
      debug('Listening event %s on self', eventName);
      this.on(eventName, method);
    } else if (this[eventName] && _.isFunction(this[eventName].on)) {
      debug('Listening event %s on self.%s.on', selector, eventName);
      this[eventName].on(selector, method);
    }
  }
  return this;
};

/**
 * addRoute
 *
 * Add custom route to this resource, it will be handled before the created model routes
 * @param method {String} enum GET,POST,PUT,DELETE,PATCH,HEAD
 * @param route {String}
 * @param middleare list
 *
 * example:
 *
 * addRoute("GET", "/some_route", middleware, function(req, res, next) {
 *   res.send("HI!")
 * })
 *
 * @api public
 **/
Resource.prototype.addRoute = function () {
  debug('Add route %s', JSON.stringify(arguments));
  var method = arguments[0];
  var route = arguments[1];
  // GET, route_path, mw1, mw2 ....
  var fnList = [route].concat([].slice.call(arguments).slice(2));
  this.router[method.toLowerCase()].apply(this.router, fnList);
};

// middlewares are processed for each request
Resource.prototype.initMiddlewares = function (options) {
  // setup error domain first, always.
  this.app.use(function (req, res, next) {
    next();
  });
  this.app.use(middleware.domain());
  this.app.use(this.router);
};

Resource.prototype.initializeResource = function (options) {
  options = options || {};
  var self = this;
  var apiHandlers = {};
  var buildModel = _.bind(this.buildModel, this);
  var buildCollection = _.bind(this.buildCollection, this);
  var update = _.bind(this.update, this);
  var create = _.bind(this.create, this);
  var save = _.bind(this.save, this);
  var remove = _.bind(this.remove, this);
  var send = serverbone.utils.response.sendJson;

  //init middlewares etc. first
  this.initMiddlewares();
  this._addParameterHandler();

  // Setup crud API routes
  // Chained method handlers per method
  apiHandlers = {
    list: [buildCollection, send],
    get: [buildModel, send],
    post: [buildModel, create, send],
    put: [buildModel, update, save, send],
    patch: [buildModel, update, save, send],
    'delete': [buildModel, remove, send],
  };

  apiHandlers = _.chain(apiHandlers)
    .extend(options.apiHandlers)
    .value();

  this.apiHandlers = apiHandlers;

  // Setup custom routes if any
  if (options.customRoutes) {
    options.customRoutes.forEach(function (config) {
      self._addCustomRoute(config);
    });
  }

  // bind each array of handlers to the proper HTTP verb.
  Object.keys(apiHandlers).forEach(function (method) {
    self._addRoute(method);
  });

  if (options.mountRelations === true) {
    // mount collection relations
    this._mountRelations();
  }

  // error handling must be last
  this._addErrorHandler();
};

/**
 * _mountRelations
 *
 * Goes trough relations specified in the model schema and mounts all relations as
 * subresources. Schema should set {mountRelations: true} option and
 * relation must have set option {mount: true}, if it should be mounted.
 */
Resource.prototype._mountRelations = function () {
  var self = this;

  var schema = this.ModelClass.prototype.schema;
  if (!schema) return;

  var properties = schema.properties;
  if (!properties) return;

  Object.keys(properties).forEach(function (propertyName) {
    var property = properties[propertyName];
    var mountRelation = typeof property === 'object'
        && typeof property.collection !==  'undefined'
        && property.mount === true;

    if (mountRelation) {
      var relationCollection = property.collection;
      var mountName = property.name || propertyName;

      var ResourceClass;

      if (_.isString(property.resourceType)) {
        if (property.resourceType === 'list') {
          ResourceClass = serverbone.resources.ListResource;
        }
      }

      if (_.isObject(property.resourceType)) {
        ResourceClass = property.resourceType;
      }

      if (!ResourceClass) ResourceClass = Resource;

      var resource = new ResourceClass(self.name + '_' + mountName, {
        collection: relationCollection
      });

      resource._constructCollection = function (req) {
        var collection = req.model.get(propertyName);
        if (!collection.actor) collection.actor = req.actor;
        return collection;
      };

      self.relations[mountName] = resource;

      self.app.all('/:id/' + mountName, function (req, res, next) {
        req.originalUrl = req.url;
        req.url = req.url.replace(req.params.id + '/' + mountName, '');
        delete req.params;
        resource.app.call(resource.app, req, res, next);
      });

      self.app.all('/:id/' + mountName + '/:relId', function (req, res, next) {
        req.originalUrl = req.url;
        req.url = req.url.replace(req.params.id + '/' + mountName + '/', '');
        delete req.params;
        resource.app.call(resource.app, req, res, next);
      });

    }
  });
};

/**
 * Adds express app.param handler for this routes model.idAttribute
 *
 * The param handler creates a model instance to req.model and sets the models idAttribute to the captured param.
 * req.model.fetch() will be called and the returned promise set to req.model.promise then calls next().
 *
 * example:
 *
 * req.model.promise.then(function() {
 *   res.send(req.model);
 * });
 *
 * @api private
 */
Resource.prototype._addParameterHandler = function () {
  var self = this;
  this.app.param('id', function(req, res, next, id) {
    switch (req.method) {
      case 'GET':
      case 'DELETE':
      case 'PUT':
      case 'PATCH':
      case 'POST':
        req.model = self._constructModel(req);
        req.model.set(req.model.idAttribute, id);

        if (id && id !== 'undefined') { //TODO fix this bug
          req.model.promise = req.model.fetch({
            actor: req.actor
          });
        } else {
          console.warn('id param missing', req.url);
        }
        break;
      default:
        return next(new Error('Unknown request method: ' + req.method));
    }
    next();
  });
};

/**
 * _addErrorHandler
 *
 * Adds express application error handler also adds this application under its own error domain that handles any
 * uncaught exceptions relating to this resource.
 *
 * this.app middleware chain is already ran using domain that forwards all uncaught exceptions here.
 */
Resource.prototype._addErrorHandler = function () {
  debug('Adding express errorhandler');
  this.app.use(function (err, req, res, next) {
    return serverbone.utils.response.sendError(req, res, err);
  });
};

/**
 * _addRoute adds route to express application
 */
Resource.prototype._addRoute = function (method) {
  var methodHandlers = this.apiHandlers[method];
  var route = '/';

  if (method !== 'post' && method !== 'list') route += ':id';
  if (method === 'list') method = 'get';

  this.routes[method] = {
    method: method,
    path: route
  };
  var args = [route].concat(methodHandlers);
  debug('%s AddRoute: %s %s', this.name, method, route);
  this.app[method].apply(this.app, args);
};

/**
 * Adds a custom route to Resource
 * @param {Object} routeConfig, fmt:
 * {path: '/foo', method: 'get', handlers: [fn1, fn2]}
 */
Resource.prototype._addCustomRoute = function (routeConfig) {
  var route = routeConfig.path;
  var args = [route].concat(routeConfig.handlers);
  this.app[routeConfig.method].apply(this.app, args);
};

// Instantiate new Model if POST, otherwise fetch existing
Resource.prototype.buildModel = function(req, res, next) {
  debug('buildModel method: %s, url: %s', req.method, req.url);
  switch (req.method) {
    case 'POST':
      res.locals.resource = this._constructModel(req, req.body);
      debug('model added to res.local.resource: %s', JSON.stringify(res.locals.resource));
      next();
      break;
    default:
      req.model.promise.done(
        function() {
          return next();
        },
        function handleError(err) {
          return next(err);
        }
      );
  }
};

Resource.prototype._getModelClass = function (req) {
  var Class;
  if (this.ModelClass) {
    Class = this.ModelClass;
  } else if (this.CollectionClass && this.CollectionClass.prototype.model) {
    Class = this.CollectionClass.prototype.model;
  } else {
    Class = this._constructCollection(req).model;
  }

  return Class;
};

Resource.prototype._constructModel = function (req, data) {
  var Class = this._getModelClass(req);
  return new Class(data, {actor: req.actor});
};

Resource.prototype._constructCollection = function (req) {
  return new this.CollectionClass(null, {actor: req.actor});
};

// Fetch collection with given params
Resource.prototype.buildCollection = function (req, res, next) {
  res.locals.resource = req.collection = this._constructCollection(req);
  var fetchOptions = this._parseFetchOptions(req.query);
  fetchOptions.actor = fetchOptions.actor || req.actor;
  req.collection
    .fetch(fetchOptions)
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};

// Parse & convert options for collection.fetch
Resource.prototype._parseFetchOptions = function (sentOptions) {
  var opts = {};
  if (sentOptions.sort) opts.sort = sentOptions.sort;
  if (sentOptions.limit) opts.limit = parseInt(sentOptions.limit, 10);
  if (sentOptions.offset) opts.offset = parseInt(sentOptions.offset, 10);
  if (sentOptions.before_id) opts.before_id = sentOptions.before_id;
  if (sentOptions.after_id) opts.after_id = sentOptions.after_id;
  return opts;
};

// Update Model attributes
Resource.prototype.update = function (req, res, next) {
  var attrs = req.body;
  req.model.set(attrs);
  next();
};

// Create new model
Resource.prototype.create = function (req, res, next) {
  var collection = this._constructCollection(req);
  collection
    .create(res.locals.resource.toJSON({actor: req.actor}), {wait: true, actor: req.actor})
    .done(function (model) {
      res.locals.resource = model;
      next();
    }, function(err) {
      next(err);
    });
};

// Save updated model or update changed attributes with PATCH
Resource.prototype.save = function (req, res, next) {
  //TODO: PATCH support
  req.model
    .save(null, {actor: req.actor})
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};

// Destroy Model
Resource.prototype.remove = function (req, res, next) {
  req.model
    .destroy({actor: req.actor})
    .done(function () {
      next();
    }, function(err) {
      next(err);
    });
};<|endoftext|>


<commit_before>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true XM:true */

(function () {
  "use strict";

  var _ = X._,
    exec = require('child_process').exec;

  /**
    Used to serve up files to the client. Uses Content-Type to prompt browser to
    save the file

    @extends X.Route
    @class
   */
  X.maintenanceRoute = X.Route.create({

    handle: function (xtr) {
      var that = this,
        commandCount = 0,
        commandErrorCount = 0,
        commandsReturned = 0,
        commandLog = [],
        outLog = [],
        errorLog = [],
        returnObject = {},
        url = require("url"),
        querystring = require("querystring"),
        originalUrl = xtr.get("url"),
        args = querystring.parse(url.parse(originalUrl).query);

      if (args.key !== 'blerg') {
        // TODO: better authentication
        //cookie = xtr.request.cookies.xtsessioncookie,
        returnObject.status = "ERROR";
        returnObject.error = "Unauthorized maintenance request";
        xtr.write(JSON.stringify(returnObject)).close();
        return;
      }

      var organizationColl = new XM.OrganizationCollection(),
        fetchSuccess = function (model, response) {
          _.each(response, function (org) {
            if (!org.isActive) {
              // no need to update disactivated organizations
              return;
            }
            _.each(org.extensions, function (ext) {
              var extLoc = ext.extension.location,
                extName = ext.extension.name,

                scriptDir = ".." + extLoc + "/source/" + extName + "/database/source",
                scriptName = "init_script.sql",
                host = org.databaseServer.hostname,
                port = org.databaseServer.port,
                orgName = org.name,
                flags = "-h " + host + " -p " + port + " -d " + orgName + " -f " + scriptName + "\n",
                psqlRoot = "/usr/local/pgsql/bin/psql",
                // var psqlRoot = "/usr/bin/psql"; // maxhammer
                sudoUser = process.env.SUDO_USER,
                psqlCommand = psqlRoot + " -U " + sudoUser + " " + flags,
                callback = function (error, stdout, stderr) {
                  outLog.push(stdout);
                  if (stderr) {
                    errorLog.push(stderr);
                  }
                  if (error !== null) {
                    commandErrorCount++;
                    errorLog.push("execution error: " + error);
                  }
                  commandsReturned++;

                  // TODO: error on timeout
                  if (commandsReturned === commandCount) {
                    returnObject.status = "SUCCESS";
                    returnObject.errorCount = commandErrorCount;
                    returnObject.log = outLog;
                    returnObject.commandLog = commandLog;
                    returnObject.errorLog = errorLog;
                    xtr.write(JSON.stringify(returnObject)).close();
                  }
                };

              // allow the user to single out a particular organization
              // for updating if they want.
              if (args.organization && args.organization !== orgName) {
                return;
              }

              // execute command
              exec("(cd " + scriptDir + " && exec " + psqlCommand + ")", callback);
              commandCount++;
              commandLog.push(psqlCommand);
            });
          });
          if (commandCount === 0) {
            // Report fully back even if not all the commands were run.
            returnObject.status = "SUCCESS";
            returnObject.errorCount = commandErrorCount;
            returnObject.log = outLog;
            returnObject.commandLog = commandLog;
            returnObject.errorLog = errorLog;
            xtr.write(JSON.stringify(returnObject)).close();
          }
        },
        fetchError = function (model, error) {
          console.log("fetch error");
          xtr.write("error").close();
        },
        options = {
          success: fetchSuccess,
          error: fetchError,
          username: X.options.globalDatabase.nodeUsername
        };

      organizationColl.fetch(options);

    },

    executeCommand: function (flags, xtr) {

    },

    handles: ["maintenance", "/maintenance"]
  });
}());
<commit_msg>issue #19051: fixed incorrect comment<commit_after>/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
regexp:true, undef:true, strict:true, trailing:true, white:true */
/*global X:true XM:true */

(function () {
  "use strict";

  var _ = X._,
    exec = require('child_process').exec;

  /**
    Used to serve up files to the client. Uses Content-Type to prompt browser to
    save the file

    @extends X.Route
    @class
   */
  X.maintenanceRoute = X.Route.create({

    handle: function (xtr) {
      var that = this,
        commandCount = 0,
        commandErrorCount = 0,
        commandsReturned = 0,
        commandLog = [],
        outLog = [],
        errorLog = [],
        returnObject = {},
        url = require("url"),
        querystring = require("querystring"),
        originalUrl = xtr.get("url"),
        args = querystring.parse(url.parse(originalUrl).query);

      if (args.key !== 'blerg') {
        // TODO: better authentication
        //cookie = xtr.request.cookies.xtsessioncookie,
        returnObject.status = "ERROR";
        returnObject.error = "Unauthorized maintenance request";
        xtr.write(JSON.stringify(returnObject)).close();
        return;
      }

      var organizationColl = new XM.OrganizationCollection(),
        fetchSuccess = function (model, response) {
          _.each(response, function (org) {
            if (!org.isActive) {
              // no need to update disactivated organizations
              return;
            }
            _.each(org.extensions, function (ext) {
              var extLoc = ext.extension.location,
                extName = ext.extension.name,

                scriptDir = ".." + extLoc + "/source/" + extName + "/database/source",
                scriptName = "init_script.sql",
                host = org.databaseServer.hostname,
                port = org.databaseServer.port,
                orgName = org.name,
                flags = "-h " + host + " -p " + port + " -d " + orgName + " -f " + scriptName + "\n",
                psqlRoot = "/usr/local/pgsql/bin/psql",
                // var psqlRoot = "/usr/bin/psql"; // maxhammer
                sudoUser = process.env.SUDO_USER,
                psqlCommand = psqlRoot + " -U " + sudoUser + " " + flags,
                callback = function (error, stdout, stderr) {
                  outLog.push(stdout);
                  if (stderr) {
                    errorLog.push(stderr);
                  }
                  if (error !== null) {
                    commandErrorCount++;
                    errorLog.push("execution error: " + error);
                  }
                  commandsReturned++;

                  // TODO: error on timeout
                  if (commandsReturned === commandCount) {
                    returnObject.status = "SUCCESS";
                    returnObject.errorCount = commandErrorCount;
                    returnObject.log = outLog;
                    returnObject.commandLog = commandLog;
                    returnObject.errorLog = errorLog;
                    xtr.write(JSON.stringify(returnObject)).close();
                  }
                };

              // allow the user to single out a particular organization
              // for updating if they want.
              if (args.organization && args.organization !== orgName) {
                return;
              }

              // execute command
              exec("(cd " + scriptDir + " && exec " + psqlCommand + ")", callback);
              commandCount++;
              commandLog.push(psqlCommand);
            });
          });
          if (commandCount === 0) {
            // Report fully back even if no commands were run.
            returnObject.status = "SUCCESS";
            returnObject.errorCount = commandErrorCount;
            returnObject.log = outLog;
            returnObject.commandLog = commandLog;
            returnObject.errorLog = errorLog;
            xtr.write(JSON.stringify(returnObject)).close();
          }
        },
        fetchError = function (model, error) {
          console.log("fetch error");
          xtr.write("error").close();
        },
        options = {
          success: fetchSuccess,
          error: fetchError,
          username: X.options.globalDatabase.nodeUsername
        };

      organizationColl.fetch(options);

    },

    executeCommand: function (flags, xtr) {

    },

    handles: ["maintenance", "/maintenance"]
  });
}());
<|endoftext|>