<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

exports.last = function(a) {
    return a[a.length - 1];
};

exports.stringReverse = function(string) {
    return string.split("").reverse().join("");
};

exports.stringRepeat = function (string, count) {
    var result = '';
    while (count > 0) {
        if (count & 1)
            result += string;

        if (count >>= 1)
            string += string;
    }
    return result;
};

var trimBeginRegexp = /^\s\s*/;
var trimEndRegexp = /\s\s*$/;

exports.stringTrimLeft = function (string) {
    return string.replace(trimBeginRegexp, '');
};

exports.stringTrimRight = function (string) {
    return string.replace(trimEndRegexp, '');
};

exports.copyObject = function(obj) {
    var copy = {};
    for (var key in obj) {
        copy[key] = obj[key];
    }
    return copy;
};

exports.copyArray = function(array){
    var copy = [];
    for (var i=0, l=array.length; i<l; i++) {
        if (array[i] && typeof array[i] == "object")
            copy[i] = this.copyObject( array[i] );
        else
            copy[i] = array[i];
    }
    return copy;
};

exports.deepCopy = function (obj) {
    if (typeof obj !== "object" || !obj)
        return obj;
    var cons = obj.constructor;
    if (cons === RegExp)
        return obj;

    var copy = cons();
    for (var key in obj) {
        if (typeof obj[key] === "object") {
            copy[key] = exports.deepCopy(obj[key]);
        } else {
            copy[key] = obj[key];
        }
    }
    return copy;
};

exports.arrayToMap = function(arr) {
    var map = {};
    for (var i=0; i<arr.length; i++) {
        map[arr[i]] = 1;
    }
    return map;

};

exports.createMap = function(props) {
    var map = Object.create(null);
    for (var i in props) {
        map[i] = props[i];
    }
    return map;
};

/*
 * splice out of 'array' anything that === 'value'
 */
exports.arrayRemove = function(array, value) {
  for (var i = 0; i <= array.length; i++) {
    if (value === array[i]) {
      array.splice(i, 1);
    }
  }
};

exports.escapeRegExp = function(str) {
    return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
};

exports.escapeHTML = function(str) {
    return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<");
};

exports.getMatchOffsets = function(string, regExp) {
    var matches = [];

    string.replace(regExp, function(str) {
        matches.push({
            offset: arguments[arguments.length-2],
            length: str.length
        });
    });

    return matches;
};

/* deprecated */
exports.deferredCall = function(fcn) {
    var timer = null;
    var callback = function() {
        timer = null;
        fcn();
    };

    var deferred = function(timeout) {
        deferred.cancel();
        timer = setTimeout(callback, timeout || 0);
        return deferred;
    };

    deferred.schedule = deferred;

    deferred.call = function() {
        this.cancel();
        fcn();
        return deferred;
    };

    deferred.cancel = function() {
        clearTimeout(timer);
        timer = null;
        return deferred;
    };

    deferred.isPending = function() {
        return timer;
    };

    return deferred;
};

exports.delayedCall = function(fcn, defaultTimeout) {
    var timer = null;
    var callback = function() {
        timer = null;
        fcn();
    };

    var _self = function(timeout) {
        if (timer == null)
            timer = setTimeout(callback, timeout || defaultTimeout);
    };

    _self.delay = function(timeout) {
        timer && clearTimeout(timer);
        timer = setTimeout(callback, timeout || defaultTimeout);
    };
    _self.schedule = _self;

    _self.call = function() {
        this.cancel();
        fcn();
    };

    _self.cancel = function() {
        timer && clearTimeout(timer);
        timer = null;
    };

    _self.isPending = function() {
        return timer;
    };

    return _self;
};
});
<commit_msg>Make deepCopy work correctly with arrays<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

exports.last = function(a) {
    return a[a.length - 1];
};

exports.stringReverse = function(string) {
    return string.split("").reverse().join("");
};

exports.stringRepeat = function (string, count) {
    var result = '';
    while (count > 0) {
        if (count & 1)
            result += string;

        if (count >>= 1)
            string += string;
    }
    return result;
};

var trimBeginRegexp = /^\s\s*/;
var trimEndRegexp = /\s\s*$/;

exports.stringTrimLeft = function (string) {
    return string.replace(trimBeginRegexp, '');
};

exports.stringTrimRight = function (string) {
    return string.replace(trimEndRegexp, '');
};

exports.copyObject = function(obj) {
    var copy = {};
    for (var key in obj) {
        copy[key] = obj[key];
    }
    return copy;
};

exports.copyArray = function(array){
    var copy = [];
    for (var i=0, l=array.length; i<l; i++) {
        if (array[i] && typeof array[i] == "object")
            copy[i] = this.copyObject( array[i] );
        else
            copy[i] = array[i];
    }
    return copy;
};

exports.deepCopy = function (obj) {
    if (typeof obj !== "object" || !obj)
        return obj;
    var cons = obj.constructor;
    if (cons === RegExp)
        return obj;

    var copy = cons();
    if (Array.isArray(obj)) {
        for (var key = 0; key < obj.length; key++) {
            if (typeof obj[key] === "object") {
                copy[key] = exports.deepCopy(obj[key]);
            } else {
                copy[key] = obj[key];
            }
        }
    } else {
        for (var key in obj) {
            if (typeof obj[key] === "object") {
                copy[key] = exports.deepCopy(obj[key]);
            } else {
                copy[key] = obj[key];
            }
        }
    }

    return copy;
};

exports.arrayToMap = function(arr) {
    var map = {};
    for (var i=0; i<arr.length; i++) {
        map[arr[i]] = 1;
    }
    return map;

};

exports.createMap = function(props) {
    var map = Object.create(null);
    for (var i in props) {
        map[i] = props[i];
    }
    return map;
};

/*
 * splice out of 'array' anything that === 'value'
 */
exports.arrayRemove = function(array, value) {
  for (var i = 0; i <= array.length; i++) {
    if (value === array[i]) {
      array.splice(i, 1);
    }
  }
};

exports.escapeRegExp = function(str) {
    return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
};

exports.escapeHTML = function(str) {
    return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<");
};

exports.getMatchOffsets = function(string, regExp) {
    var matches = [];

    string.replace(regExp, function(str) {
        matches.push({
            offset: arguments[arguments.length-2],
            length: str.length
        });
    });

    return matches;
};

/* deprecated */
exports.deferredCall = function(fcn) {
    var timer = null;
    var callback = function() {
        timer = null;
        fcn();
    };

    var deferred = function(timeout) {
        deferred.cancel();
        timer = setTimeout(callback, timeout || 0);
        return deferred;
    };

    deferred.schedule = deferred;

    deferred.call = function() {
        this.cancel();
        fcn();
        return deferred;
    };

    deferred.cancel = function() {
        clearTimeout(timer);
        timer = null;
        return deferred;
    };

    deferred.isPending = function() {
        return timer;
    };

    return deferred;
};

exports.delayedCall = function(fcn, defaultTimeout) {
    var timer = null;
    var callback = function() {
        timer = null;
        fcn();
    };

    var _self = function(timeout) {
        if (timer == null)
            timer = setTimeout(callback, timeout || defaultTimeout);
    };

    _self.delay = function(timeout) {
        timer && clearTimeout(timer);
        timer = setTimeout(callback, timeout || defaultTimeout);
    };
    _self.schedule = _self;

    _self.call = function() {
        this.cancel();
        fcn();
    };

    _self.cancel = function() {
        timer && clearTimeout(timer);
        timer = null;
    };

    _self.isPending = function() {
        return timer;
    };

    return _self;
};
});
<|endoftext|>


<commit_before>/*
 * tex.js
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *
 */
define(function(require, exports, module) {
"use strict";

var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var TexHighlightRules = require("./tex_highlight_rules").TexHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;

var Mode = function(suppressHighlighting) {
	if (suppressHighlighting)
    	this.HighlightRules = TextHighlightRules;
	else
    	this.HighlightRules = TexHighlightRules;
    this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, TextMode);

(function() {
   this.getNextLineIndent = function(state, line, tab) {
      return this.$getIndent(line);
   };

   this.allowAutoInsert = function() {
      return false;
   };
    this.$id = "ace/mode/tex";
}).call(Mode.prototype);

exports.Mode = Mode;
});
<commit_msg>Added toggle comment support for TeX #2698<commit_after>/*
 * tex.js
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *
 */
define(function(require, exports, module) {
"use strict";

var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var TexHighlightRules = require("./tex_highlight_rules").TexHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;

var Mode = function(suppressHighlighting) {
	if (suppressHighlighting)
    	this.HighlightRules = TextHighlightRules;
	else
    	this.HighlightRules = TexHighlightRules;
    this.$outdent = new MatchingBraceOutdent();
};
oop.inherits(Mode, TextMode);

(function() {
   this.lineCommentStart = "%";
   this.getNextLineIndent = function(state, line, tab) {
      return this.$getIndent(line);
   };

   this.allowAutoInsert = function() {
      return false;
   };
    this.$id = "ace/mode/tex";
}).call(Mode.prototype);

exports.Mode = Mode;
});
<|endoftext|>


<commit_before>module.exports = function(grasshopper) {
    "use strict";
    var base = {},
        CONTENT_TYPE = 'application/json';

    base.AVAILABLE_PRIVILEGES = {
        ADMIN: 0,
        EDITOR: 1,
        AUTHOR: 2,
        READER: 3,
        NONE: 4
    };

    /*
     Explanation of status codes used by the Grasshoper API:
     =============================================
     200: The request has succeeded.
     400: The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.
     401: The request requires user authentication. The response MUST include a WWW-Authenticate header field containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field. If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.
     403: The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.
     404: The server has not found anything matching the Request-URI. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable.
     408: The client did not produce a request within the time that the server was prepared to wait.
     500: The server encountered an unexpected condition which prevented it from fulfilling the request.
     502: The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.
     503: The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
     */
    base.STATUS_CODES = {
        SUCCESS: 200,
        BAD_REQUEST: 400,
        UNAUTHORIZED: 401,
        FORBIDDEN: 403,
        NOT_FOUND: 404,
        REQUEST_TIMEOUT: 408,
        SERVER_ERROR: 500,
        BAD_GATEWAY: 502,
        SERVICE_UNAVAILABLE: 503
    };

    base.getStatusMessage = function(code){
        var message = "";

        switch(code){
            case base.STATUS_CODES.SUCCESS:
                message = "Success";
                break;
            case base.STATUS_CODES.BAD_REQUEST:
                message = "Bad Request";
                break;
            case base.STATUS_CODES.UNAUTHORIZED:
                message = "Unauthorized";
                break;
            case base.STATUS_CODES.NOT_FOUND:
                message = "Not found";
                break;
            case base.STATUS_CODES.REQUEST_TIMEOUT:
                message = "Request Timeout";
                break;
            case base.STATUS_CODES.SERVER_ERROR:
                message = "Server Error";
                break;
            case base.STATUS_CODES.BAD_GATEWAY:
                message = "Bad Gateway";
                break;
            case base.STATUS_CODES.SERVICE_UNAVAILABLE:
                message = "Service Unavailable";
                break;
            default:
                break;
        }

        return message;
    };

    base.getAuthenticationHeader = function(req, callback){
        var authHeader = req.headers['authorization'];

        if(authHeader){
            var token=authHeader.split(/\s+/).pop()||'',
                auth=new Buffer(token, 'base64').toString();

            callback(null, auth);
        }
        else {
            callback(new Error("Authorization credentials not provided."));
        }
    };

    base.authenticateRequest = function(req, res, callback){
        base.getAuthenticationHeader(req, function(err, hdr){
            if(err){
                base.write(base.STATUS_CODES.UNAUTHORIZED, base.getStatusMessage(base.STATUS_CODES.UNAUTHORIZED), res);
            }
            else {
                //Use the token to look up user details and send back permissions to the caller.
                var token = grasshopper.tokens.validate(hdr);

                if(!token.isValid){
                    base.write(base.STATUS_CODES.UNAUTHORIZED, base.getStatusMessage(base.STATUS_CODES.UNAUTHORIZED), res);
                }
                else {
                    callback(null, token);
                }
            }
        });
    };

    base.userCan = function(userObj, minPriviledge){
        return true;
    };

    /**
     *
     * @param responseCode
     * @param responseData
     * @param res
     */
    base.write = function(responseCode, responseData, res){
        res.writeHead(responseCode, { 'Content-Type': CONTENT_TYPE });
        res.write(responseData);
        res.end();
    };

    return base;
};
<commit_msg>Added documentation<commit_after>module.exports = function(grasshopper) {
    "use strict";
    var base = {},
        CONTENT_TYPE = 'application/json';

    /**
     * Every api method and call can be protected by setting a minimum permission level on it. These lookup values
     * are used to ensure readers don't update user accounts, etc.
     *
     * @type {{ADMIN: number, EDITOR: number, AUTHOR: number, READER: number, NONE: number}}
     */
    base.AVAILABLE_PRIVILEGES = {
        ADMIN: 0,
        EDITOR: 1,
        AUTHOR: 2,
        READER: 3,
        NONE: 4
    };

    /*
     Explanation of status codes used by the Grasshoper API:
     =============================================
     200: The request has succeeded.
     400: The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.
     401: The request requires user authentication. The response MUST include a WWW-Authenticate header field containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field. If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.
     403: The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.
     404: The server has not found anything matching the Request-URI. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable.
     408: The client did not produce a request within the time that the server was prepared to wait.
     500: The server encountered an unexpected condition which prevented it from fulfilling the request.
     502: The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.
     503: The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
     */
    base.STATUS_CODES = {
        SUCCESS: 200,
        BAD_REQUEST: 400,
        UNAUTHORIZED: 401,
        FORBIDDEN: 403,
        NOT_FOUND: 404,
        REQUEST_TIMEOUT: 408,
        SERVER_ERROR: 500,
        BAD_GATEWAY: 502,
        SERVICE_UNAVAILABLE: 503
    };

    /**
     * Simple lookup method to return back the status strings that relate to an error code.
     * @param code
     * @returns {string}
     */
    base.getStatusMessage = function(code){
        var message = "";

        switch(code){
            case base.STATUS_CODES.SUCCESS:
                message = "Success";
                break;
            case base.STATUS_CODES.BAD_REQUEST:
                message = "Bad Request";
                break;
            case base.STATUS_CODES.UNAUTHORIZED:
                message = "Unauthorized";
                break;
            case base.STATUS_CODES.NOT_FOUND:
                message = "Not found";
                break;
            case base.STATUS_CODES.REQUEST_TIMEOUT:
                message = "Request Timeout";
                break;
            case base.STATUS_CODES.SERVER_ERROR:
                message = "Server Error";
                break;
            case base.STATUS_CODES.BAD_GATEWAY:
                message = "Bad Gateway";
                break;
            case base.STATUS_CODES.SERVICE_UNAVAILABLE:
                message = "Service Unavailable";
                break;
            default:
                break;
        }

        return message;
    };

    /**
     * Method will inspect a request that comes into the api and pull out the request authentication hearder if it
     * exists. If it does not exist then an error should be returned.
     * @param req
     * @param callback
     */
    base.getAuthenticationHeader = function(req, callback){
        var authHeader = req.headers['authorization'];

        if(authHeader){
            var token=authHeader.split(/\s+/).pop()||'',
                auth=new Buffer(token, 'base64').toString();

            callback(null, auth);
        }
        else {
            callback(new Error("Authorization credentials not provided."));
        }
    };

    /**
     * Method will evaluate the authentication header and see if it matches a token that is already being used in the
     * system. If it does not exist or the token is no longer valid it will send back send back a 401.
     * @param req
     * @param res
     * @param callback
     */
    base.authenticateRequest = function(req, res, callback){
        base.getAuthenticationHeader(req, function(err, hdr){
            if(err){
                base.write(base.STATUS_CODES.UNAUTHORIZED, base.getStatusMessage(base.STATUS_CODES.UNAUTHORIZED), res);
            }
            else {
                //Use the token to look up user details and send back permissions to the caller.
                var token = grasshopper.tokens.validate(hdr);

                if(!token.isValid){
                    base.write(base.STATUS_CODES.UNAUTHORIZED, base.getStatusMessage(base.STATUS_CODES.UNAUTHORIZED), res);
                }
                else {
                    callback(null, token);
                }
            }
        });
    };

    /**
     * Method should evaluate a current user's privileges and determine if the user can do something in the system.
     * @param userObj
     * @param minPriviledge
     * @returns {boolean}
     */
    base.userCan = function(userObj, minPriviledge){
        return true;
    };

    /**
     * Utility method that will write the response to the client.
     * @param responseCode HTTP status code
     * @param responseData JSON object response.
     * @param res
     */
    base.write = function(responseCode, responseData, res){
        res.writeHead(responseCode, { 'Content-Type': CONTENT_TYPE });
        res.write(responseData);
        res.end();
    };

    return base;
};
<|endoftext|>


<commit_before>// Display modal text.
// Menu([
//   [`CommandLabel1`, ()=> {
//     //Handler1
//   }],
//   [`CommandLabel2`, ()=> {
//     //Handler2
//   }]
// ]);

import { createRectangle } from 'pura/geometry/tuple';
import { ctx, fillText } from 'pura/canvas/tuple';
import { drawBox, stroke, textSize, lineHeight, white } from 'style';
import { inputs } from 'controls';
import { canvas, viewHeight, viewWidth } from 'dom';
import { pointRight } from 'emoji';

const Menu = (commands)=> {
  // Menu dimensions.
  let menuWidth = viewWidth/3;
  const menuHeight = lineHeight*commands.length + stroke*2;
  let menuX = viewWidth/2 - menuWidth/2 - stroke/2;
  const menuY = viewHeight/2 - menuHeight/2;

  let activeCommandIndex = 0;

  const children = [];

  // Create command render objects.
  for(const [index, [text, handler]] of commands.entries()) {
    const label = text.toUpperCase();

    const txtMetrics = ctx.measureText(label);
    menuWidth = Math.max(menuWidth, txtMetrics.width + stroke*4);

    children.push(
      {
        geometry: createRectangle(
          [
            -menuWidth/2 + stroke*2,
            index*lineHeight - stroke*2
          ], 0, menuWidth, lineHeight
        ),

        render() {
          fillText({ style: white }, [0, 0], label);

          // Render pointer hand.
          if(activeCommandIndex === index) {
            fillText({}, [-13, 0], pointRight);
          }
        }
      }
    );
  }

  // Reposition menu.
  menuX = viewWidth/2 - menuWidth/2 - stroke/2;

  // Render menu.
  return {
    geometry: createRectangle([menuX, menuY], 0, menuWidth, menuHeight),

    children,

    render() {
      drawBox([0, 0], menuWidth, 0, menuHeight);

      // Keyboard controls.
      if(inputs.down) {
        if(activeCommandIndex < commands.length - 1) {
          activeCommandIndex++;
        }
      } else if(inputs.up) {
        if(activeCommandIndex > 0) {
          activeCommandIndex--;
        }
      }
      if(inputs.space || inputs.return) {
        commands[activeCommandIndex][1]();
      }
    }
  };
};

export default Menu;
<commit_msg>Use new interactions.<commit_after>// Display modal text.
// Menu([
//   [`CommandLabel1`, ()=> {
//     //Handler1
//   }],
//   [`CommandLabel2`, ()=> {
//     //Handler2
//   }]
// ]);

import { createRectangle } from 'pura/geometry/tuple';
import { ctx, fillText } from 'pura/canvas/tuple';
import { drawBox, stroke, lineHeight, white } from 'style';
import { inputs } from 'controls';
import { viewHeight, viewWidth } from 'dom';
import { pointRight } from 'emoji';

const Menu = (commands)=> {
  // Menu dimensions.
  let menuWidth = viewWidth/3;
  const menuHeight = lineHeight*commands.length + stroke*2;
  let menuX = viewWidth/2 - menuWidth/2 - stroke/2;
  const menuY = viewHeight/2 - menuHeight/2;

  let activeCommandIndex = 0;

  const children = [];

  // Create command render objects.
  for(const [index, [text, handler]] of commands.entries()) {
    const label = text.toUpperCase();

    const txtMetrics = ctx.measureText(label);
    menuWidth = Math.max(menuWidth, txtMetrics.width + stroke*4);

    children.push(
      {
        geometry: createRectangle(
          [
            -menuWidth/2 + stroke*2,
            index*lineHeight - stroke*2
          ], 0, menuWidth, lineHeight
        ),

        render() {
          fillText({ style: white }, [0, 0], label);

          // Render pointer hand.
          if(activeCommandIndex === index) {
            fillText({}, [-13, 0], pointRight);
          }
        },

        interact: {
          onMouseDown: handler,
          onMouseMove() {
            activeCommandIndex = index;
          }
        }
      }
    );
  }

  // Reposition menu.
  menuX = viewWidth/2 - menuWidth/2 - stroke/2;

  // Render menu.
  return {
    geometry: createRectangle([menuX, menuY], 0, menuWidth, menuHeight),

    children,

    render() {
      drawBox([0, 0], menuWidth, 0, menuHeight);

      // Keyboard controls.
      if(inputs.down === 1) {
        if(activeCommandIndex < commands.length - 1) {
          activeCommandIndex++;
        }
      } else if(inputs.up === 1) {
        if(activeCommandIndex > 0) {
          activeCommandIndex--;
        }
      }
      if(inputs.space === 1 || inputs.return === 1) {
        commands[activeCommandIndex][1]();
      }
    }
  };
};

export default Menu;
<|endoftext|>


<commit_before>var arrayDiff = require('arraydiff');

function extend(target) {
    for (var i = 1; i < arguments.length; i += 1) {
        var source = arguments[i];
        Object.keys(source).forEach(function (key) {
            target[key] = source[key];
        });
    }
    return target;
}

module.exports = function arrayChanges(actual, expected, equal, similar) {
    var mutatedArray = new Array(actual.length);

    for (var k = 0; k < actual.length; k += 1) {
        mutatedArray[k] = {
            type: 'similar',
            value: actual[k]
        };
    }

    if (mutatedArray.length > 0) {
        mutatedArray[mutatedArray.length - 1].last = true;
    }

    similar = similar || function (a, b) {
        return false;
    };

    var itemsDiff = arrayDiff([].concat(actual), [].concat(expected), function (a, b) {
        return equal(a, b) || similar(a, b);
    });

    var removeTable = [];
    function offsetIndex(index) {
        return index + (removeTable[index - 1] || 0);
    }

    var removes = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'remove';
    });

    var removesByIndex = {};
    var removedItems = 0;
    removes.forEach(function (diffItem) {
        var removeIndex = removedItems + diffItem.index;
        mutatedArray.slice(removeIndex, diffItem.howMany + removeIndex).forEach(function (v) {
            v.type = 'remove';
        });
        removedItems += diffItem.howMany;
        removesByIndex[diffItem.index] = removedItems;
    });

    function updateRemoveTable() {
        removedItems = 0;
        Array.prototype.forEach.call(actual, function (_, index) {
            removedItems += removesByIndex[index] || 0;
            removeTable[index] = removedItems;
        });
    }

    updateRemoveTable();

    var moves = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'move';
    });

    var movedItems = 0;
    moves.forEach(function (diffItem) {
        var moveFromIndex = offsetIndex(diffItem.from);
        var removed = mutatedArray.slice(moveFromIndex, diffItem.howMany + moveFromIndex);
        var added = removed.map(function (v) {
            return extend({}, v, { last: false, type: 'insert' });
        });
        removed.forEach(function (v) {
            v.type = 'remove';
        });
        Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.to), 0].concat(added));
        movedItems += diffItem.howMany;
        removesByIndex[diffItem.from] = movedItems;
        updateRemoveTable();
    });

    var inserts = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'insert';
    });

    inserts.forEach(function (diffItem) {
        var added = new Array(diffItem.values.length);
        for (var i = 0 ; i < diffItem.values.length ; i += 1) {
            added[i] = {
                type: 'insert',
                value: diffItem.values[i]
            };
        }
        Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.index), 0].concat(added));
    });

    var offset = 0;
    mutatedArray.forEach(function (diffItem, index) {
        var type = diffItem.type;
        if (type === 'remove') {
            offset -= 1;
        } else if (type === 'similar') {
            diffItem.expected = expected[offset + index];
        }
    });

    var conflicts = mutatedArray.reduce(function (conflicts, item) {
        return item.type === 'similar' ? conflicts : conflicts + 1;
    }, 0);

    for (var i = 0, c = 0; i < Math.max(actual.length, expected.length) &&  c <= conflicts; i += 1) {
        var expectedType = typeof expected[i];
        var actualType = typeof actual[i];

        if (
            actualType !== expectedType ||
                ((actualType === 'object' || actualType === 'string') && !similar(actual[i], expected[i])) ||
                (actualType !== 'object' && actualType !== 'string' && !equal(actual[i], expected[i]))
        ) {
            c += 1;
        }
    }

    if (c <= conflicts) {
        mutatedArray = [];
        var j;
        for (j = 0; j < Math.min(actual.length, expected.length); j += 1) {
            mutatedArray.push({
                type: 'similar',
                value: actual[j],
                expected: expected[j]
            });
        }

        if (actual.length < expected.length) {
            for (; j < Math.max(actual.length, expected.length); j += 1) {
                mutatedArray.push({
                    type: 'insert',
                    value: expected[j]
                });
            }
        } else {
            for (; j < Math.max(actual.length, expected.length); j += 1) {
                mutatedArray.push({
                    type: 'remove',
                    value: actual[j]
                });
            }
        }
        if (mutatedArray.length > 0) {
            mutatedArray[mutatedArray.length - 1].last = true;
        }
    }

    mutatedArray.forEach(function (diffItem) {
        if (diffItem.type === 'similar' && equal(diffItem.value, diffItem.expected)) {
            diffItem.type = 'equal';
        }
    });

    return mutatedArray;
};
<commit_msg>Removed unecessary type checking<commit_after>var arrayDiff = require('arraydiff');

function extend(target) {
    for (var i = 1; i < arguments.length; i += 1) {
        var source = arguments[i];
        Object.keys(source).forEach(function (key) {
            target[key] = source[key];
        });
    }
    return target;
}

module.exports = function arrayChanges(actual, expected, equal, similar) {
    var mutatedArray = new Array(actual.length);

    for (var k = 0; k < actual.length; k += 1) {
        mutatedArray[k] = {
            type: 'similar',
            value: actual[k]
        };
    }

    if (mutatedArray.length > 0) {
        mutatedArray[mutatedArray.length - 1].last = true;
    }

    similar = similar || function (a, b) {
        return false;
    };

    var itemsDiff = arrayDiff([].concat(actual), [].concat(expected), function (a, b) {
        return equal(a, b) || similar(a, b);
    });

    var removeTable = [];
    function offsetIndex(index) {
        return index + (removeTable[index - 1] || 0);
    }

    var removes = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'remove';
    });

    var removesByIndex = {};
    var removedItems = 0;
    removes.forEach(function (diffItem) {
        var removeIndex = removedItems + diffItem.index;
        mutatedArray.slice(removeIndex, diffItem.howMany + removeIndex).forEach(function (v) {
            v.type = 'remove';
        });
        removedItems += diffItem.howMany;
        removesByIndex[diffItem.index] = removedItems;
    });

    function updateRemoveTable() {
        removedItems = 0;
        Array.prototype.forEach.call(actual, function (_, index) {
            removedItems += removesByIndex[index] || 0;
            removeTable[index] = removedItems;
        });
    }

    updateRemoveTable();

    var moves = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'move';
    });

    var movedItems = 0;
    moves.forEach(function (diffItem) {
        var moveFromIndex = offsetIndex(diffItem.from);
        var removed = mutatedArray.slice(moveFromIndex, diffItem.howMany + moveFromIndex);
        var added = removed.map(function (v) {
            return extend({}, v, { last: false, type: 'insert' });
        });
        removed.forEach(function (v) {
            v.type = 'remove';
        });
        Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.to), 0].concat(added));
        movedItems += diffItem.howMany;
        removesByIndex[diffItem.from] = movedItems;
        updateRemoveTable();
    });

    var inserts = itemsDiff.filter(function (diffItem) {
        return diffItem.type === 'insert';
    });

    inserts.forEach(function (diffItem) {
        var added = new Array(diffItem.values.length);
        for (var i = 0 ; i < diffItem.values.length ; i += 1) {
            added[i] = {
                type: 'insert',
                value: diffItem.values[i]
            };
        }
        Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.index), 0].concat(added));
    });

    var offset = 0;
    mutatedArray.forEach(function (diffItem, index) {
        var type = diffItem.type;
        if (type === 'remove') {
            offset -= 1;
        } else if (type === 'similar') {
            diffItem.expected = expected[offset + index];
        }
    });

    var conflicts = mutatedArray.reduce(function (conflicts, item) {
        return item.type === 'similar' ? conflicts : conflicts + 1;
    }, 0);

    for (var i = 0, c = 0; i < Math.max(actual.length, expected.length) &&  c <= conflicts; i += 1) {
        if (
            !equal(actual[i], expected[i]) && !similar(actual[i], expected[i])
        ) {
            c += 1;
        }
    }

    if (c <= conflicts) {
        mutatedArray = [];
        var j;
        for (j = 0; j < Math.min(actual.length, expected.length); j += 1) {
            mutatedArray.push({
                type: 'similar',
                value: actual[j],
                expected: expected[j]
            });
        }

        if (actual.length < expected.length) {
            for (; j < Math.max(actual.length, expected.length); j += 1) {
                mutatedArray.push({
                    type: 'insert',
                    value: expected[j]
                });
            }
        } else {
            for (; j < Math.max(actual.length, expected.length); j += 1) {
                mutatedArray.push({
                    type: 'remove',
                    value: actual[j]
                });
            }
        }
        if (mutatedArray.length > 0) {
            mutatedArray[mutatedArray.length - 1].last = true;
        }
    }

    mutatedArray.forEach(function (diffItem) {
        if (diffItem.type === 'similar' && equal(diffItem.value, diffItem.expected)) {
            diffItem.type = 'equal';
        }
    });

    return mutatedArray;
};
<|endoftext|>


<commit_before>import {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLEnumType,
  GraphQLUnionType,
  GraphQLList,
  getNullableType,
  getNamedType,
} from 'graphql/type';
import { graphql } from 'graphql';
import uuid from 'node-uuid';
import { forEachField, buildSchemaFromTypeDefinitions } from './schemaGenerator';

// This function wraps addMockFunctionsToSchema for more convenience
function mockServer(schema, mocks = {}, preserveResolvers = false) {
  let mySchema = schema;
  if (!(schema instanceof GraphQLSchema)) {
    // TODO: provide useful error messages here if this fails
    mySchema = buildSchemaFromTypeDefinitions(schema);
  }
  addMockFunctionsToSchema({ schema: mySchema, mocks, preserveResolvers });

  return { query: (query, vars) => graphql(mySchema, query, {}, {}, vars) };
}

// TODO allow providing a seed such that lengths of list could be deterministic
// this could be done by using casual to get a random list length if the casual
// object is global.
function addMockFunctionsToSchema({ schema, mocks = {}, preserveResolvers = false } = {}) {
  function isObject(thing) {
    return thing === Object(thing) && !Array.isArray(thing);
  }
  if (!schema) {
    // XXX should we check that schema is an instance of GraphQLSchema?
    throw new Error('Must provide schema to mock');
  }
  if (!isObject(mocks)) {
    throw new Error('mocks must be of type Object');
  }

  // use Map internally, because that API is nicer.
  const mockFunctionMap = new Map();
  Object.keys(mocks).forEach((typeName) => {
    mockFunctionMap.set(typeName, mocks[typeName]);
  });

  mockFunctionMap.forEach((mockFunction, mockTypeName) => {
    if (typeof mockFunction !== 'function') {
      throw new Error(`mockFunctionMap[${mockTypeName}] must be a function`);
    }
  });

  const defaultMockMap = new Map();
  defaultMockMap.set('Int', () => Math.round(Math.random() * 200) - 100);
  defaultMockMap.set('Float', () => Math.random() * 200 - 100);
  defaultMockMap.set('String', () => 'Hello World');
  defaultMockMap.set('Boolean', () => Math.random() > 0.5);
  defaultMockMap.set('ID', () => uuid.v4());

  function mergeObjects(a, b) {
    return Object.assign(a, b);
  }

  // returns a random element from that ary
  function getRandomElement(ary) {
    const sample = Math.floor(Math.random() * ary.length);
    return ary[sample];
  }

  // takes either an object or a (possibly nested) array
  // and completes the customMock object with any fields
  // defined on genericMock
  // only merges objects or arrays. Scalars are returned as is
  function mergeMocks(genericMockFunction, customMock) {
    if (Array.isArray(customMock)) {
      return customMock.map((el) => mergeMocks(genericMockFunction, el));
    }
    if (isObject(customMock)) {
      return mergeObjects(genericMockFunction(), customMock);
    }
    return customMock;
  }

  const mockType = function mockType(type, typeName, fieldName) {
    // order of precendence for mocking:
    // 1. if the object passed in already has fieldName, just use that
    // --> if it's a function, that becomes your resolver
    // --> if it's a value, the mock resolver will return that
    // 2. if the nullableType is a list, recurse
    // 2. if there's a mock defined for this typeName, that will be used
    // 3. if there's no mock defined, use the default mocks for this type
    return (o, a, c, r) => {
      // nullability doesn't matter for the purpose of mocking.
      const fieldType = getNullableType(type);
      const namedFieldType = getNamedType(fieldType);

      if (o && typeof o[fieldName] !== 'undefined') {
        let result;
        // if we're here, the field is already defined
        if (typeof o[fieldName] === 'function') {
          result = o[fieldName](o, a, c, r);
          if (result instanceof MockList) {
            result = result.mock(o, a, c, r, fieldType, mockType);
          }
        } else {
          result = o[fieldName];
        }

        // Now we merge the result with the default mock for this type.
        // This allows overriding defaults while writing very little code.
        if (mockFunctionMap.has(namedFieldType.name)) {
          result = mergeMocks(
            mockFunctionMap.get(namedFieldType.name).bind(null, o, a, c, r), result
          );
        }
        return result;
      }

      if (fieldType instanceof GraphQLList) {
        return [mockType(fieldType.ofType)(o, a, c, r), mockType(fieldType.ofType)(o, a, c, r)];
      }
      if (mockFunctionMap.has(fieldType.name)) {
        // the object passed doesn't have this field, so we apply the default mock
        return mockFunctionMap.get(fieldType.name)(o, a, c, r);
      }
      if (fieldType instanceof GraphQLObjectType) {
        // objects don't return actual data, we only need to mock scalars!
        return {};
      }
      // TODO mocking Interface and Union types will require determining the
      // resolve type before passing it on.
      // XXX we recommend a generic way for resolve type here, which is defining
      // typename on the object.
      if (fieldType instanceof GraphQLUnionType) {
        // TODO: if a union type doesn't implement resolveType, we could overwrite
        // it with a default that works with what's below.
        return { typename: getRandomElement(fieldType.getTypes()) };
      }
      if (fieldType instanceof GraphQLEnumType) {
        return getRandomElement(fieldType.getValues()).value;
      }
      if (defaultMockMap.has(fieldType.name)) {
        return defaultMockMap.get(fieldType.name)(o, a, c, r);
      }
      // if we get to here, we don't have a value, and we don't have a mock for this type,
      // we could return undefined, but that would be hard to debug, so we throw instead.
      throw new Error(`No mock defined for type "${fieldType.name}"`);
    };
  };

  forEachField(schema, (field, typeName, fieldName) => {
    if (preserveResolvers && field.resolve) {
      return;
    }

    // we have to handle the root mutation and root query types differently,
    // because no resolver is called at the root.
    const isOnQueryType = typeName === (schema.getQueryType() || {}).name;
    const isOnMutationType = typeName === (schema.getMutationType() || {}).name;
    if (isOnQueryType || isOnMutationType) {
      if (mockFunctionMap.has(typeName)) {
        const rootMock = mockFunctionMap.get(typeName);
        if (rootMock()[fieldName]) {
          // TODO: assert that it's a function
          // eslint-disable-next-line no-param-reassign
          field.resolve = (o, a, c, r) => {
            const u = o || {}; // TODO: should we clone instead?
            u[fieldName] = rootMock()[fieldName];
            // XXX this is a bit of a hack to still use mockType, which
            // lets you mock lists etc. as well
            // otherwise we could just set field.resolve to rootMock()[fieldName]
            // it's like pretending there was a resolve function that ran before
            // the root resolve function.
            return mockType(field.type, typeName, fieldName)(u, a, c, r);
          };
          return;
        }
      }
    }
    // eslint-disable-next-line no-param-reassign
    field.resolve = mockType(field.type, typeName, fieldName);
  });
}

class MockList {
  // wrappedFunction can return another MockList or a value
  constructor(len, wrappedFunction) {
    this.len = len;
    if (typeof wrappedFunction !== 'undefined') {
      if (typeof wrappedFunction !== 'function') {
        throw new Error('Second argument to MockList must be a function or undefined');
      }
      this.wrappedFunction = wrappedFunction;
    }
  }

  mock(o, a, c, r, fieldType, mockTypeFunc) {
    function randint(low, high) {
      return Math.floor(Math.random() * (high - low + 1) + low);
    }
    let arr;
    if (Array.isArray(this.len)) {
      arr = new Array(randint(this.len[0], this.len[1]));
    } else {
      arr = new Array(this.len);
    }
    for (let i = 0; i < arr.length; i++) {
      if (typeof this.wrappedFunction === 'function') {
        const res = this.wrappedFunction(o, a, c, r);
        if (res instanceof MockList) {
          arr[i] = res.mock(o, a, c, r, getNullableType(fieldType.ofType), mockTypeFunc);
        } else {
          arr[i] = res;
        }
      } else {
        arr[i] = mockTypeFunc(fieldType.ofType)(o, a, c, r);
      }
    }
    return arr;
  }
}

export {
  addMockFunctionsToSchema,
  MockList,
  mockServer,
};
<commit_msg>give pass through args real names<commit_after>import {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLEnumType,
  GraphQLUnionType,
  GraphQLList,
  getNullableType,
  getNamedType,
} from 'graphql/type';
import { graphql } from 'graphql';
import uuid from 'node-uuid';
import { forEachField, buildSchemaFromTypeDefinitions } from './schemaGenerator';

// This function wraps addMockFunctionsToSchema for more convenience
function mockServer(schema, mocks = {}, preserveResolvers = false) {
  let mySchema = schema;
  if (!(schema instanceof GraphQLSchema)) {
    // TODO: provide useful error messages here if this fails
    mySchema = buildSchemaFromTypeDefinitions(schema);
  }
  addMockFunctionsToSchema({ schema: mySchema, mocks, preserveResolvers });

  return { query: (query, vars) => graphql(mySchema, query, {}, {}, vars) };
}

// TODO allow providing a seed such that lengths of list could be deterministic
// this could be done by using casual to get a random list length if the casual
// object is global.
function addMockFunctionsToSchema({ schema, mocks = {}, preserveResolvers = false } = {}) {
  function isObject(thing) {
    return thing === Object(thing) && !Array.isArray(thing);
  }
  if (!schema) {
    // XXX should we check that schema is an instance of GraphQLSchema?
    throw new Error('Must provide schema to mock');
  }
  if (!isObject(mocks)) {
    throw new Error('mocks must be of type Object');
  }

  // use Map internally, because that API is nicer.
  const mockFunctionMap = new Map();
  Object.keys(mocks).forEach((typeName) => {
    mockFunctionMap.set(typeName, mocks[typeName]);
  });

  mockFunctionMap.forEach((mockFunction, mockTypeName) => {
    if (typeof mockFunction !== 'function') {
      throw new Error(`mockFunctionMap[${mockTypeName}] must be a function`);
    }
  });

  const defaultMockMap = new Map();
  defaultMockMap.set('Int', () => Math.round(Math.random() * 200) - 100);
  defaultMockMap.set('Float', () => Math.random() * 200 - 100);
  defaultMockMap.set('String', () => 'Hello World');
  defaultMockMap.set('Boolean', () => Math.random() > 0.5);
  defaultMockMap.set('ID', () => uuid.v4());

  function mergeObjects(a, b) {
    return Object.assign(a, b);
  }

  // returns a random element from that ary
  function getRandomElement(ary) {
    const sample = Math.floor(Math.random() * ary.length);
    return ary[sample];
  }

  // takes either an object or a (possibly nested) array
  // and completes the customMock object with any fields
  // defined on genericMock
  // only merges objects or arrays. Scalars are returned as is
  function mergeMocks(genericMockFunction, customMock) {
    if (Array.isArray(customMock)) {
      return customMock.map((el) => mergeMocks(genericMockFunction, el));
    }
    if (isObject(customMock)) {
      return mergeObjects(genericMockFunction(), customMock);
    }
    return customMock;
  }

  const mockType = function mockType(type, typeName, fieldName) {
    // order of precendence for mocking:
    // 1. if the object passed in already has fieldName, just use that
    // --> if it's a function, that becomes your resolver
    // --> if it's a value, the mock resolver will return that
    // 2. if the nullableType is a list, recurse
    // 2. if there's a mock defined for this typeName, that will be used
    // 3. if there's no mock defined, use the default mocks for this type
    return (...args) => {
      const [root, queryArgs, context, info] = args;

      // nullability doesn't matter for the purpose of mocking.
      const fieldType = getNullableType(type);
      const namedFieldType = getNamedType(fieldType);

      if (root && typeof root[fieldName] !== 'undefined') {
        let result;
        // if we're here, the field is already defined
        if (typeof root[fieldName] === 'function') {
          result = root[fieldName](...args);
          if (result instanceof MockList) {
            result = result.mock(...args, fieldType, mockType);
          }
        } else {
          result = root[fieldName];
        }

        // Now we merge the result with the default mock for this type.
        // This allows overriding defaults while writing very little code.
        if (mockFunctionMap.has(namedFieldType.name)) {
          result = mergeMocks(
            mockFunctionMap.get(namedFieldType.name).bind(null, ...args), result
          );
        }
        return result;
      }

      if (fieldType instanceof GraphQLList) {
        return [mockType(fieldType.ofType)(...args), mockType(fieldType.ofType)(...args)];
      }
      if (mockFunctionMap.has(fieldType.name)) {
        // the object passed doesn't have this field, so we apply the default mock
        return mockFunctionMap.get(fieldType.name)(...args);
      }
      if (fieldType instanceof GraphQLObjectType) {
        // objects don't return actual data, we only need to mock scalars!
        return {};
      }
      // TODO mocking Interface and Union types will require determining the
      // resolve type before passing it on.
      // XXX we recommend a generic way for resolve type here, which is defining
      // typename on the object.
      if (fieldType instanceof GraphQLUnionType) {
        // TODO: if a union type doesn't implement resolveType, we could overwrite
        // it with a default that works with what's below.
        return { typename: getRandomElement(fieldType.getTypes()) };
      }
      if (fieldType instanceof GraphQLEnumType) {
        return getRandomElement(fieldType.getValues()).value;
      }
      if (defaultMockMap.has(fieldType.name)) {
        return defaultMockMap.get(fieldType.name)(...args);
      }
      // if we get to here, we don't have a value, and we don't have a mock for this type,
      // we could return undefined, but that would be hard to debug, so we throw instead.
      throw new Error(`No mock defined for type "${fieldType.name}"`);
    };
  };

  forEachField(schema, (field, typeName, fieldName) => {
    if (preserveResolvers && field.resolve) {
      return;
    }

    // we have to handle the root mutation and root query types differently,
    // because no resolver is called at the root.
    const isOnQueryType = typeName === (schema.getQueryType() || {}).name;
    const isOnMutationType = typeName === (schema.getMutationType() || {}).name;
    if (isOnQueryType || isOnMutationType) {
      if (mockFunctionMap.has(typeName)) {
        const rootMock = mockFunctionMap.get(typeName);
        if (rootMock()[fieldName]) {
          // TODO: assert that it's a function
          // eslint-disable-next-line no-param-reassign
          field.resolve = (root, ...rest) => {
            const updatedRoot = root || {}; // TODO: should we clone instead?
            updatedRoot[fieldName] = rootMock()[fieldName];
            // XXX this is a bit of a hack to still use mockType, which
            // lets you mock lists etc. as well
            // otherwise we could just set field.resolve to rootMock()[fieldName]
            // it's like pretending there was a resolve function that ran before
            // the root resolve function.
            return mockType(
              field.type, typeName, fieldName)(updatedRoot, ...rest);
          };
          return;
        }
      }
    }
    // eslint-disable-next-line no-param-reassign
    field.resolve = mockType(field.type, typeName, fieldName);
  });
}

class MockList {
  // wrappedFunction can return another MockList or a value
  constructor(len, wrappedFunction) {
    this.len = len;
    if (typeof wrappedFunction !== 'undefined') {
      if (typeof wrappedFunction !== 'function') {
        throw new Error('Second argument to MockList must be a function or undefined');
      }
      this.wrappedFunction = wrappedFunction;
    }
  }

  mock(root, args, context, info, fieldType, mockTypeFunc) {
    function randint(low, high) {
      return Math.floor(Math.random() * (high - low + 1) + low);
    }
    let arr;
    if (Array.isArray(this.len)) {
      arr = new Array(randint(this.len[0], this.len[1]));
    } else {
      arr = new Array(this.len);
    }
    for (let i = 0; i < arr.length; i++) {
      if (typeof this.wrappedFunction === 'function') {
        const res = this.wrappedFunction(root, args, context, info);
        if (res instanceof MockList) {
          arr[i] = res.mock(root, args, context, info, getNullableType(fieldType.ofType), mockTypeFunc);
        } else {
          arr[i] = res;
        }
      } else {
        arr[i] = mockTypeFunc(fieldType.ofType)(root, args, context, info);
      }
    }
    return arr;
  }
}

export {
  addMockFunctionsToSchema,
  MockList,
  mockServer,
};
<|endoftext|>


<commit_before>var stylize = require('../vows/console').stylize;
var inspect = require('../vows/console').inspect;

require('assert').AssertionError.prototype.toString = function () {
    var that = this,
        source;

    if (this.stack) {
        source = this.stack.match(/([a-zA-Z0-9._-]+\.js)(:\d+):\d+/);
    }

    function parse(str) {
        return str.replace(/{actual}/g,   inspect(that.actual)).
                   replace(/{operator}/g, stylize(that.operator, 'bold')).
                   replace(/{expected}/g, (that.expected instanceof Function)
                                          ? that.expected.name
                                          : inspect(that.expected));
    }

    if (this.message) {
        return stylize(parse(this.message), 'yellow') +
               ((source) ? stylize(' // ' + source[1] + source[2], 'grey') : '');
    } else {
        return stylize([
            this.expected,
            this.operator,
            this.actual
        ].join(' '), 'yellow');
    }
};

<commit_msg>When called outside the vows context where `this.stack` && `source` are undefined it will now no longer crash and burn<commit_after>var stylize = require('../vows/console').stylize;
var inspect = require('../vows/console').inspect;

require('assert').AssertionError.prototype.toString = function () {
    var that = this,
        source;

    if (this.stack) {
        source = this.stack.match(/([a-zA-Z0-9._-]+\.js)(:\d+):\d+/);
    }

    function parse(str) {
        return str.replace(/{actual}/g,   inspect(that.actual)).
                   replace(/{operator}/g, stylize(that.operator, 'bold')).
                   replace(/{expected}/g, (that.expected instanceof Function)
                                          ? that.expected.name
                                          : inspect(that.expected));
    }

    if (this.message) {
        var msg = stylize(parse(this.message), 'yellow');
      	if (source) {
      		  msg += stylize(' // ' + source[1] + source[2], 'grey');
      	}
        return msg;
    } else {
        return stylize([
            this.expected,
            this.operator,
            this.actual
        ].join(' '), 'yellow');
    }
};

<|endoftext|>


<commit_before>'use strict';

var Mock = require('mockjs'),
    fs = require('fs'),
    path = require('path');

function Mock(options){
    this.options = options || {};

}

Mock.prototype.mock(){

}

Mock.prototype.getJsonData = function(url) {
    var pathStr = path.join(process.cwd(), mockConfig.json.path + url + (mockConfig.json.suffix || '.json'));
    if (fs.existsSync(pathStr)) {
        var data = fs.readFileSync(pathStr, 'utf-8'),
            json = JSON.parse(data);
        if (mockConfig.json.wrap) {
            if (json.enable) {
                return JSON.stringify(json[json.value]);
            }
        } else {
            return data;
        }
    }
};

Mock.prototype.getTemplateData = function(url) {
    // body...
};<commit_msg>commit mock.js<commit_after>'use strict';

var MockJs = require('mockjs'),
    fs = require('fs'),
    path = require('path'),
    director = require('director'),
    co = require('co'),
    thunkify = require('thunkify'),
    router = new director.http.Router();

function Mock(options) {
    this.options = options || {};
    this.initRouter();

    this.getData = thunkify(this.getMockData);
}

//init router
Mock.prototype.initRouter = function() {
    var as = this.options.as || '',
        mockConfig = this.options.mockConfig;
    //router action
    if (typeof as == 'string') {
        var suffix = as.split(',')
        for (var i = 0; i < suffix.length; i++) {
            var reg = '/(.*)' + suffix[i],
                me = this;
            router.post(new RegExp(reg), function(url) {
                if (mockConfig) {
                    me.mockTo.call(me, url, this.req, this.res)
                }
            });
        };
    }
}

Mock.prototype.dispatch = function(req, res) {
    return router.dispatch(req, res);
}

Mock.prototype.mockTo = function(url, req, res) {
    var me = this;
    co(function*() {
        for (var i = 0; i < me.options.mockConfig.dataSource.length; i++) {
            var result = yield me.getData(me.options.mockConfig.dataSource[i], url, req, res);
            if (result) {
                break;
            } else if (!result && i == me.options.mockConfig.dataSource.length - 1) {
                //最后一个仍然没有返回数据，那么则返回404
                res.writeHead(404);
                res.end('not found');
            }
        };
        me.options.logger.info('Datasource is ' + me.options.mockConfig.dataSource[i]);
    }).catch(function(err) {
        res.writeHead(404);
        res.end(err.stack);
        me.options.logger.request(req, res, err)
    });
}

Mock.prototype.getMockData = function(type, url, req, res, cb) {
    var result = false;
    switch (type) {
        case 'json':
            var data = this.getJsonData(url);
            if (typeof data != 'undefined') {
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                })
                res.end(data);
                this.options.logger.request(req, res);
                result = true;
            } else {
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                })
                res.end(data);
                this.options.logger.request(req, res);
                result = true;
            }
            break;
        case 'online':
            break;
    }
    cb(null, result);
};

Mock.prototype.getJsonData = function(url) {
    var pathStr = path.join(process.cwd(), this.options.mockConfig.json.path + url + (this.options.mockConfig.json.suffix || '.json'));
    this.options.logger.info('Json data path is ' + pathStr);
    if (fs.existsSync(pathStr)) {
        var data = fs.readFileSync(pathStr, 'utf-8'),
            json = JSON.parse(data);
        if (this.options.mockConfig.json.wrap) {
            if (json.enable) {
                return JSON.stringify(json[json.value]);
            }
        } else {
            return data;
        }
    } else {
        this.options.logger.info("Can't find json data with the path '" + pathStr + "'")
    }
};

Mock.prototype.getTemplateData = function(url) {
    // body...
};

module.exports = Mock;<|endoftext|>


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

  var Utils = {};

  Utils.Type = {
    isObject: function(v) {
      return v instanceof Object;
    },
    isFunction: function(v) {
      return v instanceof Function;
    },
    isArray: function(v) {
      return v instanceof Array;
    }
  };

  Utils.Log = {
    print: function(msg, type) {
      // Mood 开启 debug 时才打印log
      if ( !win.Mood._config.debug ) {
        return;
      }
      // TODO: to be more beautyful
      if ( Utils.Type.isArray(msg) ) {
        msg = '[Mood] ' + msg.join('');
      }
      console[type](msg);
    }
  };

  // 处理‘abc.bcd.ddd’的相关方法
  Utils.ChainName = {
    _DELIMITER: '.',
    resolve: function (chain) {
      return chain.split(this._DELIMITER);
    },
    join: function(names) {
      return names.join(this._DELIMITER);
    },
    first: function(chain) {
      return this.resolve(chain)[0];
    }
  };
  // 函数节流，见JavaScript高级编程22.3节
  Utils.tr = function(func) {
    this.timeId = null;
    this.processor = func;
  };

  Utils.Processor.prototype.process = function(args, context) {
    clearTimeout(this.timeId);
    var upper = this;
    this.timeId = win.setTimeout(function(){
      upper.processor.apply(context, args);
      Utils.Log.print('run a hook', 'log');
    }, 10);
  };

  var Type = Utils.Type;
  var Log = Utils.Log;
  var ChainName = Utils.ChainName;
  var Processor = Utils.Processor;

  // 支持a.b.c的属性名，直接使用a的Hooks
  var Hook = function() {
    this._hooks = {};
    this._scopes = {};
  };

  Hook.prototype.add = function(scope, hooks) {
    // <param scope>: hook in hooks will call hook.call(win, scope)
    // <param hooks>: must be a Array
    var hookName = scope.getName();
    var savedHooks = this._hooks[hookName] || [];
    var _hooks = [];
    hooks.forEach(function(hook) {
      _hooks.push(new Processor(hook));
    });
    this._hooks[hookName] = savedHooks.concat(_hooks);
    this._scopes[hookName] = scope;
  };

  // 传入链式名称‘abc.bcd.dd’，解析所有要运行的hook
  Hook.prototype.run = function(name){
    var upper = this;
    var hookNames = Hook.getAllHookNames(name);
    hookNames.forEach(function(hookName) {
      upper.runSingle(hookName);
    });
  };

  // 运行this._hooks中单个属性对应的hook 队列
  Hook.prototype.runSingle = function(hookName) {
    var upper = this;
    var hooks = this._hooks[hookName];
    var scope = this._scopes[hookName];
    if ( !hooks ) {
      Log.print([hookName, ' Warning: hook not exist! If the not add ', hookName, ', please ignore this warning.'], 'log');
      return;
    }
    if ( !scope ) {
      Log.print([hookName, ' Error: scope not exist!'], 'error');
      return;
    }
    hooks.forEach(function(hook) {
      if ( hook instanceof Processor ) {
        // 'this' is a problem
        hook.process([scope], win);
      } else {
        Log.print([hookName, ' Error: hook must be a Function'], 'error');
      }
    });
  };

  Hook.getAllHookNames = function(name) {
    var names = ChainName.resolve(name);
    var _names = [];
    // reduce要注意加initial值和不加的区别
    names.reduce(function(prev, cur) {
      // 这里name是不会是空字符串的情况，所以下面的使用可以应付相关问题
      var name = prev ? ChainName.join([prev, cur]) : cur;
      _names.push(name);
      return name;
    }, '');
    return _names;
  };

  var Scope = function(app, schema, chain) {
    /*
     * <param chain>: 用来确定当前scope的作用域，比如说'abc.a.b'
     *   表明当前的schema参数的属性name，可以通过'abc.a.b.name' 来引用;
     *   当对`schema.name`进行赋值时，可以触发`this.hooks`中名称为的'abc'，
     *   'abc.a', 'abc.b'的 hook 队列；
     *
     *
     * Usage:
     *   var m = new Scope(app, schema, chain)
     *
     * */
    if ( !(app instanceof Mood) ) {
      Log.print('Scope instance error: no app parameter exist!', 'error');
      return;
    }
    // This __name is a scope name
    this.__name = chain;
    this._app = app;
    if ( schema && Type.isObject(schema) ) {
      for ( var p in schema ) {
        if ( schema.hasOwnProperty(p) ) {
          this.addProp(p, chain);
        }
      }
    }
  };

  Scope.prototype.addProp = function (name, chain) {
    // 用于缓存当前Scope对象属性的值，以让getter可以正常获得
    var upper = this;
    var app = this._app;
    var inner = '__p_' + name; // 属性的值保存在'__p_test'

    // chain 参考 Scope chain 参数说明
    chain = chain ? chain + '.' : '';
    var chainName = chain + name;

    Object.defineProperty(upper, name, {
      set: function(value) {
        if ( Type.isObject(value) ) {
          // 创建子Scope
          this[inner] = new Scope(app, value, chainName);
        } else {
          this[inner] = value;
        }
        app.hooks.run(chainName);
        Log.print(['Set ', chainName, ': ', value, '; HookName: ', chainName], 'log');
      },
      get: function() {
        Log.print(['Get ', chainName, ': ', this[inner], '; HookName: ', chainName], 'log');
        return this[inner];
      },
    });

  };

  Scope.prototype.getName = function() {
    return this.__name;
  };

  // Mood constructor
  function Mood() {
    this.hooks = new Hook();
    this.rootScope = new Scope(this);
  }

  Mood.prototype.createScope = function(name, schema, controllers, hooks) {
    // <params schema>: is a Object or model structure
    var scope = this.addScope(name, schema);
    // Must before controller
    this.bindHooks(scope, hooks);
    this.initControllers(scope, controllers);
  };

  Mood.prototype.getScope = function(name) {
    return this.rootScope[name];
  };

  Mood.prototype.addScope = function(name, schema) {
    this.rootScope.addProp(name);
    this.rootScope[name] = schema;
    return this.rootScope[name];
  };

  Mood.prototype.bindHooks = function(scope, hooks) {
    if ( Type.isFunction(hooks) ) {
      hooks = [hooks];
    }
    this.hooks.add(scope, hooks);
  };

  Mood.prototype.initControllers = function(scope, contrs) {
    if ( Type.isFunction(contrs) ) {
      contrs = [contrs];
    }
    contrs.forEach(function(contr) {
      contr.call(window, scope);
    });
  };

  Mood._config = {
    debug: false
  };

  Mood.config = function(option) {
    for ( var p in option ) {
      if ( option.hasOwnProperty(p) ) {
        Mood._config = option[p];
      }
    }
  };

  win.Mood = Mood;
  win.Hook = Hook;
})(window);

<commit_msg>修复一些错误，Must be the cat - -!<commit_after>(function(win) {
  'use strict';

  var Utils = {};

  Utils.Type = {
    isObject: function(v) {
      return v instanceof Object;
    },
    isFunction: function(v) {
      return v instanceof Function;
    },
    isArray: function(v) {
      return v instanceof Array;
    }
  };

  Utils.Log = {
    print: function(msg, type) {
      // Mood 开启 debug 时才打印log
      if ( !win.Mood._config.debug ) {
        return;
      }
      // TODO: to be more beautyful
      if ( Utils.Type.isArray(msg) ) {
        msg = '[Mood] ' + msg.join('');
      }
      console[type](msg);
    }
  };

  // 处理‘abc.bcd.ddd’的相关方法
  Utils.ChainName = {
    _DELIMITER: '.',
    resolve: function (chain) {
      return chain.split(this._DELIMITER);
    },
    join: function(names) {
      return names.join(this._DELIMITER);
    },
    first: function(chain) {
      return this.resolve(chain)[0];
    }
  };
  // 函数节流，见JavaScript高级编程22.3节
  Utils.Processor = function(func) {
    this.timeId = null;
    this.processor = func;
  };

  Utils.Processor.prototype.process = function(args, context) {
    clearTimeout(this.timeId);
    var upper = this;
    this.timeId = win.setTimeout(function(){
      upper.processor.apply(context, args);
      Utils.Log.print('run a hook', 'log');
    }, 10);
  };

  var Type = Utils.Type;
  var Log = Utils.Log;
  var ChainName = Utils.ChainName;
  var Processor = Utils.Processor;

  // 支持a.b.c的属性名，直接使用a的Hooks
  var Hook = function() {
    this._hooks = {};
    this._scopes = {};
  };

  Hook.prototype.add = function(scope, hooks) {
    // <param scope>: hook in hooks will call hook.call(win, scope)
    // <param hooks>: must be a Array
    var hookName = scope.getName();
    var savedHooks = this._hooks[hookName] || [];
    var _hooks = [];
    hooks.forEach(function(hook) {
      _hooks.push(new Processor(hook));
    });
    this._hooks[hookName] = savedHooks.concat(_hooks);
    this._scopes[hookName] = scope;
  };

  // 传入链式名称‘abc.bcd.dd’，解析所有要运行的hook
  Hook.prototype.run = function(name){
    var upper = this;
    var hookNames = Hook.getAllHookNames(name);
    hookNames.forEach(function(hookName) {
      upper.runSingle(hookName);
    });
  };

  // 运行this._hooks中单个属性对应的hook 队列
  Hook.prototype.runSingle = function(hookName) {
    var upper = this;
    var hooks = this._hooks[hookName];
    var scope = this._scopes[hookName];
    if ( !hooks ) {
      Log.print([hookName, ' Warning: hook not exist! If the not add ', hookName, ', please ignore this warning.'], 'log');
      return;
    }
    if ( !scope ) {
      Log.print([hookName, ' Error: scope not exist!'], 'error');
      return;
    }
    hooks.forEach(function(hook) {
      if ( hook instanceof Processor ) {
        // 'this' is a problem
        hook.process([scope], win);
      } else {
        Log.print([hookName, ' Error: hook must be a Function'], 'error');
      }
    });
  };

  Hook.getAllHookNames = function(name) {
    var names = ChainName.resolve(name);
    var _names = [];
    // reduce要注意加initial值和不加的区别
    names.reduce(function(prev, cur) {
      // 这里name是不会是空字符串的情况，所以下面的使用可以应付相关问题
      var name = prev ? ChainName.join([prev, cur]) : cur;
      _names.push(name);
      return name;
    }, '');
    return _names;
  };

  var Scope = function(app, schema, chain) {
    /*
     * <param chain>: 用来确定当前scope的作用域，比如说'abc.a.b'
     *   表明当前的schema参数的属性name，可以通过'abc.a.b.name' 来引用;
     *   当对`schema.name`进行赋值时，可以触发`this.hooks`中名称为的'abc'，
     *   'abc.a', 'abc.b'的 hook 队列；
     *
     *
     * Usage:
     *   var m = new Scope(app, schema, chain)
     *
     * */
    if ( !(app instanceof Mood) ) {
      Log.print('Scope instance error: no app parameter exist!', 'error');
      return;
    }
    // This __name is a scope name
    this.__name = chain;
    this._app = app;
    if ( schema && Type.isObject(schema) ) {
      for ( var p in schema ) {
        if ( schema.hasOwnProperty(p) ) {
          this.addProp(p, chain);
        }
      }
    }
  };

  Scope.prototype.addProp = function (name, chain) {
    // 用于缓存当前Scope对象属性的值，以让getter可以正常获得
    var upper = this;
    var app = this._app;
    var inner = '__p_' + name; // 属性的值保存在'__p_test'

    // chain 参考 Scope chain 参数说明
    chain = chain ? chain + '.' : '';
    var chainName = chain + name;

    Object.defineProperty(upper, name, {
      set: function(value) {
        if ( Type.isObject(value) ) {
          // 创建子Scope
          this[inner] = new Scope(app, value, chainName);
        } else {
          this[inner] = value;
        }
        app.hooks.run(chainName);
        Log.print(['Set ', chainName, ': ', value, '; HookName: ', chainName], 'log');
      },
      get: function() {
        Log.print(['Get ', chainName, ': ', this[inner], '; HookName: ', chainName], 'log');
        return this[inner];
      },
    });

  };

  Scope.prototype.getName = function() {
    return this.__name;
  };

  // Mood constructor
  function Mood() {
    this.hooks = new Hook();
    this.rootScope = new Scope(this);
  }

  Mood.prototype.createScope = function(name, schema, controllers, hooks) {
    // <params schema>: is a Object or model structure
    var scope = this.addScope(name, schema);
    // Must before controller
    this.bindHooks(scope, hooks);
    this.initControllers(scope, controllers);
  };

  Mood.prototype.getScope = function(name) {
    return this.rootScope[name];
  };

  Mood.prototype.addScope = function(name, schema) {
    this.rootScope.addProp(name);
    this.rootScope[name] = schema;
    return this.rootScope[name];
  };

  Mood.prototype.bindHooks = function(scope, hooks) {
    if ( Type.isFunction(hooks) ) {
      hooks = [hooks];
    }
    this.hooks.add(scope, hooks);
  };

  Mood.prototype.initControllers = function(scope, contrs) {
    if ( Type.isFunction(contrs) ) {
      contrs = [contrs];
    }
    contrs.forEach(function(contr) {
      contr.call(window, scope);
    });
  };

  Mood._config = {
    debug: false
  };

  Mood.config = function(option) {
    for ( var p in option ) {
      if ( option.hasOwnProperty(p) ) {
        Mood._config = option[p];
      }
    }
  };

  win.Mood = Mood;
  win.Hook = Hook;
})(window);

<|endoftext|>