<commit_before>/**
 * 压缩模板文件 - 业务优化版
 *
 * @see https://www.npmjs.com/package/html-compress
 * @author  Yang,junlong at 2015-07-07 17:53:29 build.
 * @version $Id$
 */

'use strict';

var util = require('./util.js'),
    parser = require('./parser.js');

module.exports = function(content, options){
    var compress = new Compress(content, options);
    return compress.main();
};

function Compress(content, options){
    this.options = options;
    this.content = content;
    this._output = [];
    this._tokenText = '';
    this.leftDelimiter = options.leftDelimiter? options.leftDelimiter : '{%';
    this.rightDelimiter = options.rightDelimiter? options.rightDelimiter : '%}';
    this.level = options.level? options.level : 'strip';

    /**
     *
     * 单一标签，这些标签不需要对应的闭合标签
     * @var array
     */
    this._singleTag = [
        "br", "input", "link", "meta", "!doctype", "basefont", "base",
        "area", "hr", "wbr", "param", "img", "isindex", "?xml", "embed"
    ];

    /**
     *
     * 可删除的闭合标签
     * @var array
     */
    this._removeEndTag = [
        "html", "body", "colgroup", "thead", "tr", "tbody", "td",
        "dt", "dd", "li", "option", "tfoot"
    ];
    /**
     *
     * 块级元素，2个块级元素之间的空格是可以删除的
     * @var array
     */
    this._blockElements = [
        'address','blockquote','center','dir','div','dl','fieldset','form',
        'h1','h2','h3','h4','h5','h6','hr','menu','noframes','noscript',
        'ol','p','pre','table','ul'
    ];
    /**
     *
     * 标签属性默认值,这些默认指是可以删除的
     * @var array
     */
    this._tagAttrDefaultValue = {
        '*' : {
            'class' : '',
            'alt' : ''
        },
        'link' : {
            'media' : 'screen'
        },
        'input' : {
            //'type' => 'text'
        },
        'form' : {
            'method' : 'get'
        },
        'script' : {
            'type' : 'text/javascript',
            'fcpcompress' : ''
        }
    };

    this._tagAttrOnlyName = [
        'disabled','selected','checked', 'readonly', 'multiple'
    ];
}

Compress.prototype.main = function(){
    // 解析模板文件
    var analyticContent = parser(this.content, 1, this.leftDelimiter, this.rightDelimiter);

    for (var i = 0, count = analyticContent.length; i < count; i++){
        this._tokenText = String(analyticContent[i][0]);
        var tokenType = analyticContent[i][1];
        //将连续的换行进行剔除，只留一个换行
        // if ( this._output[this._output.length-1] == '\n' && tokenType === util.sign.FL_NEW_LINE){
        //     continue;
        // }

        switch (tokenType){
            // 去掉换行符
            case util.sign.FL_NEW_LINE :
                break;
            // html 注释 去掉
            case util.sign.HTML_COMMENT :
                break;
            // html 文本内容
            case util.sign.HTML_CONTENT:
                if(this.level != "strip_comment"){
                    this._compressTextContent();
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html 结束标签 是否压缩掉
            case util.sign.HTML_TAG_END :
                if(this.level=="compress"){
                    if (!util.in_array(util.trim(util.trim(this._tokenText, '<>/')), this._removeEndTag)){
                        this._output[this._output.length] = this._tokenText;
                    }
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html 开始标签 不做处理
            case util.sign.HTML_TAG_START :
                //break;
            // js 开始标签
            case util.sign.HTML_JS_START:
                if(this.level=="compress" && !this.isDelimiterScriptStart(tokenType)){
                    this._compressTag();
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html css 内容 压缩css内容
            case util.sign.HTML_CSS_CONTENT :
                this._output[this._output.length] = this._tokenText;
                break;

            // html js 内容 压缩js内容
            case util.sign.HTML_JS_CONTENT :
                this._comressScriptContent();
                break;

            // tpl 模板标签
            case util.sign.FL_TPL_DELIMITER:
                var text = this._tokenText;
                if (text.indexOf('{%*') !== -1){ //去掉注释
                    break;
                }
                if (text.indexOf('extends') !== -1){ //smarty3里extends后至少要加个空白字符
                    text += ' ';
                }
                this._output[this._output.length] = text;
                break;
            default:
                this._output[this._output.length] = this._tokenText;
        }
    }
    return this._output.join('');
};

Compress.prototype._comressScriptContent = function(){
    if (this._tokenText !== ''){
        //去除注释
        var reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)/g;
        this._tokenText = this._tokenText.replace(reg, function(word) { // 去除注释后的文本
            return /^\/{2,}/.test(word) || /^\/\*/.test(word) ? "" : word;
        });

        //多空格转一个
        reg = /\s{2,}/g;
        this._tokenText = this._tokenText.replace(reg," ");

        var tmp = this._tokenText.split(/\n|\r|\t/);
        var out = [];
        for(var i = 0, l = tmp.length; i < l; i++){
            var _out = util.trim(tmp[i]);
            if(_out.substr(0, 2) == '//') {
                continue;
            }

            out.push(_out);
        }
        this._tokenText = out.join('');
        if(this._tokenText !== ''){
            this._output[this._output.length] = this._tokenText;
        }
    }
}

/**
 * 压缩文本内容
 * 如果内容部完全是空格,将多个空格合并为一个空格
 *
 * @return {[type]} [description]
 */
Compress.prototype._compressTextContent = function(){
    if (this._tokenText !== ''){
        //如果内容里还有//，有可能含有注释，则不能去掉换行
        if (this._tokenText.indexOf('//') !== -1){
            this._output[this._output.length] = this._tokenText;
            return true;
        }
        var beReplaceContent = '';
        if(this.level == 'strip:space'){
            beReplaceContent = " ";
        }
        this._tokenText = util.trim(util.str_replace(["\r","\n","\t"], beReplaceContent, this._tokenText));
        if(this._tokenText !== ''){
            this._output[this._output.length] = this._tokenText;
        }
    }
};

Compress.prototype._compressTag = function(){
    var result = parser(this._tokenText, 2, this.leftDelimiter, this.rightDelimiter);

    if (result[0] === util.sign.HTML_TAG_END){
        this._output[this._output.length] = '</' + result[1] + '>';
        return true;
    }
    var resultString = '<' + result[1];
    if (result[2].length){
        var blankSpace = ' ';
        var chPattern = this._tagAttrDefaultValue['*'];
        if (util.is_array(this._tagAttrDefaultValue[result[1]])){
            chPattern = util.array_merge(chPattern, this._tagAttrDefaultValue[result[1]]);
        }

        for(var i = 0; i < result[2].length; i++){
            var item = result[2][i];
            resultString = util.rtrim(resultString);
            //先进行压缩：属性或者属性值去除
            var v = util.trim(item[1],"'\"");
            var tv = chPattern[item[0]];
            //如果值在默认可去除的值范围内，则将该属性去除
            if (v === tv){
                continue;
            }
            //如果值内没有一些特殊字符，可以将两边的引号去除
            if (/^[\w\-\/\:\.\?\=]+$/.test(v)){
                resultString += blankSpace + item[0] + '=' + v;
                continue;
            }

            //smarty变量
            if (item[0] === util.sign.HTML_TPL_ATTR_NAME){
                resultString += blankSpace + item[1];
                continue;
            }
            //只要属性名的，属性值可以省略的
            if (util.in_array(item[0], this._tagAttrOnlyName)){
                resultString += blankSpace + item[0];
                continue;
            }
            //only attr,no value.such as "disabled"
            if (!util.strlen(item[1])){
                resultString += blankSpace + item[0];
                continue;
            }
            resultString += blankSpace + item[0] + '=' + item[1];
        }
    };
    resultString += '>';
    this._output[this._output.length] = resultString;
};

Compress.prototype.isDelimiterScriptStart = function(tokenType){
    if(tokenType == util.sign.HTML_JS_START
        && this._checkEqual(this._tokenText, 0, this.leftDelimiter.length + 6, this.leftDelimiter + 'script')){
        return true;
    }
    return false;
}

Compress.prototype._checkEqual = function(str, start, len, result){
    return str.substr(start, len).toLowerCase() === result.toLowerCase();
};<commit_msg>{% -> this.leftDelimiter<commit_after>/**
 * 压缩模板文件 - 业务优化版
 *
 * @see https://www.npmjs.com/package/html-compress
 * @author  Yang,junlong at 2015-07-07 17:53:29 build.
 * @version $Id$
 */

'use strict';

var util = require('./util.js'),
    parser = require('./parser.js');

module.exports = function(content, options){
    var compress = new Compress(content, options);
    return compress.main();
};

function Compress(content, options){
    this.options = options;
    this.content = content;
    this._output = [];
    this._tokenText = '';
    this.leftDelimiter = options.leftDelimiter? options.leftDelimiter : '{%';
    this.rightDelimiter = options.rightDelimiter? options.rightDelimiter : '%}';
    this.level = options.level? options.level : 'strip';

    /**
     *
     * 单一标签，这些标签不需要对应的闭合标签
     * @var array
     */
    this._singleTag = [
        "br", "input", "link", "meta", "!doctype", "basefont", "base",
        "area", "hr", "wbr", "param", "img", "isindex", "?xml", "embed"
    ];

    /**
     *
     * 可删除的闭合标签
     * @var array
     */
    this._removeEndTag = [
        "html", "body", "colgroup", "thead", "tr", "tbody", "td",
        "dt", "dd", "li", "option", "tfoot"
    ];
    /**
     *
     * 块级元素，2个块级元素之间的空格是可以删除的
     * @var array
     */
    this._blockElements = [
        'address','blockquote','center','dir','div','dl','fieldset','form',
        'h1','h2','h3','h4','h5','h6','hr','menu','noframes','noscript',
        'ol','p','pre','table','ul'
    ];
    /**
     *
     * 标签属性默认值,这些默认指是可以删除的
     * @var array
     */
    this._tagAttrDefaultValue = {
        '*' : {
            'class' : '',
            'alt' : ''
        },
        'link' : {
            'media' : 'screen'
        },
        'input' : {
            //'type' => 'text'
        },
        'form' : {
            'method' : 'get'
        },
        'script' : {
            'type' : 'text/javascript',
            'fcpcompress' : ''
        }
    };

    this._tagAttrOnlyName = [
        'disabled','selected','checked', 'readonly', 'multiple'
    ];
}

Compress.prototype.main = function(){
    // 解析模板文件
    var analyticContent = parser(this.content, 1, this.leftDelimiter, this.rightDelimiter);

    for (var i = 0, count = analyticContent.length; i < count; i++){
        this._tokenText = String(analyticContent[i][0]);
        var tokenType = analyticContent[i][1];
        //将连续的换行进行剔除，只留一个换行
        // if ( this._output[this._output.length-1] == '\n' && tokenType === util.sign.FL_NEW_LINE){
        //     continue;
        // }

        switch (tokenType){
            // 去掉换行符
            case util.sign.FL_NEW_LINE :
                break;
            // html 注释 去掉
            case util.sign.HTML_COMMENT :
                break;
            // html 文本内容
            case util.sign.HTML_CONTENT:
                if(this.level != "strip_comment"){
                    this._compressTextContent();
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html 结束标签 是否压缩掉
            case util.sign.HTML_TAG_END :
                if(this.level=="compress"){
                    if (!util.in_array(util.trim(util.trim(this._tokenText, '<>/')), this._removeEndTag)){
                        this._output[this._output.length] = this._tokenText;
                    }
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html 开始标签 不做处理
            case util.sign.HTML_TAG_START :
                //break;
            // js 开始标签
            case util.sign.HTML_JS_START:
                if(this.level=="compress" && !this.isDelimiterScriptStart(tokenType)){
                    this._compressTag();
                }else{
                    this._output[this._output.length] = this._tokenText;
                }
                break;
            // html css 内容 压缩css内容
            case util.sign.HTML_CSS_CONTENT :
                this._output[this._output.length] = this._tokenText;
                break;

            // html js 内容 压缩js内容
            case util.sign.HTML_JS_CONTENT :
                this._comressScriptContent();
                break;

            // tpl 模板标签
            case util.sign.FL_TPL_DELIMITER:
                var text = this._tokenText;
                if (text.indexOf(this.leftDelimiter + '*') !== -1){ //去掉注释
                    break;
                }
                if (text.indexOf('extends') !== -1){ //smarty3里extends后至少要加个空白字符
                    text += ' ';
                }
                this._output[this._output.length] = text;
                break;
            default:
                this._output[this._output.length] = this._tokenText;
        }
    }
    return this._output.join('');
};

Compress.prototype._comressScriptContent = function(){
    if (this._tokenText !== ''){
        //去除注释
        var reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)/g;
        this._tokenText = this._tokenText.replace(reg, function(word) { // 去除注释后的文本
            return /^\/{2,}/.test(word) || /^\/\*/.test(word) ? "" : word;
        });

        //多空格转一个
        reg = /\s{2,}/g;
        this._tokenText = this._tokenText.replace(reg," ");

        var tmp = this._tokenText.split(/\n|\r|\t/);
        var out = [];
        for(var i = 0, l = tmp.length; i < l; i++){
            var _out = util.trim(tmp[i]);
            if(_out.substr(0, 2) == '//') {
                continue;
            }

            out.push(_out);
        }
        this._tokenText = out.join('');
        if(this._tokenText !== ''){
            this._output[this._output.length] = this._tokenText;
        }
    }
}

/**
 * 压缩文本内容
 * 如果内容部完全是空格,将多个空格合并为一个空格
 *
 * @return {[type]} [description]
 */
Compress.prototype._compressTextContent = function(){
    if (this._tokenText !== ''){
        //如果内容里还有//，有可能含有注释，则不能去掉换行
        if (this._tokenText.indexOf('//') !== -1){
            this._output[this._output.length] = this._tokenText;
            return true;
        }
        var beReplaceContent = '';
        if(this.level == 'strip:space'){
            beReplaceContent = " ";
        }
        this._tokenText = util.trim(util.str_replace(["\r","\n","\t"], beReplaceContent, this._tokenText));
        if(this._tokenText !== ''){
            this._output[this._output.length] = this._tokenText;
        }
    }
};

Compress.prototype._compressTag = function(){
    var result = parser(this._tokenText, 2, this.leftDelimiter, this.rightDelimiter);

    if (result[0] === util.sign.HTML_TAG_END){
        this._output[this._output.length] = '</' + result[1] + '>';
        return true;
    }
    var resultString = '<' + result[1];
    if (result[2].length){
        var blankSpace = ' ';
        var chPattern = this._tagAttrDefaultValue['*'];
        if (util.is_array(this._tagAttrDefaultValue[result[1]])){
            chPattern = util.array_merge(chPattern, this._tagAttrDefaultValue[result[1]]);
        }

        for(var i = 0; i < result[2].length; i++){
            var item = result[2][i];
            resultString = util.rtrim(resultString);
            //先进行压缩：属性或者属性值去除
            var v = util.trim(item[1],"'\"");
            var tv = chPattern[item[0]];
            //如果值在默认可去除的值范围内，则将该属性去除
            if (v === tv){
                continue;
            }
            //如果值内没有一些特殊字符，可以将两边的引号去除
            if (/^[\w\-\/\:\.\?\=]+$/.test(v)){
                resultString += blankSpace + item[0] + '=' + v;
                continue;
            }

            //smarty变量
            if (item[0] === util.sign.HTML_TPL_ATTR_NAME){
                resultString += blankSpace + item[1];
                continue;
            }
            //只要属性名的，属性值可以省略的
            if (util.in_array(item[0], this._tagAttrOnlyName)){
                resultString += blankSpace + item[0];
                continue;
            }
            //only attr,no value.such as "disabled"
            if (!util.strlen(item[1])){
                resultString += blankSpace + item[0];
                continue;
            }
            resultString += blankSpace + item[0] + '=' + item[1];
        }
    };
    resultString += '>';
    this._output[this._output.length] = resultString;
};

Compress.prototype.isDelimiterScriptStart = function(tokenType){
    if(tokenType == util.sign.HTML_JS_START
        && this._checkEqual(this._tokenText, 0, this.leftDelimiter.length + 6, this.leftDelimiter + 'script')){
        return true;
    }
    return false;
}

Compress.prototype._checkEqual = function(str, start, len, result){
    return str.substr(start, len).toLowerCase() === result.toLowerCase();
};<|endoftext|>


<commit_before>var nature = (function(){

	function resolveInheritance(parents){

		//empty baseline (no parents)
		var definitions = [];

		//recursively resolve inheratance
		recursiveInheretance(parents, definitions);

		return definitions;
	}

	function recursiveInheretance(parents, definitions){

		var i = parents.length, def;
		while(i--){
			def = parents[i];
			if(!def["nature:definition"]){
				//ingore previously resolved dependencies
				if(definitions.indexOf(def)===-1){
					definitions.push(def);
				}
			} else {
				recursiveInheretance(def["nature:definition"], definitions);
			}

		}
	}

	function createClass(args, keys, factoryMode){

		var definitions = resolveInheritance(args);

		var unfold, packageKey, mainDef;

		if (keys){

			//get main method definition
			mainDef = definitions[0];

			//unfold method
			unfold = function(obj){
				if(!obj['nature:protected']) {
					throw new Error("Nature.js: Object package not found.");
				}
				return obj['nature:protected'](keys);
			}

			//reassign new method enclosed with unfold
			definitions[0] = args[args.length-1] = function(pub, prot){
				mainDef.apply(this, [pub, prot, unfold]);
			}

			packageKey = keys[keys.length-1];
		}

		function generateInstance(args, pub, prot){
			var i=definitions.length;

			//create from definitions
			while(i--){
				definitions[i](pub, prot);
			}

			//initialise constructor if it exists
			if(typeof prot.construct === "function"){
				prot.construct.apply(prot, args);
			}

			if(packageKey){

				Object.defineProperty(pub, "nature:protected", {
				  enumerable: false,
				  configurable: false,
				  writable: false,
				  value: function(keys){
						if(packageKey && keys && keys.indexOf(packageKey)!==-1){
							return prot;
						} else {
							throw new Error("Nature.js: Private access from out of package denied.");
						}
					}
				});
			}
		}

		function Nature(){
			var prot = {};
			//in factoryMode our instance is actually a function calling prot.scope
			var instance = this;

			if(factoryMode) {
				instance = function(){
					if(typeof prot.scope === 'function'){
						return prot.scope.apply(this, arguments)
					}
				}
				Object.defineProperty(instance, "nature:isFactory", {
					enumerable: false,
					configurable: false,
					writable: false,
					value: true
				});
			}

			generateInstance(arguments, instance, prot);

			if(factoryMode) {
				//keep inheritance chain
				if(typeof prot.scope === 'function'){
					instance.prototype = prot.scope.prototype;
				}
			}

			return instance;
		}

		//save definitions for future inheritance dependencies
		Object.defineProperty(Nature, "nature:definition", {
			enumerable: false,
			configurable: false,
			writable: false,
			value: args
		});

		Object.defineProperty(Nature, "name", {
			enumerable: false,
			configurable: false,
			writable: false,
			value: 'test'
		});

		return Nature;

	}

	function createNature(pKeys){

		var locked = false;

		function generateCreateMethod(deps, isFactory){

			return function create(def){
				if(locked) {
					throw new Error("Nature.js: cannot create class on closed package.");
				}

				deps.push(def);
				return createClass(deps, pKeys, isFactory);
			}
		}

		var pack = {

			createPackage : function createPackage(){

				var packageKeys = pKeys ? pKeys.slice() : [];

				packageKeys.push({});

				return createNature(packageKeys);
			},

			from : function from(){
				var deps = [].slice.apply(arguments);
				return {
					create: generateCreateMethod(deps),
					factory: generateCreateMethod(deps, true)
				}
			},

			create: function create() {
				return generateCreateMethod([]).apply(null, arguments)
			},

			factory: generateCreateMethod([], true)
		}

		if(pKeys){
			pack.close = function close(){
				locked = true;
			}
		}

		return pack;
	}

	return createNature();

})();

if(typeof module !== 'undefined' && module.exports){
	module.exports = nature;
}
<commit_msg>chore(nature): remove name definition<commit_after>var nature = (function(){

	function resolveInheritance(parents){

		//empty baseline (no parents)
		var definitions = [];

		//recursively resolve inheratance
		recursiveInheretance(parents, definitions);

		return definitions;
	}

	function recursiveInheretance(parents, definitions){

		var i = parents.length, def;
		while(i--){
			def = parents[i];
			if(!def["nature:definition"]){
				//ingore previously resolved dependencies
				if(definitions.indexOf(def)===-1){
					definitions.push(def);
				}
			} else {
				recursiveInheretance(def["nature:definition"], definitions);
			}

		}
	}

	function createClass(args, keys, factoryMode){

		var definitions = resolveInheritance(args);

		var unfold, packageKey, mainDef;

		if (keys){

			//get main method definition
			mainDef = definitions[0];

			//unfold method
			unfold = function(obj){
				if(!obj['nature:protected']) {
					throw new Error("Nature.js: Object package not found.");
				}
				return obj['nature:protected'](keys);
			}

			//reassign new method enclosed with unfold
			definitions[0] = args[args.length-1] = function(pub, prot){
				mainDef.apply(this, [pub, prot, unfold]);
			}

			packageKey = keys[keys.length-1];
		}

		function generateInstance(args, pub, prot){
			var i=definitions.length;

			//create from definitions
			while(i--){
				definitions[i](pub, prot);
			}

			//initialise constructor if it exists
			if(typeof prot.construct === "function"){
				prot.construct.apply(prot, args);
			}

			if(packageKey){

				Object.defineProperty(pub, "nature:protected", {
				  enumerable: false,
				  configurable: false,
				  writable: false,
				  value: function(keys){
						if(packageKey && keys && keys.indexOf(packageKey)!==-1){
							return prot;
						} else {
							throw new Error("Nature.js: Private access from out of package denied.");
						}
					}
				});
			}
		}

		function Nature(){
			var prot = {};
			//in factoryMode our instance is actually a function calling prot.scope
			var instance = this;

			if(factoryMode) {
				instance = function(){
					if(typeof prot.scope === 'function'){
						return prot.scope.apply(this, arguments)
					}
				}
				Object.defineProperty(instance, "nature:isFactory", {
					enumerable: false,
					configurable: false,
					writable: false,
					value: true
				});
			}

			generateInstance(arguments, instance, prot);

			if(factoryMode) {
				//keep inheritance chain
				if(typeof prot.scope === 'function'){
					instance.prototype = prot.scope.prototype;
				}
			}

			return instance;
		}

		//save definitions for future inheritance dependencies
		Object.defineProperty(Nature, "nature:definition", {
			enumerable: false,
			configurable: false,
			writable: false,
			value: args
		});

		return Nature;

	}

	function createNature(pKeys){

		var locked = false;

		function generateCreateMethod(deps, isFactory){

			return function create(def){
				if(locked) {
					throw new Error("Nature.js: cannot create class on closed package.");
				}

				deps.push(def);
				return createClass(deps, pKeys, isFactory);
			}
		}

		var pack = {

			createPackage : function createPackage(){

				var packageKeys = pKeys ? pKeys.slice() : [];

				packageKeys.push({});

				return createNature(packageKeys);
			},

			from : function from(){
				var deps = [].slice.apply(arguments);
				return {
					create: generateCreateMethod(deps),
					factory: generateCreateMethod(deps, true)
				}
			},

			create: function create() {
				return generateCreateMethod([]).apply(null, arguments)
			},

			factory: generateCreateMethod([], true)
		}

		if(pKeys){
			pack.close = function close(){
				locked = true;
			}
		}

		return pack;
	}

	return createNature();

})();

if(typeof module !== 'undefined' && module.exports){
	module.exports = nature;
}
<|endoftext|>


<commit_before>'use strict';

const Hoek = require('hoek');
const Joi = require('joi');
const Async = require('async');
const _ = require('lodash');
const Promise = require('bluebird');

/**
 * Plugin Internals
 * @type {Object}
 */
const internals = {};

// TODO: maybe options
const Configue = internals.Configue = module.exports = function Configue(options = {}) {
    if (!(this instanceof Configue)) {
        return new Configue(options);
    }

    // load fresh instance of nconf
    delete require.cache[require.resolve('nconf')];

    const nconf = internals.nconf = this.nconf = require('nconf');
    const settings = this.settings = Hoek.cloneWithShallow(options, 'provider');
    this.resolved = false;

    const results = Joi.validate(settings, internals.schema);
    if (results.error) throw results.error;

    nconf.use('memory');
    nconf.clear();

    if (!options.defer) {
        this.resolve();
    }
};

// Fluent builder
Configue._options = {};
['files', 'defaults', 'disable', 'env', 'argv', 'customWorkflow', 'required', 'overrides', 'defer'].forEach((option) => {
    Configue[option] = (opt) => {
        Configue._options[option] = opt;
        return Configue;
    };
});
['first', 'overrides', 'argv', 'env', 'files', 'defaults'].forEach((hook) => {
    Configue[hook + 'Hook'] = (opt) => {
        _.set(Configue._options, `postHooks.${hook}`, opt);
        return Configue;
    };
});
Configue.get = () => {
    const c = new Configue(Configue._options);
    Configue._options = {};
    return c;
};

Configue.prototype.resolve = function ()  {
    if (this.resolved) return;

    if (this.settings.customWorkflow)
        this.settings.customWorkflow(this.nconf);
    else internals.applyDefaultWorkflow(this.nconf, this.settings);
    this.resolved = true;
};

Configue.prototype.get = function get(key, defaultValue) {
    const result = this.nconf.get(key);
    return result === undefined ? defaultValue : result;
};

Configue.prototype.getFirst = function (keys) {
    // TODO refactor once es6 full support
    for(const key of (Array.isArray(keys)? keys : Array.from(arguments))) {
        const result = this.nconf.get(key);
        if (result !== undefined) return result;
    }
};

Configue.prototype.getAll = function (keys) {
    return (Array.isArray(keys)? keys : Array.from(arguments))
        .map((key) => this.nconf.get(key));
};

internals.configueTemplate = (configue, defaults = {}) => (chains, ...keys) => {
    return _.reduce(_.zip(chains, keys),
        (acc, [chain, key]) => {
            const base = acc + chain;
            if (!key) return base;
            return base + configue.get(key, _.get(defaults, key));
        },
        '')
};

// TODO: cover with test!! and document!
Configue.prototype.template = function(defaultOrChain, ...keys){
    if(_.isPlainObject(defaultOrChain))
       return internals.configueTemplate(this, defaultOrChain)
    else
        return internals.configueTemplate(this)(defaultOrChain, ...keys)

};

Configue.prototype.t = internals.Configue.prototype.template;

Configue.prototype.getObject = function (...args) {
    const keys = Array.isArray(_.first(args)) ? _.first(args): args;
    return _.reduce(keys, (memo, key) => ({...memo, [key]: this.get(key)}), {});
};

/**
 * Register the <tt>Configue</tt> plugin and process the various steps and hooks
 * @param server - Hapi server to configure
 * @param options - options of the Configue Plugin
 * @param next - plugin continuation
 */
const hapiPlugin = Configue.prototype.plugin = function (decorateName) {
    const configue = this;
    const plugin = function plugin(server, options, next) {

        if (!configue.resolved) {
            try {
                configue.resolve();
            } catch (err) {
                return next(err);
            }
        }
        server.log(['plugin', 'info'], 'Registering the configue has decoration');
        const configGetter = configue.get.bind(configue);
        configGetter.get = configGetter;
        configGetter.getFirst  = configue.getFirst.bind(configue);
        configGetter.getAll  = configue.getAll.bind(configue);

        server.decorate('server', decorateName || 'configue', configGetter);
        server.decorate('request', decorateName || 'configue', configGetter);
        next();
    };
    plugin.attributes = hapiPlugin.attributes;
    return plugin;
};

hapiPlugin.attributes = {
    pkg: require('../package.json')
};

/**
 * Joi options schema
 */
internals.schema = [
    Joi.object({defer: Joi.boolean(), customWorkflow: Joi.func()}),
    Joi.object().keys({
        defer: Joi.boolean(),
        disable: Joi.object({
            argv: Joi.boolean(),
            env: Joi.boolean()
        }),
        argv: [Joi.object()],
        env: [Joi.object(), Joi.array().items(Joi.string()), Joi.string()],

        files: [Joi.string(),
            Joi.array().items(Joi.object({
                file: Joi.string().required(),
                format: Joi.object({stringify: Joi.func(), parse: Joi.func()})
            })),
            Joi.array().items(Joi.string())],
        defaults: [Joi.object(),
            Joi.array().items(Joi.object())],
        overrides: Joi.object(),
        required: Joi.array().items(Joi.string()),
        postHooks: Joi.object({
            first: Joi.func(),
            overrides: Joi.func(),
            argv: Joi.func(),
            env: Joi.func(),
            files: Joi.func(),
            defaults: Joi.func()
        })
    })];

/**
 * Apply the Default Configuration Workflow
 * @param nconf - the nconf object
 * @param settings - plugin config
 */
internals.applyDefaultWorkflow = function applyDefaultWorkflow(nconf, settings) {

  // Load eventual overrides values and then iterates over the different steps (in order: argv, env, files, default)
  this.processHook(settings.postHooks, 'first');
  this.iterateSteps(this.steps, settings);
  this.checkRequired(settings);
};

/**
 * Iterate synchronously over the various steps
 * @param steps - list of configuration steps
 * @param settings - project settings
 * @returns {Function}
 */
internals.iterateSteps = function iterateSteps(steps, settings) {
    const hooks = settings.postHooks;
    for(const stepName of steps) {
        this.stepActions[stepName](settings);
        if (hooks && hooks[stepName])
            this.executePostHook(hooks[stepName]);
    }
};

/**
 * Ordered list of configuration steps
 * @type {string[]}
 */
internals.steps = ['overrides', 'argv', 'env', 'files', 'defaults'];
// Definition of associated actions below

/**
 * Closure that takes the name of a nconf resource as a parameters and
 * loads it if it is not disabled in the plugin options
 *
 * @param resource - the ressource to be loaded. (argv, env, files...)
 * @returns {Function}
 */
internals.load = function load(resource) {
    return function onResource(options) {
        if (!options.disable || !options.disable[resource]) {
            return internals.nconf[resource](options[resource]);
        }
    };
};

/**
 * Process the hook for a given step
 * @param hooks
 * @param stepName
 * @returns {Function}
 */
internals.processHook = function processHook(hooks, stepName) {
    if (hooks && hooks[stepName])
        this.executePostHook(hooks[stepName]);
};

/**
 * Execute a hook
 * @param hook - a post step hook
 */
internals.executePostHook = function executeHook(hook) {
    return hook(internals.nconf);
};

internals.checkRequired = function (options) {
    if (options.required) {
        this.nconf.required(options.required);
    }
};

/**
 * Load the files in options using <tt>nconf.file</tt>
 * @param options - plugin options
 */
internals.loadFiles = function loadFiles(options) {
    const files = options.files;
    if (Array.isArray(files) && files.length) {
        files.forEach(file => internals.nconf.file((typeof files[0] === 'string') ? file : file.file, file));
        // file(.file) is used as namespace for nconf
    } else if (typeof files === 'string' && files.length)
        internals.nconf.file(files);
};

/**
 * Load the defaults in options using <tt>nconf.defaults</tt>
 * @param options - plugin options
 */
internals.loadDefaults = function loadDefaults(options) {
    const defaults = options.defaults;
    internals.nconf.defaults(Array.isArray(defaults) ? _.defaults.apply({}, defaults) : defaults);
};

/**
 * Steps and their associated action function
 * @type {{argv: Function, env: Function, files: loadFiles}}
 */
internals.stepActions = {
    overrides: internals.load('overrides'),
    argv: internals.load('argv'),
    env: internals.load('env'),
    files: internals.loadFiles,
    defaults: internals.loadDefaults
};<commit_msg>refactor: major change removing the internal object<commit_after>'use strict';

const Hoek = require('hoek');
const Joi = require('joi');
const Async = require('async');
const _ = require('lodash');
const Promise = require('bluebird');

// TODO: maybe options
const Configue = module.exports = function Configue(options = {}) {
    if (!(this instanceof Configue)) {
        return new Configue(options);
    }

    // load fresh instance of nconf
    delete require.cache[require.resolve('nconf')];

    const nconf = this.nconf = require('nconf');
    const settings = this.settings = Hoek.cloneWithShallow(options, 'provider');
    this.resolved = false;

    const results = Joi.validate(settings, configueOptionsSchema);
    if (results.error) throw results.error;

    nconf.use('memory');
    nconf.clear();

    if (!options.defer) {
        this.resolve();
    }
};

// Fluent builder
Configue._options = {};
['files', 'defaults', 'disable', 'env', 'argv', 'customWorkflow', 'required', 'overrides', 'defer'].forEach((option) => {
    Configue[option] = (opt) => {
        Configue._options[option] = opt;
        return Configue;
    };
});
['first', 'overrides', 'argv', 'env', 'files', 'defaults'].forEach((hook) => {
    Configue[hook + 'Hook'] = (opt) => {
        _.set(Configue._options, `postHooks.${hook}`, opt);
        return Configue;
    };
});
Configue.get = () => {
    const c = new Configue(Configue._options);
    Configue._options = {};
    return c;
};

Configue.prototype.resolve = function ()  {
    if (this.resolved) return;

    if (this.settings.customWorkflow)
        this.settings.customWorkflow(this.nconf);
    else applyDefaultWorkflow(this.nconf, this.settings);
    this.resolved = true;
};

Configue.prototype.get = function get(key, defaultValue) {
    const result = this.nconf.get(key);
    return result === undefined ? defaultValue : result;
};

Configue.prototype.getFirst = function (keys) {
    // TODO refactor once es6 full support
    for(const key of (Array.isArray(keys)? keys : Array.from(arguments))) {
        const result = this.nconf.get(key);
        if (result !== undefined) return result;
    }
};

Configue.prototype.getAll = function (keys) {
    return (Array.isArray(keys)? keys : Array.from(arguments))
        .map((key) => this.nconf.get(key));
};

const configueTemplate = (configue, defaults = {}) => (chains, ...keys) => {
    return _.reduce(_.zip(chains, keys),
        (acc, [chain, key]) => {
            const base = acc + chain;
            if (!key) return base;
            return base + configue.get(key, _.get(defaults, key));
        },
        '')
};

// TODO: cover with test!! and document!
Configue.prototype.template = function(defaultOrChain, ...keys){
    if(_.isPlainObject(defaultOrChain))
       return configueTemplate(this, defaultOrChain)
    else
        return configueTemplate(this)(defaultOrChain, ...keys)

};

Configue.prototype.t = Configue.prototype.template;

Configue.prototype.getObject = function (...args) {
    const keys = Array.isArray(_.first(args)) ? _.first(args): args;
    return _.reduce(keys, (memo, key) => ({...memo, [key]: this.get(key)}), {});
};

/**
 * Register the <tt>Configue</tt> plugin and process the various steps and hooks
 * @param server - Hapi server to configure
 * @param options - options of the Configue Plugin
 * @param next - plugin continuation
 */
const hapiPlugin = Configue.prototype.plugin = function (decorateName) {
    const configue = this;
    const plugin = function plugin(server, options, next) {

        if (!configue.resolved) {
            try {
                configue.resolve();
            } catch (err) {
                return next(err);
            }
        }
        server.log(['plugin', 'info'], 'Registering the configue has decoration');
        const configGetter = configue.get.bind(configue);
        configGetter.get = configGetter;
        configGetter.getFirst  = configue.getFirst.bind(configue);
        configGetter.getAll  = configue.getAll.bind(configue);

        server.decorate('server', decorateName || 'configue', configGetter);
        server.decorate('request', decorateName || 'configue', configGetter);
        next();
    };
    plugin.attributes = hapiPlugin.attributes;
    return plugin;
};

hapiPlugin.attributes = {
    pkg: require('../package.json')
};

/**
 * Joi options schema
 */
const configueOptionsSchema = [
    Joi.object({defer: Joi.boolean(), customWorkflow: Joi.func()}),
    Joi.object().keys({
        defer: Joi.boolean(),
        disable: Joi.object({
            argv: Joi.boolean(),
            env: Joi.boolean()
        }),
        argv: [Joi.object()],
        env: [Joi.object(), Joi.array().items(Joi.string()), Joi.string()],

        files: [Joi.string(),
            Joi.array().items(Joi.object({
                file: Joi.string().required(),
                format: Joi.object({stringify: Joi.func(), parse: Joi.func()})
            })),
            Joi.array().items(Joi.string())],
        defaults: [Joi.object(),
            Joi.array().items(Joi.object())],
        overrides: Joi.object(),
        required: Joi.array().items(Joi.string()),
        postHooks: Joi.object({
            first: Joi.func(),
            overrides: Joi.func(),
            argv: Joi.func(),
            env: Joi.func(),
            files: Joi.func(),
            defaults: Joi.func()
        })
    })];

/**
 * Apply the Default Configuration Workflow
 * @param nconf - the nconf object
 * @param settings - plugin config
 */
const applyDefaultWorkflow = function applyDefaultWorkflow(nconf, settings) {

  // Load eventual overrides values and then iterates over the different steps (in order: argv, env, files, default)
  processHook(nconf, settings.postHooks, 'first');
  iterateSteps(nconf, STEPS, settings);
  checkRequired(nconf, settings);
};

/**
 * Iterate synchronously over the various steps
 * @param steps - list of configuration steps
 * @param settings - project settings
 * @returns {Function}
 */
const iterateSteps = (nconf, steps, settings) => {
    const hooks = settings.postHooks;
    for(const stepName of steps) {
        STEP_ACTIONS[stepName](nconf, settings);
        processHook(nconf, hooks, stepName);
    }
};

/**
 * Ordered list of configuration steps
 * @type {string[]}
 */
const STEPS = ['overrides', 'argv', 'env', 'files', 'defaults'];
// Definition of associated actions below

/**
 * Closure that takes the name of a nconf resource as a parameters and
 * loads it if it is not disabled in the plugin options
 *
 * @param resource - the ressource to be loaded. (argv, env, files...)
 * @returns {Function} (nconf, options)
 */
const load = resource => (nconf, options) => {
    if (!options.disable || !options.disable[resource]) {
        return nconf[resource](options[resource]);
    }
};

/**
 * Process the hook for a given step
 * @param hooks
 * @param stepName
 * @returns {Function}
 */
const processHook = (nconf, hooks, stepName) => {
    if (hooks && hooks[stepName])
        hooks[stepName](nconf);
};

const checkRequired = (nconf, options) => {
    if (options.required) {
        nconf.required(options.required);
    }
};

/**
 * Load the files in options using <tt>nconf.file</tt>
 * @param options - plugin options
 */
const loadFiles = (nconf, options) => {
    const files = options.files;
    if (Array.isArray(files) && files.length) {
        files.forEach(file => nconf.file((typeof files[0] === 'string') ? file : file.file, file));
        // file(.file) is used as namespace for nconf
    } else if (typeof files === 'string' && files.length)
        nconf.file(files);
};

/**
 * Load the defaults in options using <tt>nconf.defaults</tt>
 * @param options - plugin options
 */
const loadDefaults = function loadDefaults(nconf, options) {
    const defaults = options.defaults;
    nconf.defaults(Array.isArray(defaults) ? _.defaults.apply({}, defaults) : defaults);
};

/**
 * Steps and their associated action function
 * @type {{argv: Function, env: Function, files: loadFiles}}
 */
const STEP_ACTIONS = {
    overrides: load('overrides'),
    argv: load('argv'),
    env: load('env'),
    files: loadFiles,
    defaults: loadDefaults
};
<|endoftext|>


<commit_before>var Promise     = require('bluebird')
  , winston     = require('winston')
  , _           = require('lodash')
  ;

class Consumer {
  listen(server) {
    let io = require('socket.io')(server);

    io.on('connection', (socket) => {
      this.on_connection(socket);

      socket.on('disconnect', () => {
        this.on_disconnect(socket);
      });

      socket.on('msg', (msg) => {
        this.on_msg(msg);
      });
    });

    this.io = io;
  }

  on_connection(socket) {
    winston.info('a user connected');
  }

  on_disconnect(socket) {
    winston.info('user disconnected');
  }

  /*
  {
    type  : stdout | stderr,
    time  : unix tyime stamp,
    data  : buffer [data]
  }
  */
  on_msg(msg) {
    if(msg.type == 'stdout') {
      winston.info(msg.data.toString());
    }

    if(msg.type == 'stderr') {
      winston.error(msg.data.toString());
    }
  }

  static make_server() {
    return Promise
            .try(() => {
              let server = require('http').createServer(() => {});

              return Promise
                      .fromCallback((cb) => {
                        server.listen((err) => cb(err, server));
                      });
            });
  }
}

module.exports = Consumer;

if (require.main === module) {
  Consumer
    .make_server()
    .then((server) => {
      (new Consumer()).listen(server);
    });
}<commit_msg>add server to self<commit_after>var Promise     = require('bluebird')
  , winston     = require('winston')
  , _           = require('lodash')
  ;

class Consumer {
  listen(server) {
    let io = require('socket.io')(server);

    io.on('connection', (socket) => {
      this.on_connection(socket);

      socket.on('disconnect', () => {
        this.on_disconnect(socket);
      });

      socket.on('msg', (msg) => {
        this.on_msg(msg);
      });
    });

    this.io = io;
    this.server = server;
  }

  on_connection(socket) {
    winston.info('a user connected');
  }

  on_disconnect(socket) {
    winston.info('user disconnected');
  }

  /*
  {
    type  : stdout | stderr,
    time  : unix tyime stamp,
    data  : buffer [data]
  }
  */
  on_msg(msg) {
    if(msg.type == 'stdout') {
      winston.info(msg.data.toString());
    }

    if(msg.type == 'stderr') {
      winston.error(msg.data.toString());
    }
  }

  static make_server() {
    return Promise
            .try(() => {
              let server = require('http').createServer(() => {});

              return Promise
                      .fromCallback((cb) => {
                        server.listen((err) => cb(err, server));
                      });
            });
  }
}

module.exports = Consumer;

if (require.main === module) {
  Consumer
    .make_server()
    .then((server) => {
      (new Consumer()).listen(server);
    });
}<|endoftext|>


<commit_before>/* continue.js 1.x -- A very easy and clean async flow controller
 *
 * Copyright (C) 2015 Chizhong Jin
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD license.  See the LICENSE file for details.
 */

'use strict';

var func_length_n = function(n, func) {
  // black magic, here !!! help me, if you have better idea
  switch (n) {
    case 0:  return function() {return func(arguments)};
    case 1:  return function(a) {return func(arguments)};
    case 2:  return function(a, b) {return func(arguments)};
    case 3:  return function(a, b, c) {return func(arguments)};
    case 4:  return function(a, b, c, d) {return func(arguments)};
    case 5:  return function(a, b, c, d, e) {return func(arguments)};
    case 6:  return function(a, b, c, d, e, f) {return func(arguments)};
    case 7:  return function(a, b, c, d, e, f, g) {return func(arguments)};
    case 8:  return function(a, b, c, d, e, f, g, h) {return func(arguments)};
    case 9:  return function(a, b, c, d, e, f, g, h, i) {return func(arguments)};
  }
  // >= 10
  var args = [];
  for (var i = 0; i < n; ++i) {
    args.push('a' + i);
  }
  args.push('return this(arguments);');
  return Function.prototype.constructor.apply(this, args).bind(func);
};

var C_proto = {
  __proto__: Function,
  constructor: function (node, locals) {
    var obj = function() {
      obj.invoke.apply(obj, arguments);
    };
    obj.__proto__ = C_proto;
    obj.node = node;
    obj.locals = locals;
    return obj;
  },
  double_invoke_protect: function() {
    if (this.invoked) {
      throw 'continue.js: c() invoked double times';
    }
    this.invoked = true;
  },
  invoke: function () {
    this.node.catch_exception = false;
    this.double_invoke_protect();
    this.locals.args = arguments;
    this.node.next.invoke(this.locals);
  },
  assign: function() {
    var keys = arguments;
    var c = this;
    var locals = c.locals;
    var func = function(args) {
      for (var i = 0; i < keys.length; ++i) {
        if (keys[i] !== null) {
          locals[keys[i]] = args[i];
        }
      }
      c.invoke.apply(c, args);
    };
    return func_length_n(keys.length, func);
  },
  assign2: function() {
    var key_pairs = arguments;
    var c = this;
    var locals = c.locals;
    var func = function(args) {
      for (var i = 0; i < key_pairs.length / 2; ++i) {
        if (key_pairs[i * 2] !== null && key_pairs[i * 2 + 1] !== null) {
          key_pairs[i * 2][key_pairs[i * 2 + 1]] = args[i];
        }
      }
      c.invoke.apply(c, args);
    };
    return func_length_n(key_pairs.length / 2, func);
  },
  get fail () {
    return function(err) {
      this.locals.err = err === undefined ? 'error' : err;
      this.invoke();
    }.bind(this);
  }
};
var C = C_proto.constructor;
C.prototype = C_proto;

var Block_proto = {
  __proto__: Function,
  constructor: function () {
    var obj = function() {
      return obj.then.apply(obj, arguments);
    };
    obj.__proto__ = Block_proto;
    return obj;
  },
  genNext: function() {
    this.next = Block_proto.constructor();
    this.next.first = this.first;
    return this.next;
  },
  then: function(func) {
    this.invoke = function(locals) {
      // catch_exception closed when c() invoked.
      this.catch_exception = true;
      try {
        var last_err = locals.err;
        if (last_err !== undefined && last_err !== null) {
          if (func.length >= 3) {
            locals.err = null;
            func(last_err, C(this, locals), locals);
          } else {
            this.next.invoke(locals);
          }
        } else {
          if (func.length < 3) {
            func(C(this, locals), locals);
          } else {
            this.next.invoke(locals);
          }
        }
      } catch (err) {
        if (this.catch_exception) {
          locals.err = err;
          this.next.invoke(locals);
        } else {
          throw err;
        }
      }
    };
    return this.genNext();
  },
  end: function() {
    var args = Array.prototype.slice.call(arguments);
    var silent = args.length > 0 && args[0].constructor === Boolean ? args.shift() : false;
    var func = args.pop();
    var assign = args;

    this.invoke = function(locals) {
      if (!silent && locals.err !== undefined && locals.err !== null) {
        throw locals.err;
      }
      if (func) {
        var args = [];
        assign.forEach(function (a) {
          args.push(locals[a]);
        });
        func.apply(this, args);
      }
    };

    this.first.invoke({});
  },
  done: function(func) {
    this.invoke = function(locals) {
      func(locals.err, locals);
    };

    this.first.invoke({});
  }
};
var Block = Block_proto.constructor;
Block.prototype = Block_proto;

var s_proxy = function(name) {
  return function() {
    var b = Block();
    b.first = b;
    return b[name].apply(b, arguments);
  };
};
var S = s_proxy('then');
S.then = s_proxy('then');

module.exports = S;
<commit_msg>allow empty chain<commit_after>/* continue.js 1.x -- A very easy and clean async flow controller
 *
 * Copyright (C) 2015 Chizhong Jin
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD license.  See the LICENSE file for details.
 */

'use strict';

var func_length_n = function(n, func) {
  // black magic, here !!! help me, if you have better idea
  switch (n) {
    case 0:  return function() {return func(arguments)};
    case 1:  return function(a) {return func(arguments)};
    case 2:  return function(a, b) {return func(arguments)};
    case 3:  return function(a, b, c) {return func(arguments)};
    case 4:  return function(a, b, c, d) {return func(arguments)};
    case 5:  return function(a, b, c, d, e) {return func(arguments)};
    case 6:  return function(a, b, c, d, e, f) {return func(arguments)};
    case 7:  return function(a, b, c, d, e, f, g) {return func(arguments)};
    case 8:  return function(a, b, c, d, e, f, g, h) {return func(arguments)};
    case 9:  return function(a, b, c, d, e, f, g, h, i) {return func(arguments)};
  }
  // >= 10
  var args = [];
  for (var i = 0; i < n; ++i) {
    args.push('a' + i);
  }
  args.push('return this(arguments);');
  return Function.prototype.constructor.apply(this, args).bind(func);
};

var C_proto = {
  __proto__: Function,
  constructor: function (node, locals) {
    var obj = function() {
      obj.invoke.apply(obj, arguments);
    };
    obj.__proto__ = C_proto;
    obj.node = node;
    obj.locals = locals;
    return obj;
  },
  double_invoke_protect: function() {
    if (this.invoked) {
      throw 'continue.js: c() invoked double times';
    }
    this.invoked = true;
  },
  invoke: function () {
    this.node.catch_exception = false;
    this.double_invoke_protect();
    this.locals.args = arguments;
    this.node.next.invoke(this.locals);
  },
  assign: function() {
    var keys = arguments;
    var c = this;
    var locals = c.locals;
    var func = function(args) {
      for (var i = 0; i < keys.length; ++i) {
        if (keys[i] !== null) {
          locals[keys[i]] = args[i];
        }
      }
      c.invoke.apply(c, args);
    };
    return func_length_n(keys.length, func);
  },
  assign2: function() {
    var key_pairs = arguments;
    var c = this;
    var locals = c.locals;
    var func = function(args) {
      for (var i = 0; i < key_pairs.length / 2; ++i) {
        if (key_pairs[i * 2] !== null && key_pairs[i * 2 + 1] !== null) {
          key_pairs[i * 2][key_pairs[i * 2 + 1]] = args[i];
        }
      }
      c.invoke.apply(c, args);
    };
    return func_length_n(key_pairs.length / 2, func);
  },
  get fail () {
    return function(err) {
      this.locals.err = err === undefined ? 'error' : err;
      this.invoke();
    }.bind(this);
  }
};
var C = C_proto.constructor;
C.prototype = C_proto;

var Block_proto = {
  __proto__: Function,
  constructor: function () {
    var obj = function() {
      return obj.then.apply(obj, arguments);
    };
    obj.__proto__ = Block_proto;
    return obj;
  },
  genNext: function() {
    this.next = Block_proto.constructor();
    this.next.first = this.first;
    return this.next;
  },
  then: function(func) {
    this.invoke = function(locals) {
      // catch_exception closed when c() invoked.
      this.catch_exception = true;
      try {
        var last_err = locals.err;
        if (last_err !== undefined && last_err !== null) {
          if (func.length >= 3) {
            locals.err = null;
            func(last_err, C(this, locals), locals);
          } else {
            this.next.invoke(locals);
          }
        } else {
          if (func.length < 3) {
            func(C(this, locals), locals);
          } else {
            this.next.invoke(locals);
          }
        }
      } catch (err) {
        if (this.catch_exception) {
          locals.err = err;
          this.next.invoke(locals);
        } else {
          throw err;
        }
      }
    };
    return this.genNext();
  },
  end: function() {
    var args = Array.prototype.slice.call(arguments);
    var silent = args.length > 0 && args[0].constructor === Boolean ? args.shift() : false;
    var func = args.pop();
    var assign = args;

    this.invoke = function(locals) {
      if (!silent && locals.err !== undefined && locals.err !== null) {
        throw locals.err;
      }
      if (func) {
        var args = [];
        assign.forEach(function (a) {
          args.push(locals[a]);
        });
        func.apply(this, args);
      }
    };

    this.first.invoke({});
  },
  done: function(func) {
    this.invoke = function(locals) {
      func(locals.err, locals);
    };

    this.first.invoke({});
  }
};
var Block = Block_proto.constructor;
Block.prototype = Block_proto;

var s_proxy = function(name) {
  return function() {
    var b = Block();
    b.first = b;
    return b[name].apply(b, arguments);
  };
};
var S = s_proxy('then');
S.then = s_proxy('then');
S.end = s_proxy('end');
S.done = s_proxy('done');

module.exports = S;
<|endoftext|>


<commit_before>import React from 'react';
import {Platform} from 'react-native';
import {StateNavigator} from 'navigation';
import {NavigationStack} from 'navigation-react-native';
import Home from './Home';
import Notifications from './Notifications';
import Tabs from './Tabs';
import Tweet from './Tweet';
import Timeline from './Timeline';
import {getHome, getFollows, getTweet, getTimeline} from './data';

export default () => {
  const stateNavigator = new StateNavigator([
    {key: 'home', route: '{tab?}', defaults: {tab: 'home'}},
    {key: 'notifications', route: 'x/y'},
    {key: 'tweet', route: 'tweet/{id}', trackCrumbTrail: true, defaultTypes: {id: 'number'}},
    {key: 'timeline', route: 'timeline/{id}', trackCrumbTrail: true, defaultTypes: {id: 'number'}}
  ]);

  const buildStartUrl = url => {
    const {state, data} = stateNavigator.parseLink(url);
    let fluent = stateNavigator.fluent().navigate('home');
    if (state.key === 'home' && data.tab === 'notifications')
      stateNavigator.historyManager.addHistory(fluent.url, true);
    return fluent.navigate(state.key, data).url;
  };
  stateNavigator.configure(stateNavigator, new NavigationStack.HistoryManager(buildStartUrl));

  const {home, notifications, tweet, timeline} = stateNavigator.states;
  const HomeLayout = Platform.OS === 'ios' ? Home : Tabs;
  home.renderScene = ({tab}) => <HomeLayout tweets={getHome()} follows={getFollows()} tab={tab} />;
  notifications.renderScene = () => <Notifications follows={getFollows()} />;
  tweet.renderScene = ({id}) => <Tweet tweet={getTweet(id)}  />;
  timeline.renderScene = ({id}) => <Timeline timeline={getTimeline(id)}  />;

  if (Platform.OS !== 'web') stateNavigator.navigate('home');
  else stateNavigator.start();

  return stateNavigator;
}
<commit_msg>Created history manager only on web<commit_after>import React from 'react';
import {Platform} from 'react-native';
import {StateNavigator} from 'navigation';
import {NavigationStack} from 'navigation-react-native';
import Home from './Home';
import Notifications from './Notifications';
import Tabs from './Tabs';
import Tweet from './Tweet';
import Timeline from './Timeline';
import {getHome, getFollows, getTweet, getTimeline} from './data';

export default () => {
  const stateNavigator = new StateNavigator([
    {key: 'home', route: '{tab?}', defaults: {tab: 'home'}},
    {key: 'notifications', route: 'x/y'},
    {key: 'tweet', route: 'tweet/{id}', trackCrumbTrail: true, defaultTypes: {id: 'number'}},
    {key: 'timeline', route: 'timeline/{id}', trackCrumbTrail: true, defaultTypes: {id: 'number'}}
  ]);

  if (Platform.OS === 'web') {
    const buildStartUrl = url => {
      const {state, data} = stateNavigator.parseLink(url);
      let fluent = stateNavigator.fluent().navigate('home');
      if (state.key === 'home' && data.tab === 'notifications')
        stateNavigator.historyManager.addHistory(fluent.url, true);
      return fluent.navigate(state.key, data).url;
    };
    stateNavigator.configure(stateNavigator, new NavigationStack.HistoryManager(buildStartUrl));
  }

  const {home, notifications, tweet, timeline} = stateNavigator.states;
  const HomeLayout = Platform.OS === 'ios' ? Home : Tabs;
  home.renderScene = ({tab}) => <HomeLayout tweets={getHome()} follows={getFollows()} tab={tab} />;
  notifications.renderScene = () => <Notifications follows={getFollows()} />;
  tweet.renderScene = ({id}) => <Tweet tweet={getTweet(id)}  />;
  timeline.renderScene = ({id}) => <Timeline timeline={getTimeline(id)}  />;

  if (Platform.OS !== 'web') stateNavigator.navigate('home');
  else stateNavigator.start();

  return stateNavigator;
}
<|endoftext|>


<commit_before>'use babel'

let Disposable = null
let CompositeDisposable = null
let ClangTidyCommand = null
let ClangTidy = null
let ClangArguments = null
let ClangTidyLinter = null
let ClangTidyAutocompleteProvider = null
let ClangRenameCommand
let ClangRenameView = null

function initialize () {
  if (!Disposable || !CompositeDisposable) {
    const atom = require('atom')
    Disposable = atom.Disposable
    CompositeDisposable = atom.CompositeDisposable
  }
  if (!ClangTidyCommand) ClangTidyCommand = require('./clang-tidy-command')
  if (!ClangTidy) ClangTidy = require('./clang-tidy')
  if (!ClangArguments) ClangArguments = require('./clang-arguments')
  if (!ClangTidyLinter) ClangTidyLinter = require('./clang-tidy-linter')
  if (!ClangTidyAutocompleteProvider) {
    ClangTidyAutocompleteProvider = require('./clang-tidy-autocomplete-provider')
  }
  if (!ClangRenameCommand) ClangRenameCommand = require('./clang-rename-command')
  if (!ClangRenameView) ClangRenameView = require('./clang-rename-view')
}

export default {
  subscriptions: null,

  grammars: [
    'source.c',
    'source.cpp',
    'source.objc',
    'source.objcpp'
  ],

  config: {
    clangTidyCommand: {
      title: 'clang-tidy command',
      type: 'string',
      default: 'clang-tidy'
    },
    clangRenameCommand: {
      title: 'clang-rename command',
      type: 'string',
      default: 'clang-rename'
    },
    clangCppFlags: {
      title: 'clang flags for C++',
      type: [],
      items: { type: 'string' }
    },
    clangCFlags: {
      title: 'clang flags for C',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    clangObjectiveCppFlags: {
      title: 'clang flags for Objective C++',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    clangObjectiveCFlags: {
      title: 'clang flags for Objective C',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    buildPath: {
      title: 'the build pass used in clang-tools',
      type: 'string',
      default: 'compile_commands.json'
    }
  },

  activate (state) {
    initialize()

    this.clangTidyCommand = new ClangTidyCommand(
      () => atom.config.get('clang-tools-extra.clangTidyCommand'),
      () => atom.config.get('clang-tools-extra.buildPath')
    )
    this.clangRenameCommand = new ClangRenameCommand(
      () => atom.config.get('clang-tools-extra.clangRenameCommand'),
      () => atom.config.get('clang-tools-extra.buildPath')
    )

    this.clangArguments = new ClangArguments({
      getClangCFlags: () => atom.config.get('clang-tools-extra.clangCFlags').split(/\s+/),
      getClangCppFlags: () => atom.config.get('clang-tools-extra.clangCppFlags').split(/\s+/),
      getClangObjectiveCFlags: () => atom.config.get('clang-tools-extra.clangObjectiveCFlags').split(/\s+/),
      getClangObjectiveCppFlags: () => atom.config.get('clang-tools-extra.clangObjectiveCppFlags').split(/\s+/)
    })

    this.clangTidy = new ClangTidy(this.clangTidyCommand, this.clangArguments)

    this.clangTidyLinter = new ClangTidyLinter(this.clangTidy)

    this.autocompleteProvider = new ClangTidyAutocompleteProvider(this.clangTidy)
    this.autocompleteProvider.selector = this.grammars.map(elem => `.${elem}`).join(','),
    this.autocompleteProvider.inclusionPriority = 1

    this.subscriptions = new CompositeDisposable()

    this.clangRenameView = new ClangRenameView(this.clangRenameCommand, this.clangArguments)
    this.subscriptions.add(new Disposable(() => this.clangRenameView.destroy()))

    // Register the commands
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:apply-all-fixes': () => this.applyAllFixes()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:rename': () => this.showRenameView()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:confirm-rename': () => this.invokeRenameCommand()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'core:cancel': event => {
          const editor = atom.workspace.getActiveTextEditor()
          if (!editor) return

          if (this.clangRenameView.isCursorIn(editor)) {
            this.clangRenameView.cancel(editor)
            event.stopImmediatePropagation()
          }
        }
      })
    )
  },

  deactivate () {
    this.subscriptions.dispose()
  },

  serialize () {
    return {}
  },

  applyAllFixes () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    this.clangTidy.applyAllFixes(editor)
  },

  showRenameView () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    this.clangRenameView.invoke(editor)
  },

  invokeRenameCommand () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    if (this.clangRenameView.isCursorIn(editor)) {
      this.clangRenameView.apply(editor)
    }
  },

  provide () {
    return this.autocompleteProvider
  },

  provideLinter () {
    const provider = {
      name: 'clang-tools-extra',
      grammarScopes: this.grammars,
      scope: 'file',
      lintOnFly: false,
      lint: (textEditor) => this.clangTidyLinter.lint(textEditor)
    }
    return provider
  }
}
<commit_msg>Add notification<commit_after>'use babel'

let Disposable = null
let CompositeDisposable = null
let ClangTidyCommand = null
let ClangTidy = null
let ClangArguments = null
let ClangTidyLinter = null
let ClangTidyAutocompleteProvider = null
let ClangRenameCommand
let ClangRenameView = null

function initialize () {
  if (!Disposable || !CompositeDisposable) {
    const atom = require('atom')
    Disposable = atom.Disposable
    CompositeDisposable = atom.CompositeDisposable
  }
  if (!ClangTidyCommand) ClangTidyCommand = require('./clang-tidy-command')
  if (!ClangTidy) ClangTidy = require('./clang-tidy')
  if (!ClangArguments) ClangArguments = require('./clang-arguments')
  if (!ClangTidyLinter) ClangTidyLinter = require('./clang-tidy-linter')
  if (!ClangTidyAutocompleteProvider) {
    ClangTidyAutocompleteProvider = require('./clang-tidy-autocomplete-provider')
  }
  if (!ClangRenameCommand) ClangRenameCommand = require('./clang-rename-command')
  if (!ClangRenameView) ClangRenameView = require('./clang-rename-view')
}

export default {
  subscriptions: null,

  grammars: [
    'source.c',
    'source.cpp',
    'source.objc',
    'source.objcpp'
  ],

  config: {
    clangTidyCommand: {
      title: 'clang-tidy command',
      type: 'string',
      default: 'clang-tidy'
    },
    clangRenameCommand: {
      title: 'clang-rename command',
      type: 'string',
      default: 'clang-rename'
    },
    clangCppFlags: {
      title: 'clang flags for C++',
      type: [],
      items: { type: 'string' }
    },
    clangCFlags: {
      title: 'clang flags for C',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    clangObjectiveCppFlags: {
      title: 'clang flags for Objective C++',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    clangObjectiveCFlags: {
      title: 'clang flags for Objective C',
      type: 'array',
      default: [],
      items: { type: 'string' }
    },
    buildPath: {
      title: 'the build pass used in clang-tools',
      type: 'string',
      default: 'compile_commands.json'
    }
  },

  activate (state) {
    initialize()

    this.clangTidyCommand = new ClangTidyCommand(
      () => atom.config.get('clang-tools-extra.clangTidyCommand'),
      () => atom.config.get('clang-tools-extra.buildPath')
    )
    this.clangRenameCommand = new ClangRenameCommand(
      () => atom.config.get('clang-tools-extra.clangRenameCommand'),
      () => atom.config.get('clang-tools-extra.buildPath')
    )

    this.clangArguments = new ClangArguments({
      getClangCFlags: () => atom.config.get('clang-tools-extra.clangCFlags').split(/\s+/),
      getClangCppFlags: () => atom.config.get('clang-tools-extra.clangCppFlags').split(/\s+/),
      getClangObjectiveCFlags: () => atom.config.get('clang-tools-extra.clangObjectiveCFlags').split(/\s+/),
      getClangObjectiveCppFlags: () => atom.config.get('clang-tools-extra.clangObjectiveCppFlags').split(/\s+/)
    })

    this.clangTidy = new ClangTidy(this.clangTidyCommand, this.clangArguments)

    this.clangTidyLinter = new ClangTidyLinter(this.clangTidy)

    this.autocompleteProvider = new ClangTidyAutocompleteProvider(this.clangTidy)
    this.autocompleteProvider.selector = this.grammars.map(elem => `.${elem}`).join(','),
    this.autocompleteProvider.inclusionPriority = 1

    this.subscriptions = new CompositeDisposable()

    this.clangRenameView = new ClangRenameView(this.clangRenameCommand, this.clangArguments)
    this.subscriptions.add(new Disposable(() => this.clangRenameView.destroy()))

    // Register the commands
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:apply-all-fixes': () => this.applyAllFixes()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:rename': () => this.showRenameView()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'clang-tools-extra:confirm-rename': () => this.invokeRenameCommand()
      })
    )
    this.subscriptions.add(
      atom.commands.add('atom-text-editor', {
        'core:cancel': event => {
          const editor = atom.workspace.getActiveTextEditor()
          if (!editor) return

          if (this.clangRenameView.isCursorIn(editor)) {
            this.clangRenameView.cancel(editor)
            event.stopImmediatePropagation()
          }
        }
      })
    )
  },

  deactivate () {
    this.subscriptions.dispose()
  },

  serialize () {
    return {}
  },

  applyAllFixes () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    this.clangTidy.applyAllFixes(editor).catch(
      reason => atom.notifications.addError('Error in apply-all-fixes', {
        detail: reason
      })
    )
  },

  showRenameView () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    this.clangRenameView.invoke(editor)
  },

  invokeRenameCommand () {
    const editor = atom.workspace.getActiveTextEditor()
    if (!editor) return

    if (this.clangRenameView.isCursorIn(editor)) {
      this.clangRenameView.apply(editor).catch(
        reason => atom.notifications.addError('Error in confirm-rename', {
          detail: reason
        })
      )
    }
  },

  provide () {
    return this.autocompleteProvider
  },

  provideLinter () {
    const provider = {
      name: 'clang-tools-extra',
      grammarScopes: this.grammars,
      scope: 'file',
      lintOnFly: false,
      lint: (textEditor) => this.clangTidyLinter.lint(textEditor)
    }
    return provider
  }
}
<|endoftext|>


<commit_before>'use strict';

const Immutable = require('immutable');
const url = require('url');

const DataSnapshot = require('./data-snapshot');
const Reference = require('./reference');
const getPaths = require('./get-paths');
const getValue = require('./get-value');
const queryChildren = require('./query-children');
const reconciliation = require('./reconciliation');
const validateDataTree = require('./validate-data-tree');
const { UPDATES, eventTypes } = require('./constants');

const getInitialCallbackSettings = () =>
  eventTypes.reduce((acc, key) => {
    acc[key] = {};
    return acc;
  }, {});

class Database {
  constructor(app) {
    this.app = app;

    this._online = true;
    this._data = Immutable.fromJS(null);
    this._callbacks = getInitialCallbackSettings();

    // this.goOffline = () => {};

    // this.goOnline = () => {};

    this.ref = path => new Reference(app, path);

    this.refFromURL = (ref) => {
      const refUrl = url.parse(ref);

      const base = `${refUrl.protocol}//${refUrl.hostname}`;
      if (base !== app.options.databaseUrl) {
        throw new Error(`Invalid url ${ref}. Hostname should be ${app.options.databaseUrl}`);
      }

      return this.ref(refUrl.pathname);
    };

    // TESTING UTILITIES
    this.setMockData = (data) => {
      this._data = Immutable.fromJS(validateDataTree(data));
    };
    this.purgeMockData = () => {
      this._data = Immutable.fromJS(null);
    };
    this.getMockData = () => this._data.toJS();

    // INTERNALS
    // fetches data as an ImmutableJS object
    this._getData = () => this._data;

    // transforms the data correctly into ImmutableJS
    this._makeImmutable = (data) => {
      const parsedData = validateDataTree(data);
      return Immutable.fromJS(parsedData);
    };

    this._registerCallback = (eventType, path, index, callback, config) => {
      if (!this._callbacks[eventType][path]) {
        this._callbacks[eventType][path] = {};
      }
      this._callbacks[eventType][path][index] = { eventType, path, index, callback, config };

      /* FIXME
      // fire initial callbacks
      if (eventType === 'value') {
        this._fireEvent(eventType, path, path, index);
      }

      if (eventType === 'child_added') {
        const data = this._getValue(getPaths(path));

        // should give correct iteration order
        const children = queryChildren(data, config);
        children.forEach((key) => {
          this._fireEvent(eventType, path, `${path}/${key}`, index);
        });
      }
      */
    };

    this._unregisterCallback = (eventType, location, index) => {
      if (!eventType) {
        this._callbacks = getInitialCallbackSettings();
        return;
      }
      if (!location) {
        this._callbacks[eventType] = {};
        return;
      }
      delete this._callbacks[eventType][location][index];
    };

    this._getValue = keys => getValue(this._data, keys);

    this._traverseUpdateTree = (prev, next, updates) => {
      if (updates === null) {
        return;
      }

      Object.keys(updates).forEach((key) => {
        this._traverseUpdateTree(prev, next, updates[key]);
      });

      const updateInfo = updates[UPDATES];
      const { path, location } = updateInfo;
      const prevNested = getValue(prev, location);
      const nextNested = getValue(next, location);
      const reference = new Reference(app, path);
      // value event
      this._fireValueHandlers(nextNested, path, reference);
      // child_* events
      this._fireChildAddedHandlers(prevNested, nextNested, path, reference);
      this._fireChildChangedHandlers(prevNested, nextNested, path, reference, updates);
      this._fireChildRemovedHandlers(prevNested, nextNested, path, reference);
    };

    this._callbackGenerator = function* callbackGenerator(eventType, path) {
      // get all callbacks for this event and path
      const callbackMap = this._callbacks[eventType][path];

      // get list of indexes for the callbacks
      const callbackIndexes = Object.keys(callbackMap);

      for (const index of callbackIndexes) { // eslint-disable-line no-restricted-syntax
        yield callbackMap[index];
      }
    };

    this._fireValueHandlers = (nextNested, path, reference) => {
      const callbackGenerator = this._callbackGenerator('value', path);

      for (const callbackInfo of callbackGenerator) { // eslint-disable-line no-restricted-syntax
        // get callback and sort/filter config
        const { callback, config } = callbackInfo;

        // get data snapshot
        const dataSnapshot = new DataSnapshot(reference, nextNested, config);

        callback(dataSnapshot);
      }
    };

    this._childInfoGenerator = function* childInfoGenerator(
      eventType,
      prevNested,
      nextNested,
      path,
      reference) {
      const callbackGenerator = this._callbackGenerator(eventType, path);

      // initialize list to store data snapshots of children and avoid
      // redoing the same work over and over again
      const dataSnapshots = {};

      // the data passed in the snapshots depends on the event type
      let data;
      if (eventType === 'child_added') {
        data = nextNested;
      } else if (eventType === 'child_removed') {
        data = prevNested;
      }

      // lazily create snapshot to avoid heavy re-computation
      const getChildSnapshot = (key) => {
        if (!dataSnapshots[key]) {
          const refChild = reference.child(key);
          dataSnapshots[key] = new DataSnapshot(refChild, data);
        }

        return dataSnapshots[key];
      };

      for (const callbackInfo of callbackGenerator) { // eslint-disable-line no-restricted-syntax
        // get callback and sort/filter config
        const { callback, config } = callbackInfo;

        // get children of previous state
        const prevChildrenKeys = queryChildren(prevNested, config);

        // get children of next state
        const nextChildrenKeys = queryChildren(nextNested, config);

        yield { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback };
      }
    };

    this._fireChildAddedHandlers = (prevNested, nextNested, path, reference) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_added',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep previous state keys in a hash map for O(1) lookups
        const prevChildrenKeyMap = prevChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        let prevChildKey = null;
        nextChildrenKeys.forEach((key) => {
          // find if it was in the previous state, if not fire the
          // child_added event handler
          if (!prevChildrenKeyMap[key]) {
            // fire the callback
            callback(getChildSnapshot(key), prevChildKey);
          }

          // update key of previous child
          prevChildKey = key;
        });
      }
    };

    this._fireChildChangedHandlers = (prevNested, nextNested, path, reference, updates) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_changed',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep previous state keys in a hash map for O(1) lookups
        const prevChildrenKeyMap = prevChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        let prevChildKey = null;
        nextChildrenKeys.forEach((key) => {
          // find if it was in the previous state
          // if it was check if there is an updates in the child's state
          if (prevChildrenKeyMap[key] && updates[key] && updates[key][UPDATES]) {
            // fire the callback
            callback(getChildSnapshot(key), prevChildKey);
          }

          // update key of previous child
          prevChildKey = key;
        });
      }
    };

    this._fireChildRemovedHandlers = (prevNested, nextNested, path, reference) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_removed',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep next state keys in a hash map for O(1) lookups
        const nextChildrenKeyMap = nextChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        prevChildrenKeys.forEach((key) => {
          // find if it is in the next state, if not fire the
          // child_removed event handler
          if (!nextChildrenKeyMap[key]) {
            // fire the callback
            callback(getChildSnapshot(key));
          }
        });
      }
    };

    // Calling this function implies the caller has verified that the inputted
    // data tree is valid.
    // Sets ImmutableJS object as data
    this._setData = (data) => {
      const prev = this._data;
      const next = data;
      const updates = reconciliation(prev, next);
      this._data = data;

      if (updates === null) {
        return null;
      }

      this._traverseUpdateTree(prev, next, updates);

      return data;
    };
  }
}

module.exports = Database;
<commit_msg>chore: removed useless require<commit_after>'use strict';

const Immutable = require('immutable');
const url = require('url');

const DataSnapshot = require('./data-snapshot');
const Reference = require('./reference');
const getValue = require('./get-value');
const queryChildren = require('./query-children');
const reconciliation = require('./reconciliation');
const validateDataTree = require('./validate-data-tree');
const { UPDATES, eventTypes } = require('./constants');

const getInitialCallbackSettings = () =>
  eventTypes.reduce((acc, key) => {
    acc[key] = {};
    return acc;
  }, {});

class Database {
  constructor(app) {
    this.app = app;

    this._online = true;
    this._data = Immutable.fromJS(null);
    this._callbacks = getInitialCallbackSettings();

    // this.goOffline = () => {};

    // this.goOnline = () => {};

    this.ref = path => new Reference(app, path);

    this.refFromURL = (ref) => {
      const refUrl = url.parse(ref);

      const base = `${refUrl.protocol}//${refUrl.hostname}`;
      if (base !== app.options.databaseUrl) {
        throw new Error(`Invalid url ${ref}. Hostname should be ${app.options.databaseUrl}`);
      }

      return this.ref(refUrl.pathname);
    };

    // TESTING UTILITIES
    this.setMockData = (data) => {
      this._data = Immutable.fromJS(validateDataTree(data));
    };
    this.purgeMockData = () => {
      this._data = Immutable.fromJS(null);
    };
    this.getMockData = () => this._data.toJS();

    // INTERNALS
    // fetches data as an ImmutableJS object
    this._getData = () => this._data;

    // transforms the data correctly into ImmutableJS
    this._makeImmutable = (data) => {
      const parsedData = validateDataTree(data);
      return Immutable.fromJS(parsedData);
    };

    this._registerCallback = (eventType, path, index, callback, config) => {
      if (!this._callbacks[eventType][path]) {
        this._callbacks[eventType][path] = {};
      }
      this._callbacks[eventType][path][index] = { eventType, path, index, callback, config };

      /* FIXME
      // fire initial callbacks
      if (eventType === 'value') {
        this._fireEvent(eventType, path, path, index);
      }

      if (eventType === 'child_added') {
        const data = this._getValue(getPaths(path));

        // should give correct iteration order
        const children = queryChildren(data, config);
        children.forEach((key) => {
          this._fireEvent(eventType, path, `${path}/${key}`, index);
        });
      }
      */
    };

    this._unregisterCallback = (eventType, location, index) => {
      if (!eventType) {
        this._callbacks = getInitialCallbackSettings();
        return;
      }
      if (!location) {
        this._callbacks[eventType] = {};
        return;
      }
      delete this._callbacks[eventType][location][index];
    };

    this._getValue = keys => getValue(this._data, keys);

    this._traverseUpdateTree = (prev, next, updates) => {
      if (updates === null) {
        return;
      }

      Object.keys(updates).forEach((key) => {
        this._traverseUpdateTree(prev, next, updates[key]);
      });

      const updateInfo = updates[UPDATES];
      const { path, location } = updateInfo;
      const prevNested = getValue(prev, location);
      const nextNested = getValue(next, location);
      const reference = new Reference(app, path);
      // value event
      this._fireValueHandlers(nextNested, path, reference);
      // child_* events
      this._fireChildAddedHandlers(prevNested, nextNested, path, reference);
      this._fireChildChangedHandlers(prevNested, nextNested, path, reference, updates);
      this._fireChildRemovedHandlers(prevNested, nextNested, path, reference);
    };

    this._callbackGenerator = function* callbackGenerator(eventType, path) {
      // get all callbacks for this event and path
      const callbackMap = this._callbacks[eventType][path];

      // get list of indexes for the callbacks
      const callbackIndexes = Object.keys(callbackMap);

      for (const index of callbackIndexes) { // eslint-disable-line no-restricted-syntax
        yield callbackMap[index];
      }
    };

    this._fireValueHandlers = (nextNested, path, reference) => {
      const callbackGenerator = this._callbackGenerator('value', path);

      for (const callbackInfo of callbackGenerator) { // eslint-disable-line no-restricted-syntax
        // get callback and sort/filter config
        const { callback, config } = callbackInfo;

        // get data snapshot
        const dataSnapshot = new DataSnapshot(reference, nextNested, config);

        callback(dataSnapshot);
      }
    };

    this._childInfoGenerator = function* childInfoGenerator(
      eventType,
      prevNested,
      nextNested,
      path,
      reference) {
      const callbackGenerator = this._callbackGenerator(eventType, path);

      // initialize list to store data snapshots of children and avoid
      // redoing the same work over and over again
      const dataSnapshots = {};

      // the data passed in the snapshots depends on the event type
      let data;
      if (eventType === 'child_added') {
        data = nextNested;
      } else if (eventType === 'child_removed') {
        data = prevNested;
      }

      // lazily create snapshot to avoid heavy re-computation
      const getChildSnapshot = (key) => {
        if (!dataSnapshots[key]) {
          const refChild = reference.child(key);
          dataSnapshots[key] = new DataSnapshot(refChild, data);
        }

        return dataSnapshots[key];
      };

      for (const callbackInfo of callbackGenerator) { // eslint-disable-line no-restricted-syntax
        // get callback and sort/filter config
        const { callback, config } = callbackInfo;

        // get children of previous state
        const prevChildrenKeys = queryChildren(prevNested, config);

        // get children of next state
        const nextChildrenKeys = queryChildren(nextNested, config);

        yield { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback };
      }
    };

    this._fireChildAddedHandlers = (prevNested, nextNested, path, reference) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_added',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep previous state keys in a hash map for O(1) lookups
        const prevChildrenKeyMap = prevChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        let prevChildKey = null;
        nextChildrenKeys.forEach((key) => {
          // find if it was in the previous state, if not fire the
          // child_added event handler
          if (!prevChildrenKeyMap[key]) {
            // fire the callback
            callback(getChildSnapshot(key), prevChildKey);
          }

          // update key of previous child
          prevChildKey = key;
        });
      }
    };

    this._fireChildChangedHandlers = (prevNested, nextNested, path, reference, updates) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_changed',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep previous state keys in a hash map for O(1) lookups
        const prevChildrenKeyMap = prevChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        let prevChildKey = null;
        nextChildrenKeys.forEach((key) => {
          // find if it was in the previous state
          // if it was check if there is an updates in the child's state
          if (prevChildrenKeyMap[key] && updates[key] && updates[key][UPDATES]) {
            // fire the callback
            callback(getChildSnapshot(key), prevChildKey);
          }

          // update key of previous child
          prevChildKey = key;
        });
      }
    };

    this._fireChildRemovedHandlers = (prevNested, nextNested, path, reference) => {
      const childInfoGenerator = this._childInfoGenerator(
        'child_removed',
        prevNested,
        nextNested,
        path,
        reference);

      for (const childInfo of childInfoGenerator) { // eslint-disable-line no-restricted-syntax
        const { prevChildrenKeys, nextChildrenKeys, getChildSnapshot, callback } = childInfo;

        // keep next state keys in a hash map for O(1) lookups
        const nextChildrenKeyMap = nextChildrenKeys.reduce((acc, key) => {
          acc[key] = true;
          return acc;
        }, {});

        prevChildrenKeys.forEach((key) => {
          // find if it is in the next state, if not fire the
          // child_removed event handler
          if (!nextChildrenKeyMap[key]) {
            // fire the callback
            callback(getChildSnapshot(key));
          }
        });
      }
    };

    // Calling this function implies the caller has verified that the inputted
    // data tree is valid.
    // Sets ImmutableJS object as data
    this._setData = (data) => {
      const prev = this._data;
      const next = data;
      const updates = reconciliation(prev, next);
      this._data = data;

      if (updates === null) {
        return null;
      }

      this._traverseUpdateTree(prev, next, updates);

      return data;
    };
  }
}

module.exports = Database;
<|endoftext|>