<commit_before>define(["js/core/EventDispatcher", "js/lib/parser", "underscore"], function (EventDispatcher, Parser, _) {

    /**
     * Returns false if path includes function
     * @param path
     */
    var pathToString = function (path) {
        var str = [];
        for (var i = 0; i < path.length; i++) {
            var el = path[i];
            if (el.type == TYPE_VAR) {
                str.push(el.name);
            } else {
                return false;
            }
        }
        return str.join(".");
    };

    var bindingsDestroyed = 0,
        undefined;

    var Bindable;
    var Binding = EventDispatcher.inherit("js.core.Binding",
        /** @lends Binding */
        {
            defaults: {
                event: 'change',
                path: null,
                twoWay: false
            },

            ctor: function (attributes) {
                if(!Bindable){
                    try {
                        Bindable = requirejs('js/core/Bindable');
                    } catch(e) {
                        Bindable = null;
                    }
                }
                this.callBase();
                this.$targets = [];
                this.$ = attributes;
                _.defaults(this.$,this.defaults);

                this.initialize();
            },

            transform: function (val) {
                return val;
            },

            transformBack: function (val) {
                return val;
            },

            initialize: function () {
                this._checkAttributes();
                this.$parameters = [];
                this.$events = [];
                this.$subBinding = null;

                if (!this.$.rootScope) {
                    this.$.rootScope = this;
                }
                var scope = this.$.scope;
                if(_.isString(this.$.path)){
                    this.$.path = Parser.parse(this.$.path,'path');
                }
                // split up first key
                this.$.key = this.$.path[0];
                var self = this;

                if(!this.$.parent){
                    // destroy yourself on target or scope destroy
                    scope.bind('destroy', this.destroy, this);

                    this.$.root = this;
                }

                if (this.$.key.type == TYPE_FNC) {
                    var fncName = this.$.key.name, parameters = this.$.key.parameter;

                    if (_.isFunction(scope[fncName])) {
                        var fnc = scope[fncName],
                            events = [];

                        fnc._attributes = this._getOnChangeAttributesForFnc(scope, fnc);

                        if (fnc._attributes && fnc._attributes.length > 0) {
                            this.$.scope.bind("change", this._changeCallback, this);
                            this.$events.push({eventType: "change", callback: this._changeCallback});
                        }

                        fnc._events = this._getEventsForFnc(scope, fnc);
                        if (fnc._events) {
                            events = fnc._events;
                        } else {
                            events = [];
                        }

                        var event, path;
                        for (var i = 0; i < events.length; i++) {
                            event = events[i];
                            scope.bind(event, this._callback, this);
                            this.$events.push({eventType: event, callback: this._callback});
                        }

                        // TODO: remove this closure
                        var cb = function () {
                            self._callback();
                        };

                        var para;
                        this.$parameters = [];
                        for (var j = 0; j < parameters.length; j++) {
                            para = parameters[j];
                            // if we have an array, it's a path
                            if(para instanceof Array) {
                                para = {
                                    path: para,
                                    type: TYPE_NORMAL,
                                    parent: null
                                };
                            }
                            if(_.isObject(para)){
                                para = this.$.bindingCreator.create(para, this.$.target, cb);
                            }
                            this.$parameters.push(para);

                        }
                        this.$.fnc = fnc;
                        this.$.fnc.trigger = function () {
                            self.triggerBinding();
                        };
                    }

                } else {
                    this.$.event = "change:" + this.$.key.name;
                    this.$events.push({eventType: this.$.event, callback: this._callback});
                    // on change of this key
                    scope.bind(this.$.event, this._callback, this);
                }

                if (this.$.twoWay === true) {
                    this.$.targetEvent = 'change:' + this.$.targetKey;
                    if(this.$.path.length === 1){
                        this.$.target.bind(this.$.targetEvent, this._revCallback, this);
                    }
                }

                if (!this.$.parent) {
                    this.addTarget(this.$.target, this.$.targetKey || this.$.callback);
                }

                this._createSubBinding();
            },

            _getOnChangeAttributesForFnc: function(scope, fnc) {
                var ret = [];
                if (fnc._attributes && fnc._attributes.length > 0) {

                    for (var x = 0; x < fnc._attributes.length; x++) {
                        var attribute = fnc._attributes[x];

                        var fncExtractor = /^(.+)\(\)$/;
                        var extract = fncExtractor.exec(attribute);

                        if (extract) {
                            var extendFunction = scope[extract[1]];
                            if (extendFunction) {
                                // copy all onChangeAttributes
                                ret = ret.concat(this._getOnChangeAttributesForFnc(scope, extendFunction));
                            }
                        } else {
                            ret.push(attribute);
                        }
                    }
                }

                return ret;

            },

            _getEventsForFnc: function(scope, fnc){
                var ret = [];
                if (fnc._events && fnc._events.length > 0) {

                    for (var x = 0; x < fnc._events.length; x++) {
                        var event = fnc._events[x];

                        var fncExtractor = /^(.+)\(\)$/;
                        var extract = fncExtractor.exec(event);

                        if (extract) {
                            var extendFunction = scope[extract[1]];
                            if (extendFunction) {
                                // copy all onChangeAttributes
                                ret = ret.concat(this._getEventsForFnc(scope, extendFunction));
                            }
                        } else {
                            if(!_.contains(ret,event)){
                                ret.push(event);
                            }
                        }
                    }
                }

                return ret;

            },

            _checkAttributes: function () {
                // check infrastructur
                if (!this.$.path) {
                    throw "No path defined!";
                }

                if (!this.$.scope) {
                    throw "No scope defined!";
                }

                if (this.$.twoWay) {
                    if (!this.$.target) {
                        throw "TwoWay binding, but no target defined!";
                    }
                    if (!this.$.target instanceof Bindable) {
                        throw "Target is not a Bindable!";
                    }

                    if (!this.$.targetKey) {
                        throw "TwoWay binding, but no target key defined!";
                    }

                }

                if(!this.$.bindingCreator){
                    this.$.bindingCreator = this;
                }

                if(this.$.transform){
                    this.transform = this.$.transform;
                }

                if (this.$.transformBack) {
                    this.transformBack = this.$.transformBack;
                }
            },
            _createSubBinding: function () {
                if (this.$.path.length > 1) {
                    var nScope;
                    if (this.$.fnc) {
                        nScope = this.getValue();
                    } else {
                        nScope = this.$.scope.$[this.$.key.name];
                    }
                    // if keys are left and has value && is bindable
                    // get value for first child
                    if(nScope instanceof Bindable){
                        // init new binding, which triggers this binding
                        this.$subBinding = new Binding({scope: nScope, path: this.$.path.slice(1), target: this.$.target, targetKey: this.$.targetKey, rootScope: this.$.rootScope, callback: this.$.callback, context: this.$.context, twoWay: this.$.twoWay, transform: this.$.transform, transformBack: this.$.transformBack, bindingCreator: this.$.bindingCreator, parent: this, root: this.$.root});
                    } else if(nScope instanceof Object){
                        // we have a object which is not bindable
                        this.$jsonObject = nScope;
                    } else if(_.isString(nScope)){
                        this.$jsonObject = nScope;
                    }

                }
            },
            _revCallback: function (e) {
                if (this.$.fnc) {
                    var params = this._getFncParameters();
                    params.unshift(e.$);
                    this.$.fnc.apply(this.$.scope, params);
                } else {
                    this.$.scope.set(pathToString(this.$.path), this.transformBack.call(this.$.scope, e.$, this.$originalValue));
                }
            },
            /**
             * This method is called, when the a change event of an function binding is triggered
             *
             * @param event
             * @private
             */
            _changeCallback: function (event) {

                if (!this.$) {
                    return;
                }

                for (var i = 0; i < this.$.fnc._attributes.length; i++) {
                    if (!_.isUndefined(event.$[this.$.fnc._attributes[i]])) {
                        this._callback();
                        return;
                    }
                }
            },
            /**
             * This method is called, when the value of the current binding changes.
             * It recreates the the subBinding and triggers the binding
             * @private
             */
            _callback: function () {

                if(!this.$){
                    return;
                }

                // remove subBindings!
                if (this.$subBinding) {
                    this.$subBinding.destroy();
                    this.$subBinding = null;
                }

                // try to create subBinding
                this._createSubBinding();
                this.triggerBinding();
            },

            /**
             * Unbinds all events and destroys subBinding...
             */
            _innerDestroy: function () {
                // binding already destroyed?
                if(!this.$){
                    return;
                }
                var e;
                for (var j = 0; j < this.$events.length; j++) {
                    e = this.$events[j];
                    this.$.scope.unbind(e.eventType, e.callback, this);
                }

                this.$.scope.unbind('destroy', this.destroy, this);

                if (this.$.twoWay === true) {
                    this.$.target.unbind(this.$.targetEvent, this._revCallback, this);
                }
                if (this.$subBinding) {
                    this.$subBinding.destroy();
                    this.$subBinding = null;
                }

                // destroy parameter bindings
                for (var i = 0; i < this.$parameters.length; i++) {
                    var par = this.$parameters[i];
                    if (par instanceof Binding) {
                        par.destroy();
                    }
                }
                this.$parameters = null;
                this.$.scope = this.$.target = this.$.callback = null;
                this.$ = null;
                this.$targets = null;
                this.callBase();

                bindingsDestroyed++;
                if(bindingsDestroyed === 500){
                    if (typeof(CollectGarbage) == "function") {
                        CollectGarbage();
                    }
                    bindingsDestroyed = 0;
                }
            },
            /**
             * Returns an array with values of all function parameters
             * @return {Array}
             * @private
             */
            _getFncParameters: function () {
                var parameters = [];
                for (var i = 0; i < this.$parameters.length; i++) {
                    var para = this.$parameters[i];
                    if (para instanceof Binding) {
                        para = para.getValue();
                    }
                    parameters.push(para);
                }
                return parameters;
            },
            /**
             * Returns the transformed value of the binding or null, if the binding path is not present
             * @return {*}
             */
            getValue: function () {
                if (this.$subBinding) {
                    this.$subBinding.invalidateValueCache();
                    return this.$subBinding.getValue();
                } else {
                    if(this.$cachedValue !== undefined){
                        return this.$cachedValue;
                    }
                    this.$originalValue = null;
                    if (this.$.fnc && !this.$jsonObject) {
                        this.$originalValue = this.$.fnc.apply(this.$.scope, this._getFncParameters());
                    } else if (this.$.path.length === 1) {
                        this.$originalValue = this.$.scope.get(this.$.key.name);
                    } else if(this.$jsonObject) {
                        this.$originalValue = this.$.scope.get(this.$jsonObject, this.$.path.slice(1));
                    }
                    this.$cachedValue = this.transform.call(this.$.scope, this.$originalValue);
                    return this.$cachedValue;
                }

            },

            invalidateValueCache: function(){
                this.$cachedValue = undefined;
            },

            /**
             * Returns the value in the context of the surrounding bindings
             * @return {*}
             */
            getContextValue: function () {
                if (this.$.context && this.$.context.length > 1) {
                    return Binding.contextToString(this.$.context);
                } else {
                    return this.getValue();
                }
            },
            /**
             * This method triggers the binding and syncs the target with the scope
             */
            triggerBinding: function () {
                this.invalidateValueCache();
                // get value
                var val = this.getContextValue();

                var target,
                    targets = this.$.root.getTargets();
                if(targets){
                    for(var i = 0; i < targets.length; i++){
                        target = targets[i];
                        if(target.key instanceof Function){
                            target.key.call(target.scope, val, this);
                        } else {
                            target.scope.set(target.key, val);
                        }
                    }
                }

            },
            toString: function () {
                return this.getValue();
            },

            getTargets: function(){
                return this.$targets;
            },

            /***
             *
             * @param {Bindable} targetScope
             * @param {String|Function} key
             */
            addTarget: function(targetScope, key){
                targetScope.bind('destroy', function(){
                    this.removeTarget(targetScope, key);
                },this);

                this.$targets.push({
                    scope: targetScope,
                    key: key
                });
            },
            /***
             *
             * @param {Bindable} targetScope
             * @param {String|Function} key
             */
            removeTarget: function(targetScope, key){
                var target;
                if(this.$targets){
                    for (var i = 0; i < this.$targets.length; i++) {
                        target = this.$targets[i];
                        if (target.key === key && target.scope === targetScope) {
                            this.$targets.splice(i,1);
                            break;
                        }
                    }
                    if(this.$targets.length === 0){
                        this.destroy();
                    }
                }
            },

            create: function(bindingDef, target, callback){
                var options = {
                    scope: this.$.scope,
                    target: target,
                    callback: callback,
                    path: bindingDef.path,
                    parent: bindingDef.parent,
                    twoWay : bindingDef.type === TYPE_TWOWAY, bindingCreator: this.$.bindingCreator};

                var fncEl;
                var fncScope;
                if(bindingDef.transform) {
                    fncEl = bindingDef.transform.pop();
                    fncScope = this.get(bindingDef.transform);
                    if(fncScope){
                        options.transform = fncScope[fncEl.name];
                    }
                }
                if(bindingDef.transformBack){
                    fncEl = bindingDef.transformBack.pop();
                    fncScope = this.get(bindingDef.transform);
                    if (fncScope) {
                        options.transformBack = fncScope[fncEl.name];
                    }
                }
                return new Binding(options);
            }
        });

    var TYPE_FNC = Binding.TYPE_FNC = "fnc";
    var TYPE_VAR = Binding.TYPE_VAR = "var";
    var TYPE_NORMAL = Binding.TYPE_NORMAL ="normal";
    var TYPE_STATIC = Binding.TYPE_STATIC ="static";
    var TYPE_TWOWAY = Binding.TYPE_TWOWAY ="twoWay";

    Binding.contextToString = function (context) {
        var str = "", el;
        for (var i = 0; i < context.length; i++) {
            el = context[i];
            if (el instanceof Binding) {
                el = el.getValue();
            }
            if (el !== null && typeof(el) !== "undefined") {
                str += el;
            }
        }
        return str;
    };

    return Binding;
});<commit_msg>fixed invalidation bug in binding value cache<commit_after>define(["js/core/EventDispatcher", "js/lib/parser", "underscore"], function (EventDispatcher, Parser, _) {

    /**
     * Returns false if path includes function
     * @param path
     */
    var pathToString = function (path) {
        var str = [];
        for (var i = 0; i < path.length; i++) {
            var el = path[i];
            if (el.type == TYPE_VAR) {
                str.push(el.name);
            } else {
                return false;
            }
        }
        return str.join(".");
    };

    var bindingsDestroyed = 0,
        undefined;

    var Bindable;
    var Binding = EventDispatcher.inherit("js.core.Binding",
        /** @lends Binding */
        {
            defaults: {
                event: 'change',
                path: null,
                twoWay: false
            },

            ctor: function (attributes) {
                if(!Bindable){
                    try {
                        Bindable = requirejs('js/core/Bindable');
                    } catch(e) {
                        Bindable = null;
                    }
                }
                this.callBase();
                this.$targets = [];
                this.$ = attributes;
                _.defaults(this.$,this.defaults);

                this.initialize();
            },

            transform: function (val) {
                return val;
            },

            transformBack: function (val) {
                return val;
            },

            initialize: function () {
                this._checkAttributes();
                this.$parameters = [];
                this.$events = [];
                this.$subBinding = null;

                if (!this.$.rootScope) {
                    this.$.rootScope = this;
                }
                var scope = this.$.scope;
                if(_.isString(this.$.path)){
                    this.$.path = Parser.parse(this.$.path,'path');
                }
                // split up first key
                this.$.key = this.$.path[0];
                var self = this;

                if(!this.$.parent){
                    // destroy yourself on target or scope destroy
                    scope.bind('destroy', this.destroy, this);

                    this.$.root = this;
                }

                if (this.$.key.type == TYPE_FNC) {
                    var fncName = this.$.key.name, parameters = this.$.key.parameter;

                    if (_.isFunction(scope[fncName])) {
                        var fnc = scope[fncName],
                            events = [];

                        fnc._attributes = this._getOnChangeAttributesForFnc(scope, fnc);

                        if (fnc._attributes && fnc._attributes.length > 0) {
                            this.$.scope.bind("change", this._changeCallback, this);
                            this.$events.push({eventType: "change", callback: this._changeCallback});
                        }

                        fnc._events = this._getEventsForFnc(scope, fnc);
                        if (fnc._events) {
                            events = fnc._events;
                        } else {
                            events = [];
                        }

                        var event, path;
                        for (var i = 0; i < events.length; i++) {
                            event = events[i];
                            scope.bind(event, this._callback, this);
                            this.$events.push({eventType: event, callback: this._callback});
                        }

                        // TODO: remove this closure
                        var cb = function () {
                            self._callback();
                        };

                        var para;
                        this.$parameters = [];
                        for (var j = 0; j < parameters.length; j++) {
                            para = parameters[j];
                            // if we have an array, it's a path
                            if(para instanceof Array) {
                                para = {
                                    path: para,
                                    type: TYPE_NORMAL,
                                    parent: null
                                };
                            }
                            if(_.isObject(para)){
                                para = this.$.bindingCreator.create(para, this.$.target, cb);
                            }
                            this.$parameters.push(para);

                        }
                        this.$.fnc = fnc;
                        this.$.fnc.trigger = function () {
                            self.triggerBinding();
                        };
                    }

                } else {
                    this.$.event = "change:" + this.$.key.name;
                    this.$events.push({eventType: this.$.event, callback: this._callback});
                    // on change of this key
                    scope.bind(this.$.event, this._callback, this);
                }

                if (this.$.twoWay === true) {
                    this.$.targetEvent = 'change:' + this.$.targetKey;
                    if(this.$.path.length === 1){
                        this.$.target.bind(this.$.targetEvent, this._revCallback, this);
                    }
                }

                if (!this.$.parent) {
                    this.addTarget(this.$.target, this.$.targetKey || this.$.callback);
                }

                this._createSubBinding();
            },

            _getOnChangeAttributesForFnc: function(scope, fnc) {
                var ret = [];
                if (fnc._attributes && fnc._attributes.length > 0) {

                    for (var x = 0; x < fnc._attributes.length; x++) {
                        var attribute = fnc._attributes[x];

                        var fncExtractor = /^(.+)\(\)$/;
                        var extract = fncExtractor.exec(attribute);

                        if (extract) {
                            var extendFunction = scope[extract[1]];
                            if (extendFunction) {
                                // copy all onChangeAttributes
                                ret = ret.concat(this._getOnChangeAttributesForFnc(scope, extendFunction));
                            }
                        } else {
                            ret.push(attribute);
                        }
                    }
                }

                return ret;

            },

            _getEventsForFnc: function(scope, fnc){
                var ret = [];
                if (fnc._events && fnc._events.length > 0) {

                    for (var x = 0; x < fnc._events.length; x++) {
                        var event = fnc._events[x];

                        var fncExtractor = /^(.+)\(\)$/;
                        var extract = fncExtractor.exec(event);

                        if (extract) {
                            var extendFunction = scope[extract[1]];
                            if (extendFunction) {
                                // copy all onChangeAttributes
                                ret = ret.concat(this._getEventsForFnc(scope, extendFunction));
                            }
                        } else {
                            if(!_.contains(ret,event)){
                                ret.push(event);
                            }
                        }
                    }
                }

                return ret;

            },

            _checkAttributes: function () {
                // check infrastructur
                if (!this.$.path) {
                    throw "No path defined!";
                }

                if (!this.$.scope) {
                    throw "No scope defined!";
                }

                if (this.$.twoWay) {
                    if (!this.$.target) {
                        throw "TwoWay binding, but no target defined!";
                    }
                    if (!this.$.target instanceof Bindable) {
                        throw "Target is not a Bindable!";
                    }

                    if (!this.$.targetKey) {
                        throw "TwoWay binding, but no target key defined!";
                    }

                }

                if(!this.$.bindingCreator){
                    this.$.bindingCreator = this;
                }

                if(this.$.transform){
                    this.transform = this.$.transform;
                }

                if (this.$.transformBack) {
                    this.transformBack = this.$.transformBack;
                }
            },
            _createSubBinding: function () {
                if (this.$.path.length > 1) {
                    var nScope;
                    if (this.$.fnc) {
                        nScope = this.getValue();
                    } else {
                        nScope = this.$.scope.$[this.$.key.name];
                    }
                    // if keys are left and has value && is bindable
                    // get value for first child
                    if(nScope instanceof Bindable){
                        // init new binding, which triggers this binding
                        this.$subBinding = new Binding({scope: nScope, path: this.$.path.slice(1), target: this.$.target, targetKey: this.$.targetKey, rootScope: this.$.rootScope, callback: this.$.callback, context: this.$.context, twoWay: this.$.twoWay, transform: this.$.transform, transformBack: this.$.transformBack, bindingCreator: this.$.bindingCreator, parent: this, root: this.$.root});
                    } else if(nScope instanceof Object){
                        // we have a object which is not bindable
                        this.$jsonObject = nScope;
                    } else if(_.isString(nScope)){
                        this.$jsonObject = nScope;
                    }

                }
            },
            _revCallback: function (e) {
                if (this.$.fnc) {
                    var params = this._getFncParameters();
                    params.unshift(e.$);
                    this.$.fnc.apply(this.$.scope, params);
                } else {
                    this.$.scope.set(pathToString(this.$.path), this.transformBack.call(this.$.scope, e.$, this.$originalValue));
                }
            },
            /**
             * This method is called, when the a change event of an function binding is triggered
             *
             * @param event
             * @private
             */
            _changeCallback: function (event) {

                if (!this.$) {
                    return;
                }

                for (var i = 0; i < this.$.fnc._attributes.length; i++) {
                    if (!_.isUndefined(event.$[this.$.fnc._attributes[i]])) {
                        this._callback();
                        return;
                    }
                }
            },
            /**
             * This method is called, when the value of the current binding changes.
             * It recreates the the subBinding and triggers the binding
             * @private
             */
            _callback: function () {

                if(!this.$){
                    return;
                }

                this.invalidateValueCache();

                // remove subBindings!
                if (this.$subBinding) {
                    this.$subBinding.destroy();
                    this.$subBinding = null;
                }

                // try to create subBinding
                this._createSubBinding();
                this.triggerBinding();
            },

            /**
             * Unbinds all events and destroys subBinding...
             */
            _innerDestroy: function () {
                // binding already destroyed?
                if(!this.$){
                    return;
                }
                var e;
                for (var j = 0; j < this.$events.length; j++) {
                    e = this.$events[j];
                    this.$.scope.unbind(e.eventType, e.callback, this);
                }

                this.$.scope.unbind('destroy', this.destroy, this);

                if (this.$.twoWay === true) {
                    this.$.target.unbind(this.$.targetEvent, this._revCallback, this);
                }
                if (this.$subBinding) {
                    this.$subBinding.destroy();
                    this.$subBinding = null;
                }

                // destroy parameter bindings
                for (var i = 0; i < this.$parameters.length; i++) {
                    var par = this.$parameters[i];
                    if (par instanceof Binding) {
                        par.destroy();
                    }
                }
                this.$parameters = null;
                this.$.scope = this.$.target = this.$.callback = null;
                this.$ = null;
                this.$targets = null;
                this.callBase();

                bindingsDestroyed++;
                if(bindingsDestroyed === 500){
                    if (typeof(CollectGarbage) == "function") {
                        CollectGarbage();
                    }
                    bindingsDestroyed = 0;
                }
            },
            /**
             * Returns an array with values of all function parameters
             * @return {Array}
             * @private
             */
            _getFncParameters: function () {
                var parameters = [];
                for (var i = 0; i < this.$parameters.length; i++) {
                    var para = this.$parameters[i];
                    if (para instanceof Binding) {
                        para = para.getValue();
                    }
                    parameters.push(para);
                }
                return parameters;
            },
            /**
             * Returns the transformed value of the binding or null, if the binding path is not present
             * @return {*}
             */
            getValue: function () {
                if (this.$subBinding) {
                    this.$subBinding.invalidateValueCache();
                    return this.$subBinding.getValue();
                } else {
                    if(this.$cachedValue !== undefined){
                        return this.$cachedValue;
                    }
                    this.$originalValue = null;
                    if (this.$.fnc && !this.$jsonObject) {
                        this.$originalValue = this.$.fnc.apply(this.$.scope, this._getFncParameters());
                    } else if (this.$.path.length === 1) {
                        this.$originalValue = this.$.scope.get(this.$.key.name);
                    } else if(this.$jsonObject) {
                        this.$originalValue = this.$.scope.get(this.$jsonObject, this.$.path.slice(1));
                    }
                    this.$cachedValue = this.transform.call(this.$.scope, this.$originalValue);
                    return this.$cachedValue;
                }

            },

            invalidateValueCache: function(){
                this.$cachedValue = undefined;
            },

            /**
             * Returns the value in the context of the surrounding bindings
             * @return {*}
             */
            getContextValue: function () {
                if (this.$.context && this.$.context.length > 1) {
                    return Binding.contextToString(this.$.context);
                } else {
                    return this.getValue();
                }
            },
            /**
             * This method triggers the binding and syncs the target with the scope
             */
            triggerBinding: function () {
                // get value
                var val = this.getContextValue();

                var target,
                    targets = this.$.root.getTargets();
                if(targets){
                    for(var i = 0; i < targets.length; i++){
                        target = targets[i];
                        if(target.key instanceof Function){
                            target.key.call(target.scope, val, this);
                        } else {
                            target.scope.set(target.key, val);
                        }
                    }
                }

            },
            toString: function () {
                return this.getValue();
            },

            getTargets: function(){
                return this.$targets;
            },

            /***
             *
             * @param {Bindable} targetScope
             * @param {String|Function} key
             */
            addTarget: function(targetScope, key){
                targetScope.bind('destroy', function(){
                    this.removeTarget(targetScope, key);
                },this);

                this.$targets.push({
                    scope: targetScope,
                    key: key
                });
            },
            /***
             *
             * @param {Bindable} targetScope
             * @param {String|Function} key
             */
            removeTarget: function(targetScope, key){
                var target;
                if(this.$targets){
                    for (var i = 0; i < this.$targets.length; i++) {
                        target = this.$targets[i];
                        if (target.key === key && target.scope === targetScope) {
                            this.$targets.splice(i,1);
                            break;
                        }
                    }
                    if(this.$targets.length === 0){
                        this.destroy();
                    }
                }
            },

            create: function(bindingDef, target, callback){
                var options = {
                    scope: this.$.scope,
                    target: target,
                    callback: callback,
                    path: bindingDef.path,
                    parent: bindingDef.parent,
                    twoWay : bindingDef.type === TYPE_TWOWAY, bindingCreator: this.$.bindingCreator};

                var fncEl;
                var fncScope;
                if(bindingDef.transform) {
                    fncEl = bindingDef.transform.pop();
                    fncScope = this.get(bindingDef.transform);
                    if(fncScope){
                        options.transform = fncScope[fncEl.name];
                    }
                }
                if(bindingDef.transformBack){
                    fncEl = bindingDef.transformBack.pop();
                    fncScope = this.get(bindingDef.transform);
                    if (fncScope) {
                        options.transformBack = fncScope[fncEl.name];
                    }
                }
                return new Binding(options);
            }
        });

    var TYPE_FNC = Binding.TYPE_FNC = "fnc";
    var TYPE_VAR = Binding.TYPE_VAR = "var";
    var TYPE_NORMAL = Binding.TYPE_NORMAL ="normal";
    var TYPE_STATIC = Binding.TYPE_STATIC ="static";
    var TYPE_TWOWAY = Binding.TYPE_TWOWAY ="twoWay";

    Binding.contextToString = function (context) {
        var str = "", el;
        for (var i = 0; i < context.length; i++) {
            el = context[i];
            if (el instanceof Binding) {
                el = el.getValue();
            }
            if (el !== null && typeof(el) !== "undefined") {
                str += el;
            }
        }
        return str;
    };

    return Binding;
});<|endoftext|>


<commit_before>(function(root, name) {
    var node = typeof exports === 'object',
        mod = node ? module : {
            exports: {}
        };
    factory(mod, mod.exports);
    if (typeof define === 'function' && define.amd) {
        define(name, module.exports);
    } else if (!node) {
        root[name] = mod.exports;
    }

    function factory(module, exports) {
        var customTypes = [];
        var customConstructors = [];
        var reservedTypes = ["number", "undefined", "null", "infinity", "nan", "date", "regexp", "array", "integer", "float", "string", "function", "boolean", "object", "arguments"];
        var extendedTypes = ["nan", "infinity", "integer", "float"];
        /******************************************************************************
         * a function that returns the type of an object similarly to 'typeof' but with
         * the extended type differentiation proposed for EcmaScript 6 (Harmony). This
         * method relies on the value returned by Object.prototype.toString, converted
         * to a lower case string value matching the behavior of 'typeof', with some
         * special cases for distinguishing different numeric and object types.
         * The following values:
         *  - undefined
         *  - null
         * return their value as their type.  This is based on the following from the
         * EcmaScript 5.1 spec:
         *  > 15.2.4.2 Object.prototype.toString ( )
         *       When the toString method is called, the following steps are taken:
         *       1. If the this value is undefined, return "[object Undefined]".
         *       2. If the this value is null, return "[object Null]"
         * This differs from typeof in regards to null, which may return 'object' for
         * null values. Extended numeric type differentiation is optionally available
         * by passing a second argument.  If getting extended numeric types, the values:
         *  - Infinity
         *  - NaN
         * are given the same treatment as undefined and null - they return their value
         * as their type.  Extended numeric types also return types the following types:
         *  - integer
         *  - float
         * for values other than NaN or Infinity.  A full list of possible return values
         * is listed below.  Extended types may be added by passing a string type name
         * and a constructor.  In order to check for custom types, the extended argument
         * must be specified as true.  A third argument may be specified in the case of
         * desiring to check against custom types but not extended numeric types.
         *
         * @param value any type of JavaScript value to get the type of
         * @param {Boolean} extended whether to distinguish between numeric values and custom defined types
         * @param {Boolean} numericBase if extended is true, specifies whether to return extended numeric types
         * @returns {String} of one of one of the following types
         *      - undefined
         *      - null
         *      - infinity
         *      - nan
         *      - date
         *      - regexp
         *      - array
         *      - integer
         *      - float
         *      - string
         *      - function
         *      - boolean
         *      - object
         *
         * @example
         * var type = jsType.type;
         * type({ foo: 'bar' }); // 'object'
         * type(7.1, true); // 'float'
         * function MyClass() { }
         * defineType('myclass', MyClass);
         * var instance = new MyClass();
         * type(instance); // 'myclass'
         */

        function type(value, extended, baseNumeric) {
            if (value === 'undefined') return 'undefined';
            if (value === null) return 'null';
            if (extended) {
                var _type = typeof value;
                if (_type === 'number' && !baseNumeric) {
                    if (value.toString() === 'NaN') return 'nan';
                    if (value === Infinity) return 'infinity';
                    if (value % 1 === 0) return 'integer';
                    return 'float';
                }
                _type = null;
                var i = customTypes.length;
                while (i-- && !_type) {
                    _type = value instanceof customConstructors[i];
                    if (_type) return customTypes[i];
                }
            }
            // using split and substrings is faster than regular expressions
            var oclass = Object.prototype.toString.call(value).split(' ')[1];
            return oclass.substr(0, oclass.length - 1).toLowerCase();
        }
        exports.type = type;

        /**
         * define a custom type
         * @param {String} type named type for the constructor instances
         * @param {Function} constructor an object constructor
         */

        function define(typeName, constructor) {
            if (typeof typeName === 'function') {
                constructor = typeName;
                typeName = constructor.name || constructor;
            }
            if (reservedTypes.indexOf(typeName) > -1 || customTypes.indexOf(typeName) > -1) {
                throw new Error('"' + typeName + '" is already defined as a type');
            }
            customTypes.push(typeName);
            extendedTypes.push(typeName);
            customConstructors.push(constructor);
            var methodName = 'is' + typeName.charAt(0).toUpperCase() + typeName.substr(1);
            exports[methodName] = getMethod(typeName);
        }
        exports.define = define;

        function getMethod(curType) {
            if (extendedTypes.indexOf(curType) === -1) {
                return function(value) {
                    return type(value) === curType;
                };
            } else {
                return function (value) {
                    return type(value, true) === curType;
                };
            }
        }

        for (var i = 0, l = reservedTypes.length; i < l; i++) {
            var curType = reservedTypes[i];
            var methodName = 'is' + curType.charAt(0).toUpperCase() + curType.substr(1);
            exports[methodName] = getMethod(curType);
        }

    }
})(this, 'jsType');<commit_msg>updated jstype.js<commit_after>(function(root, name) {
    var node = typeof exports === 'object',
        mod = node ? module : {
            exports: {}
        };
    factory(mod, mod.exports);
    if (typeof define === 'function' && define.amd) {
        define(name, module.exports);
    } else if (!node) {
        root[name] = mod.exports;
    }

    function factory(module, exports) {
        var customTypes = [];
        var customConstructors = [];
        var reservedTypes = ["number", "undefined", "null", "infinity", "nan", "date", "regexp", "array", "integer", "float", "string", "function", "boolean", "object", "arguments"];
        var extendedTypes = ["nan", "infinity", "integer", "float"];
        /******************************************************************************
         * a function that returns the type of an object similarly to 'typeof' but with
         * the extended type differentiation proposed for EcmaScript 6 (Harmony). This
         * method relies on the value returned by Object.prototype.toString, converted
         * to a lower case string value matching the behavior of 'typeof', with some
         * special cases for distinguishing different numeric and object types.
         * The following values:
         *  - undefined
         *  - null
         * return their value as their type.  This is based on the following from the
         * EcmaScript 5.1 spec:
         *  > 15.2.4.2 Object.prototype.toString ( )
         *       When the toString method is called, the following steps are taken:
         *       1. If the this value is undefined, return "[object Undefined]".
         *       2. If the this value is null, return "[object Null]"
         * This differs from typeof in regards to null, which may return 'object' for
         * null values. Extended numeric type differentiation is optionally available
         * by passing a second argument.  If getting extended numeric types, the values:
         *  - Infinity
         *  - NaN
         * are given the same treatment as undefined and null - they return their value
         * as their type.  Extended numeric types also return types the following types:
         *  - integer
         *  - float
         * for values other than NaN or Infinity.  A full list of possible return values
         * is listed below.  Extended types may be added by passing a string type name
         * and a constructor.  In order to check for custom types, the extended argument
         * must be specified as true.  A third argument may be specified in the case of
         * desiring to check against custom types but not extended numeric types.
         *
         * @param value any type of JavaScript value to get the type of
         * @param {Boolean} extended whether to distinguish between numeric values and custom defined types
         * @param {Boolean} numericBase if extended is true, specifies whether to return extended numeric types
         * @returns {String} of one of one of the following types
         *      - undefined
         *      - null
         *      - infinity
         *      - nan
         *      - date
         *      - regexp
         *      - array
         *      - integer
         *      - float
         *      - string
         *      - function
         *      - boolean
         *      - object
         *
         * @example
         * var type = jsType.type;
         * type({ foo: 'bar' }); // 'object'
         * type(7.1, true); // 'float'
         * function MyClass() { }
         * defineType('myclass', MyClass);
         * var instance = new MyClass();
         * type(instance); // 'myclass'
         */
        function type(value, extended, baseNumeric) {
            if (value === 'undefined') return 'undefined';
            if (value === null) return 'null';
            if (extended) {
                var _type = typeof value;
                if (_type === 'number' && !baseNumeric) {
                    if (value.toString() === 'NaN') return 'nan';
                    if (value === Infinity) return 'infinity';
                    if (value % 1 === 0) return 'integer';
                    return 'float';
                }
                _type = null;
                var i = customTypes.length;
                while (i-- && !_type) {
                    _type = value instanceof customConstructors[i];
                    if (_type) return customTypes[i];
                }
            }
            // using split and substrings is faster than regular expressions
            var oclass = Object.prototype.toString.call(value).split(' ')[1];
            return oclass.substr(0, oclass.length - 1).toLowerCase();
        }
        exports.type = type;

        /**
         * define a custom type
         * @param {String} type named type for the constructor instances
         * @param {Function} constructor an object constructor
         */

        function define(typeName, constructor) {
            if (typeof typeName === 'function') {
                constructor = typeName;
                typeName = constructor.name || constructor;
            }
            if (reservedTypes.indexOf(typeName) > -1 || customTypes.indexOf(typeName) > -1) {
                throw new Error('"' + typeName + '" is already defined as a type');
            }
            customTypes.push(typeName);
            extendedTypes.push(typeName);
            customConstructors.push(constructor);
            var methodName = 'is' + typeName.charAt(0).toUpperCase() + typeName.substr(1);
            exports[methodName] = getMethod(typeName);
        }
        exports.define = define;

        function getMethod(curType) {
            if (extendedTypes.indexOf(curType) === -1) {
                return function(value) {
                    return type(value) === curType;
                };
            } else {
                return function (value) {
                    return type(value, true) === curType;
                };
            }
        }

        for (var i = 0, l = reservedTypes.length; i < l; i++) {
            var curType = reservedTypes[i];
            var methodName = 'is' + curType.charAt(0).toUpperCase() + curType.substr(1);
            exports[methodName] = getMethod(curType);
        }

    }
})(this, 'jsType');<|endoftext|>


<commit_before>
const infernalUtils = require("./infernalUtils");
const RuleContext = require("./RuleContext");

module.exports = class InfernalEngine {

    _busy = false;
    _facts = new Map();
    _rules = new Map();
    _relations = new Map();
    _agenda = new Map();
    _changes = new Set();
    _logger = null;
    _maxDepth = 50;

    constructor(maxDepth, logger) {
        if (maxDepth) this._maxDepth = maxDepth;
        this._logger = logger || { log: () => {} };
    }

    /**
     * Return the fact's value.
     *
     * @param {String} path The full path to the desired fact.
     *
     * @returns {Promise<*>} The fact value.
     */
    async get(path) {
        let factpath = path.startsWith("/") ? path : `/${path}`;
        let compiledpath = infernalUtils.compilePath(factpath);
        return this._facts.get(compiledpath);
    }

    /**
     * Sets the fact to the given value.
     *
     * @param {String} path The full path to the desired fact.
     * @param {*} value The fact value to set, must be a scalar.
     */
    async set(path, value) {
        let factpath = path.startsWith("/") ? path : `/${path}`;
        let compiledpath = infernalUtils.compilePath(factpath);
        var prevValue = this._facts.get(compiledpath);

        // Nothing to do if both values are equals, except if it is an array.
        // When setting an array, we consider the value changed without actually
        // comparing the arrays content assuming that executing the matching
        // rule is faster.
        if (!(value instanceof Array) && infernalUtils.equals(prevValue, value)) {
            return;
        }

        this._changes.add(compiledpath);
        this._facts.set(compiledpath, value);
        this._logger.log("debug", {
            action: "set",
            fact: compiledpath,
            prevValue: prevValue,
            newValue: value
        });
        _addToAgenda.call(this, compiledpath);
        if (!this._busy) {
            await this.infer(true);
        }
    }

    /**
     * Add a rule to the engine's ruleset.
     *
     * @param {String} path The path where to save the rule at.
     * @param {Function} rule The rule to add. Must be async.
     */
    async addRule(path, rule) {
        let rulepath = path.startsWith("/") ? path : `/${path}`;
        let compiledRulepath = infernalUtils.compilePath(rulepath);
        let context = infernalUtils.getContext(compiledRulepath);

        let ruleContext = new RuleContext(this, rule, context)
        let parameters = infernalUtils.parseParameters(rule);
        for (const param of parameters) {
            let factpath = param.startsWith("/") ? param : context + param;
            let compiledFactpath = infernalUtils.compilePath(factpath);
            if (!this._relations.has(compiledFactpath))
                this._relations.set(compiledFactpath, new Set());
            this._relations.get(compiledFactpath).add(compiledRulepath);
            ruleContext.inputFacts.push(compiledFactpath);
        }
        this._rules.set(compiledRulepath, ruleContext);

        this._logger.log("debug", {
            action: "addRule",
            rule: compiledRulepath
        });

        this._agenda.set(compiledRulepath, ruleContext);
        if (!this._busy) {
            await this.infer(true);
        }
    }

    /**
     * Execute inference and return a promise. At the begining of the inference, sets the
     * '/_maxDepth' fact to make it available to rules that would be interested in this value.
     * Upon each loop, the engine sets the '/_depth' value indicating the agenda generation the
     * inference is currently at.
     */
    async infer() {
        this._logger.log("debug", {
            action: "infer"
        });

        this._busy = true;
        let depth = 0;
        this._facts.set("/$/maxDepth", this._maxDepth); //metafacts do not trigger rules

        try {
            while (depth < this._maxDepth && this._agenda.size > 0) {
                depth++;
                this._facts.set("/$/depth", depth); // metafacts do not trigger rules
                this._logger.log("debug", {
                    action: "executeAgenda",
                    depth: depth
                });
                let currentAgenda = this._agenda;
                this._agenda = new Map();
                for (const [path, rulectx] of currentAgenda) {
                    this._logger.log("debug", {
                        action: "executeRule",
                        path: path
                    });
                    await rulectx.execute();
                }
            }
        }
        finally {
            this._busy = false;
        }

        if (depth == this._maxDepth) {
            throw new Error("Inference not completed because maximum depth " +
                `reached (${this._maxDepth}). Please review for infinite loop or set the ` +
                "inferenceDepth property to a higher value.");
        }
    }

    /**
     * Import the given Javascript object into the engine. Scalar values and arrays as facts,
     * functions as rules. Once the object is imported, launches the inference on all rules if
     * specified.
     *
     * @param {String}  path    The path where the object will be imported.
     * @param {Object}  obj     The object to import.
     */
    async import(path, obj) {
        this._busy = true;
        try {
            await _import.call(this, path, obj);
            await this.infer();
        }
        finally {
            this._busy = false;
        }
    }

    /**
     * Export internal facts from the given optional path as a JSON object that does not include
     * rules.
     *
     * @param {String} path Optional, default "/". The path to export as an object.
     *
     * @returns {object} a JSON object representation of the engine internal state.
     */
    async export(path) {
        let targetPath = path || "/";
        if (!targetPath.startsWith("/")) {
            targetPath = `/${targetPath}`;
        }
        let obj = {};
        for (const [key, value] of this._facts) {
            if (key.startsWith(targetPath)) {
                let subkeys = key
                    .substring(targetPath.length)
                    .replace(/\//g, " ")
                    .trim()
                    .split(" ");
                _deepSet(obj, subkeys, value);
            }
        }
        return obj;
    }

    /**
     * Exports the set of changes from the last inference.
     *
     * @returns a JSON object containing the cumulative changes.
     */
    async exportChanges() {
        let obj = {};
        for (const key of this._changes) {
            let subkeys = key
                .replace(/\//g, " ")
                .trim()
                .split(" ");
            _deepSet(obj, subkeys, this._facts.get(key));
        }
        this._changes.clear();
        return obj;
    }

    /**
     * Clear changes.
     */
    async clear() {
        this._changes.clear();
    }
}

// Private

async function _import(path, obj) {
    if (typeof path === "object") {
        return await _import.call(this, "/", path);
    }

    let targetPath = path;
    if (path.endsWith("/")) {
        targetPath = path.substring(0, path.length-1);
    }

    this._logger.log("debug", {
        action: "import",
        object: obj
    });

    // Set an object that needs to be handled like scalar value.
    if (obj instanceof Date || obj instanceof Array) {
        return await this.set(targetPath, obj);
    }

    const objtype = typeof obj;

    // Handle rules as they come by.
    if (objtype === "function") {
        return await this.addRule(targetPath, obj);
    }

    // Set scalar value
    if (objtype !== "object") {
        return await this.set(targetPath, obj);
    }

    // Drill down into the object to add other facts and rules.
    for (let member in obj) {
        await _import.call(this,
            `${targetPath}/${member}`,
            obj[member]);
    }
}

function _addToAgenda(factName) {
    if (this._relations.has(factName)) {
        let rules = this._relations.get(factName);
        rules.forEach(ruleName => {
            this._agenda.set(ruleName, this._rules.get(ruleName));
            this._logger.log("debug", {
                action: "addToAgenda",
                rule: ruleName
            });
        });
    }
}

function _deepSet(target, keys, value) {
    let key = keys[0];
    if (keys.length === 1) {
        target[key] = value;
        return;
    }
    if (typeof target[key] === "undefined") {
        target[key] = {};
    }
    _deepSet(target[key], keys.slice(1), value)
}
<commit_msg>Privatize the unused infer method<commit_after>
const infernalUtils = require("./infernalUtils");
const RuleContext = require("./RuleContext");

module.exports = class InfernalEngine {

    _busy = false;
    _facts = new Map();
    _rules = new Map();
    _relations = new Map();
    _agenda = new Map();
    _changes = new Set();
    _logger = null;
    _maxDepth = 50;

    constructor(maxDepth, logger) {
        if (maxDepth) this._maxDepth = maxDepth;
        this._logger = logger || { log: () => {} };
    }

    /**
     * Return the fact's value.
     *
     * @param {String} path The full path to the desired fact.
     *
     * @returns {Promise<*>} The fact value.
     */
    async get(path) {
        let factpath = path.startsWith("/") ? path : `/${path}`;
        let compiledpath = infernalUtils.compilePath(factpath);
        return this._facts.get(compiledpath);
    }

    /**
     * Sets the fact to the given value.
     *
     * @param {String} path The full path to the desired fact.
     * @param {*} value The fact value to set, must be a scalar.
     */
    async set(path, value) {
        let factpath = path.startsWith("/") ? path : `/${path}`;
        let compiledpath = infernalUtils.compilePath(factpath);
        var prevValue = this._facts.get(compiledpath);

        // Nothing to do if both values are equals, except if it is an array.
        // When setting an array, we consider the value changed without actually
        // comparing the arrays content assuming that executing the matching
        // rule is faster.
        if (!(value instanceof Array) && infernalUtils.equals(prevValue, value)) {
            return;
        }

        this._changes.add(compiledpath);
        this._facts.set(compiledpath, value);
        this._logger.log("debug", {
            action: "set",
            fact: compiledpath,
            prevValue: prevValue,
            newValue: value
        });
        _addToAgenda.call(this, compiledpath);
        if (!this._busy) {
            await _infer.call(this);
        }
    }

    /**
     * Add a rule to the engine's ruleset.
     *
     * @param {String} path The path where to save the rule at.
     * @param {Function} rule The rule to add. Must be async.
     */
    async addRule(path, rule) {
        let rulepath = path.startsWith("/") ? path : `/${path}`;
        let compiledRulepath = infernalUtils.compilePath(rulepath);
        let context = infernalUtils.getContext(compiledRulepath);

        let ruleContext = new RuleContext(this, rule, context)
        let parameters = infernalUtils.parseParameters(rule);
        for (const param of parameters) {
            let factpath = param.startsWith("/") ? param : context + param;
            let compiledFactpath = infernalUtils.compilePath(factpath);
            if (!this._relations.has(compiledFactpath))
                this._relations.set(compiledFactpath, new Set());
            this._relations.get(compiledFactpath).add(compiledRulepath);
            ruleContext.inputFacts.push(compiledFactpath);
        }
        this._rules.set(compiledRulepath, ruleContext);

        this._logger.log("debug", {
            action: "addRule",
            rule: compiledRulepath
        });

        this._agenda.set(compiledRulepath, ruleContext);
        if (!this._busy) {
            await _infer.call(this);
        }
    }

    /**
     * Import the given Javascript object into the engine. Scalar values and arrays as facts,
     * functions as rules. Once the object is imported, launches the inference on all rules if
     * specified.
     *
     * @param {String}  path    The path where the object will be imported.
     * @param {Object}  obj     The object to import.
     */
    async import(path, obj) {
        this._busy = true;
        try {
            await _import.call(this, path, obj);
            await _infer.call(this);
        }
        finally {
            this._busy = false;
        }
    }

    /**
     * Export internal facts from the given optional path as a JSON object that does not include
     * rules.
     *
     * @param {String} path Optional, default "/". The path to export as an object.
     *
     * @returns {object} a JSON object representation of the engine internal state.
     */
    async export(path) {
        let targetPath = path || "/";
        if (!targetPath.startsWith("/")) {
            targetPath = `/${targetPath}`;
        }
        let obj = {};
        for (const [key, value] of this._facts) {
            if (key.startsWith(targetPath)) {
                let subkeys = key
                    .substring(targetPath.length)
                    .replace(/\//g, " ")
                    .trim()
                    .split(" ");
                _deepSet(obj, subkeys, value);
            }
        }
        return obj;
    }

    /**
     * Exports the set of changes from the last inference.
     *
     * @returns a JSON object containing the cumulative changes.
     */
    async exportChanges() {
        let obj = {};
        for (const key of this._changes) {
            let subkeys = key
                .replace(/\//g, " ")
                .trim()
                .split(" ");
            _deepSet(obj, subkeys, this._facts.get(key));
        }
        this._changes.clear();
        return obj;
    }

    /**
     * Clear changes.
     */
    async clear() {
        this._changes.clear();
    }
}

// Private

/**
 * Execute inference and return a promise. At the begining of the inference, sets the
 * '/_maxDepth' fact to make it available to rules that would be interested in this value.
 * Upon each loop, the engine sets the '/_depth' value indicating the agenda generation the
 * inference is currently at.
 */
async function _infer() {
    this._logger.log("debug", {
        action: "infer"
    });

    this._busy = true;
    let depth = 0;
    this._facts.set("/$/maxDepth", this._maxDepth); //metafacts do not trigger rules

    try {
        while (depth < this._maxDepth && this._agenda.size > 0) {
            depth++;
            this._facts.set("/$/depth", depth); // metafacts do not trigger rules
            this._logger.log("debug", {
                action: "executeAgenda",
                depth: depth
            });
            let currentAgenda = this._agenda;
            this._agenda = new Map();
            for (const [path, rulectx] of currentAgenda) {
                this._logger.log("debug", {
                    action: "executeRule",
                    path: path
                });
                await rulectx.execute();
            }
        }
    }
    finally {
        this._busy = false;
    }

    if (depth == this._maxDepth) {
        throw new Error("Inference not completed because maximum depth " +
            `reached (${this._maxDepth}). Please review for infinite loop or set the ` +
            "inferenceDepth property to a higher value.");
    }
}

async function _import(path, obj) {
    if (typeof path === "object") {
        return await _import.call(this, "/", path);
    }

    let targetPath = path;
    if (path.endsWith("/")) {
        targetPath = path.substring(0, path.length-1);
    }

    this._logger.log("debug", {
        action: "import",
        object: obj
    });

    // Set an object that needs to be handled like scalar value.
    if (obj instanceof Date || obj instanceof Array) {
        return await this.set(targetPath, obj);
    }

    const objtype = typeof obj;

    // Handle rules as they come by.
    if (objtype === "function") {
        return await this.addRule(targetPath, obj);
    }

    // Set scalar value
    if (objtype !== "object") {
        return await this.set(targetPath, obj);
    }

    // Drill down into the object to add other facts and rules.
    for (let member in obj) {
        await _import.call(this,
            `${targetPath}/${member}`,
            obj[member]);
    }
}

function _addToAgenda(factName) {
    if (this._relations.has(factName)) {
        let rules = this._relations.get(factName);
        rules.forEach(ruleName => {
            this._agenda.set(ruleName, this._rules.get(ruleName));
            this._logger.log("debug", {
                action: "addToAgenda",
                rule: ruleName
            });
        });
    }
}

function _deepSet(target, keys, value) {
    let key = keys[0];
    if (keys.length === 1) {
        target[key] = value;
        return;
    }
    if (typeof target[key] === "undefined") {
        target[key] = {};
    }
    _deepSet(target[key], keys.slice(1), value)
}
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var rollup = require('rollup').rollup;
var debounce = require('debounce');

var dependencyMap = new Map();
var staleDependants = new Set();

function createPreprocessor (config, logger) {
	var log = logger.create('preprocessor.rollup');
	var cache;

	/**
	 * Manually update the modified and accessed timestamps
	 * of all dependants marked by changed dependencies.
	 */
	var recompileDependants = debounce(function () {
		var now = Date.now();
		for (var dependant of staleDependants.values()) {
			fs.utimes(dependant, now, now);
			log.debug('Recompiling dependant %s', dependant);
		}
		staleDependants.clear();
	}, 50);

	config = config || {};

	function preprocess (content, file, done) {
		log.debug('Processing %s', file.originalPath);

		try {
			config.entry = file.originalPath;
			config.cache = cache;

			rollup(config).then(function (bundle) {

				/**
				 * Map all dependencies of the current file
				 */
				file.dependencies = bundle.modules
					.map(function (module) { return module.id; })
					.filter(function (id) { return id !== file.originalPath; });

				dependencyMap.set(file.originalPath, file.dependencies);

				/**
				 * Check all dependants to see if the current file
				 * is one of their dependencies, marking those that
				 * match as stale and triggering their recompilation.
				 */
				for (var entry of dependencyMap.entries()) {
					var dependant = entry[0];
					var dependencies = entry[1];
					if (dependencies.indexOf(file.originalPath) > -1) {
						staleDependants.add(dependant);
						recompileDependants();
					}
				}

				var generated = bundle.generate(config);
				var processed = generated.code;

				cache = bundle;

				if (config.sourceMap === 'inline') {
					processed += '\n' + '//# sourceMappingURL=' + generated.map.toUrl() + '\n';
				}

				done(null, processed);
			})
			.catch(function (error) {
				log.error('Failed to process %s\n\n%s\n', file.originalPath, error.message);
				done(error, null);
			});

		} catch (exception) {
			log.error('Exception processing %s\n\n%s\n', file.originalPath, exception.message);
			done(exception, null);
		}
	}

	return preprocess;
}

createPreprocessor.$inject = ['config.rollupPreprocessor', 'logger'];

module.exports = {
	'preprocessor:rollup': ['factory', createPreprocessor],
};
<commit_msg>Add callback to async fs.utimes<commit_after>'use strict';

var fs = require('fs');
var rollup = require('rollup').rollup;
var debounce = require('debounce');

var dependencyMap = new Map();
var staleDependants = new Set();

function createPreprocessor (config, logger) {
	var log = logger.create('preprocessor.rollup');
	var cache;

	/**
	 * Manually update the modified and accessed timestamps
	 * of all dependants marked by changed dependencies.
	 */
	var recompileDependants = debounce(function () {
		var now = Date.now();
		for (var dependant of staleDependants.values()) {
			fs.utimes(dependant, now, now, function() {
				log.debug('Recompiling dependant %s', dependant);
			});
		}
		staleDependants.clear();
	}, 50);

	config = config || {};

	function preprocess (content, file, done) {
		log.debug('Processing %s', file.originalPath);

		try {
			config.entry = file.originalPath;
			config.cache = cache;

			rollup(config).then(function (bundle) {

				/**
				 * Map all dependencies of the current file
				 */
				file.dependencies = bundle.modules
					.map(function (module) { return module.id; })
					.filter(function (id) { return id !== file.originalPath; });

				dependencyMap.set(file.originalPath, file.dependencies);

				/**
				 * Check all dependants to see if the current file
				 * is one of their dependencies, marking those that
				 * match as stale and triggering their recompilation.
				 */
				for (var entry of dependencyMap.entries()) {
					var dependant = entry[0];
					var dependencies = entry[1];
					if (dependencies.indexOf(file.originalPath) > -1) {
						staleDependants.add(dependant);
						recompileDependants();
					}
				}

				var generated = bundle.generate(config);
				var processed = generated.code;

				cache = bundle;

				if (config.sourceMap === 'inline') {
					processed += '\n' + '//# sourceMappingURL=' + generated.map.toUrl() + '\n';
				}

				done(null, processed);
			})
			.catch(function (error) {
				log.error('Failed to process %s\n\n%s\n', file.originalPath, error.message);
				done(error, null);
			});

		} catch (exception) {
			log.error('Exception processing %s\n\n%s\n', file.originalPath, exception.message);
			done(exception, null);
		}
	}

	return preprocess;
}

createPreprocessor.$inject = ['config.rollupPreprocessor', 'logger'];

module.exports = {
	'preprocessor:rollup': ['factory', createPreprocessor],
};
<|endoftext|>


<commit_before>'use strict';

var util = require('./util');
var vinylFS = require('vinyl-fs');
var tornado = require('tornado-repl');

var Stack = tornado.Stack;
var Runtime = tornado.Runtime;

// String to function mapping failed, handle it
//
Stack.prototype.onHandleNotFound = function(next){
  var path = util.color.cyan(next.match || next.path);
  console.log('%s found for `%s`', util.color.red('No task'), path);
  console.log('Set one with `task(\'%s\', [Function])`', path);
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Errors anywhere in the stack
//
Stack.prototype.onHandleError = function(error, next){
  if(!(error instanceof Error)){ return next(); }

  util.log('Error in \'%s\' after %s\n%s',
    util.color.cyan(next.match),
    util.color.time(next.time),
    error.stack
  );

  next.error = error;
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Make a logger that looks similar to gulp's
//
Stack.prototype.onHandle = function(next){
  if(next.error || !this.log || /^-/.test(next.match)){
    return ; // ^ skip logging for errors, silent or flags
  }

  if(!this.configFile){
    this.configFile = util.configFile();
    this.runtime.set({configFile: this.configFile});
    if(this.configFile instanceof Error){ return ; }
    util.log('Using', util.color.file(this.configFile));
  }

  var host = '';
  if(this.host){
    host = '(' + util.color.green(this.host.path) + ') ';
  }

  if(!this.time){
    util.log('%sStarted \'%s\' in %s',
      host,
      util.color.cyan(this.path),
      util.color.bold(this.wait ? 'series' : 'parallel')
    );
    this.time = process.hrtime();
  } else if(next.time && next.match && next.match !== this.path){
    util.log('%sFinished \'%s\' after %s',
      host, util.color.cyan(next.match), util.color.time(next.time)
    );
  }

  if(!next.time){ next.time = process.hrtime(); }
  if(this.queue || this.time.end){ return ; }

  this.time.end = util.color.time(this.time);
  util.log('%sFinished \'%s\' after %s',
    host, util.color.cyan(this.path), this.time.end
  );
};

/*
# runtime.src
```js
function src(string|array glob[, object opt])
```
Same as [vinylFs.src][m-vinly-fs-src]
*/

Runtime.prototype.src = vinylFS.src;

/*
# runtime.dest
```js
function dest(string|function folder[, object opt])
```
Same as [vinylFs.dest][m-vinly-fs-dest]
*/
Runtime.prototype.dest = vinylFS.dest;

/*
# runtime.task
```js
function task(string name[, string|array dep, function handle])
```
_arguments_
 - are same as [gulp.task][m-gulp-task] aside of `dep`
 - `dep` type array or string, tasks to run before this one
 if given as a string they must be space separated

_returns_ differences with [gulp.task][m-gulp-task]
 - the function of the task `name`, if it was set before
 - this otherwise (just as [gulp.task][m-gulp-task])
*/

Runtime.prototype.task = function(name, deps, handle){
  var handleType = util.type(handle || deps);

  if(typeof name !== 'string'){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n'+
      '- Tasks require a string `name`'
    );
  } else if(!handleType.match(/function|array|string/)){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n' +
      'New tasks need at lest 2 arguments\n'+
      ' - deps: dependencies to run before this one'+
      ' - handle: function that runs the task\n'+
      '\n'
    );
  }

  var error;
  // TODO: checking handle completion should be more robust
  //
  handle = handleType.function || function(next){ next(); };
  if(!handle.length && !/return/.test(handle.toString())){
    name = util.color.cyan(name);
    error = new Error(
      'task `'+name+'` doesn\'t give a hint of its completion\n'+
      'Meaning: it doesn\'t return nor uses a callback\n'+
      'Solution:\n'+
      ' - return a stream, promise or observable\n'+
      ' - use the callback that is passed as first argument\n--'
    );
    util.color.callersPath(error);
    throw error;
  }

  if(!handle.name && !handle.displayName){
    handle.displayName = name;
  }

  var depsType = util.type(deps);
  if(!depsType.match(/array|string/) || !deps.length){
    return this.set(name, handle);
  }
  deps = depsType.array || deps.trim().split(/[ ]+/);
  this.set(name);
  // prevent circular dependencies
  // TODO: this might to much overhead, think of a better solution
  //
  deps.forEach(function isDepCircular(dep){
    var node = this.get(dep, {ref: true});
    if(node.path !== name){ return ; }

    dep = util.color.red(dep);
    name = util.color.cyan(name);
    regex = util.color.yellow(regex);

    var regex = RegExp(this.regex.filter(function(re){
      return re.path === name;
    })[0].source);

    var error = new Error(
      'task `'+name+'` dependency `'+dep+'` is circular\n'+
      'Meaning: if is run it will call itself\n'+
      'Solution: rewrite it so they don\'t match\n'+
      '- path: ' + name + '\n'+
      '- deps: ' + JSON.stringify(deps) + '\n'+
      '- regex: ' + regex +'\n--'
    );
    // mark caller's file path on the stack
    util.color.callersPath(error, 2);
    throw error;
  }, this);

  return this.set(name, {
    dep: deps,
    handle: this.stack(deps.join(' '), handle, {wait: true})
  });
};

/*
# runtime.watch
```js
function watch(string|array glob[, string|array|object opt, function cb])
```

Same as [gulp.watch][m-gulp-watch] (run tasks when files change).
Additional features:
  - Files can be reloaded on the `require.cache`
  - Tasks can be run in series directly if so specified

_arguments_ differences with [gulp.watch][m-gulp-watch]
 - `tasks` can be a string, space separated tasks will be dispatched as written
 - when `opt` is an object, it can have non mandatory properties below
  - `opt.wait` type boolean, if `opt.tasks` should run in series or not
  - `opt.tasks` type array|string, tasks to run after change and before reload
  - `opt.reload` type boolean, wether or not to reload a file

_defaults_
 - `opt.wait` to `false`
 - `opt.reload` to `undefined`

_returns_
 - a watcher, same as [gulp.watch][m-gulp-watch]

> Notes:
- callback arguments are (a gaze event, the `opt`s given)
- Only files with a registered `require.extension` will be reloaded
- When a file is deleted on disk, the reload will be skipped but the
callback is still invoked

```javascript
var gulp = require('gulp-runtime').create();

var watcher;
watcher = gulp.watch('app/*.js', 'minify browserSync');

gulp.watch('app/*.*', {
  wait: true,  // run tasks after change in series
  tasks: 'sass css jsx browserSync',
  reload: true // will only reload app/*.js files (server, db, etc)
}, function(ev, opt){
  var reloaded = opt.reload && ev.type !== 'delete';
  console.log('%s was %s', ev.path, ev.type, reloaded ? 'and reloaded' : '');
});
```
**/
Runtime.prototype.watch = function(glob, opt, cb){
  cb = util.type(cb || opt).function;
  opt = !cb && util.type(cb).plainObject
    || cb !== opt && util.type(opt).plainObject
    || {tasks: opt};

  if(cb || opt.reload){
    opt.onHandleEnd = function(ev){
      if(util.shouldReloadFile(opt, ev)){
        delete require.cache[ev.path];
        require(ev.path);
      }
      if(cb){ cb(ev, opt); }
    };
  }

  var tasks = util.type(opt && opt.tasks || opt);
  if(tasks.string || tasks.array){
    tasks = tasks.string || tasks.array.join(' ');
    return vinylFS.watch(glob, null, this.stack(tasks, opt));
  }

  return vinylFS.watch(glob, opt, opt.onHandleEnd);
};

// export back tornado
//
exports = module.exports = tornado;
<commit_msg>moved the onHandleEnd for when only tasks are given<commit_after>'use strict';

var util = require('./util');
var vinylFS = require('vinyl-fs');
var tornado = require('tornado-repl');

var Stack = tornado.Stack;
var Runtime = tornado.Runtime;

// String to function mapping failed, handle it
//
Stack.prototype.onHandleNotFound = function(next){
  var path = util.color.cyan(next.match || next.path);
  console.log('%s found for `%s`', util.color.red('No task'), path);
  console.log('Set one with `task(\'%s\', [Function])`', path);
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Errors anywhere in the stack
//
Stack.prototype.onHandleError = function(error, next){
  if(!(error instanceof Error)){ return next(); }

  util.log('Error in \'%s\' after %s\n%s',
    util.color.cyan(next.match),
    util.color.time(next.time),
    error.stack
  );

  next.error = error;
  if(this.runtime.repl){ return next(); }
  process.exit(1);
};

// Make a logger that looks similar to gulp's
//
Stack.prototype.onHandle = function(next){
  if(next.error || !this.log || /^-/.test(next.match)){
    return ; // ^ skip logging for errors, silent or flags
  }

  if(!this.configFile){
    this.configFile = util.configFile();
    this.runtime.set({configFile: this.configFile});
    if(this.configFile instanceof Error){ return ; }
    util.log('Using', util.color.file(this.configFile));
  }

  var host = '';
  if(this.host){
    host = '(' + util.color.green(this.host.path) + ') ';
  }

  if(!this.time){
    util.log('%sStarted \'%s\' in %s',
      host,
      util.color.cyan(this.path),
      util.color.bold(this.wait ? 'series' : 'parallel')
    );
    this.time = process.hrtime();
  } else if(next.time && next.match && next.match !== this.path){
    util.log('%sFinished \'%s\' after %s',
      host, util.color.cyan(next.match), util.color.time(next.time)
    );
  }

  if(!next.time){ next.time = process.hrtime(); }
  if(this.queue || this.time.end){ return ; }

  this.time.end = util.color.time(this.time);
  util.log('%sFinished \'%s\' after %s',
    host, util.color.cyan(this.path), this.time.end
  );
};

/*
# runtime.src
```js
function src(string|array glob[, object opt])
```
Same as [vinylFs.src][m-vinly-fs-src]
*/

Runtime.prototype.src = vinylFS.src;

/*
# runtime.dest
```js
function dest(string|function folder[, object opt])
```
Same as [vinylFs.dest][m-vinly-fs-dest]
*/
Runtime.prototype.dest = vinylFS.dest;

/*
# runtime.task
```js
function task(string name[, string|array dep, function handle])
```
_arguments_
 - are same as [gulp.task][m-gulp-task] aside of `dep`
 - `dep` type array or string, tasks to run before this one
 if given as a string they must be space separated

_returns_ differences with [gulp.task][m-gulp-task]
 - the function of the task `name`, if it was set before
 - this otherwise (just as [gulp.task][m-gulp-task])
*/

Runtime.prototype.task = function(name, deps, handle){
  var handleType = util.type(handle || deps);

  if(typeof name !== 'string'){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n'+
      '- Tasks require a string `name`'
    );
  } else if(!handleType.match(/function|array|string/)){
    throw new TypeError(
      'task(string name, [string|array dep, function handle])\n' +
      'New tasks need at lest 2 arguments\n'+
      ' - deps: dependencies to run before this one'+
      ' - handle: function that runs the task\n'+
      '\n'
    );
  }

  var error;
  // TODO: checking handle completion should be more robust
  //
  handle = handleType.function || function(next){ next(); };
  if(!handle.length && !/return/.test(handle.toString())){
    name = util.color.cyan(name);
    error = new Error(
      'task `'+name+'` doesn\'t give a hint of its completion\n'+
      'Meaning: it doesn\'t return nor uses a callback\n'+
      'Solution:\n'+
      ' - return a stream, promise or observable\n'+
      ' - use the callback that is passed as first argument\n--'
    );
    util.color.callersPath(error);
    throw error;
  }

  if(!handle.name && !handle.displayName){
    handle.displayName = name;
  }

  var depsType = util.type(deps);
  if(!depsType.match(/array|string/) || !deps.length){
    return this.set(name, handle);
  }
  deps = depsType.array || deps.trim().split(/[ ]+/);
  this.set(name);
  // prevent circular dependencies
  // TODO: this might to much overhead, think of a better solution
  //
  deps.forEach(function isDepCircular(dep){
    var node = this.get(dep, {ref: true});
    if(node.path !== name){ return ; }

    dep = util.color.red(dep);
    name = util.color.cyan(name);
    regex = util.color.yellow(regex);

    var regex = RegExp(this.regex.filter(function(re){
      return re.path === name;
    })[0].source);

    var error = new Error(
      'task `'+name+'` dependency `'+dep+'` is circular\n'+
      'Meaning: if is run it will call itself\n'+
      'Solution: rewrite it so they don\'t match\n'+
      '- path: ' + name + '\n'+
      '- deps: ' + JSON.stringify(deps) + '\n'+
      '- regex: ' + regex +'\n--'
    );
    // mark caller's file path on the stack
    util.color.callersPath(error, 2);
    throw error;
  }, this);

  return this.set(name, {
    dep: deps,
    handle: this.stack(deps.join(' '), handle, {wait: true})
  });
};

/*
# runtime.watch
```js
function watch(string|array glob[, string|array|object opt, function cb])
```

Same as [gulp.watch][m-gulp-watch] (run tasks when files change).
Additional features:
  - Files can be reloaded on the `require.cache`
  - Tasks can be run in series directly if so specified

_arguments_ differences with [gulp.watch][m-gulp-watch]
 - `tasks` can be a string, space separated tasks will be dispatched as written
 - when `opt` is an object, it can have non mandatory properties below
  - `opt.wait` type boolean, if `opt.tasks` should run in series or not
  - `opt.tasks` type array|string, tasks to run after change and before reload
  - `opt.reload` type boolean, wether or not to reload a file

_defaults_
 - `opt.wait` to `false`
 - `opt.reload` to `undefined`

_returns_
 - a watcher, same as [gulp.watch][m-gulp-watch]

> Notes:
- callback arguments are (a gaze event, the `opt`s given)
- Only files with a registered `require.extension` will be reloaded
- When a file is deleted on disk, the reload will be skipped but the
callback is still invoked

```javascript
var gulp = require('gulp-runtime').create();

var watcher;
watcher = gulp.watch('app/*.js', 'minify browserSync');

gulp.watch('app/*.*', {
  wait: true,  // run tasks after change in series
  tasks: 'sass css jsx browserSync',
  reload: true // will only reload app/*.js files (server, db, etc)
}, function(ev, opt){
  var reloaded = opt.reload && ev.type !== 'delete';
  console.log('%s was %s', ev.path, ev.type, reloaded ? 'and reloaded' : '');
});
```
**/
Runtime.prototype.watch = function(glob, opt, cb){
  cb = util.type(cb || opt).function;
  opt = util.type(opt).plainObject || {tasks: opt};

  var tasks = util.type(opt.tasks);
  if(tasks.string || tasks.array){
    tasks = tasks.string || tasks.array.join(' ');
    if(cb || opt.reload){
      opt.onHandleEnd = function(next, ev){
        if(this.queue || this.host){ return ; }
        if(util.shouldReloadFile(opt, ev)){
          delete require.cache[ev.path];
          require(ev.path);
        }
        if(cb){ cb(ev, opt); }
      };
    }
    return vinylFS.watch(glob, this.stack(tasks, opt));
  }

  return vinylFS.watch(glob, opt, cb);
};

// export back tornado
//
exports = module.exports = tornado;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

class MyPoll extends Component {
  constructor(props){
    super(props);
    this.mydata = this.convertDataObjectToArray(props.data)
    this.min = props.min;
    this.drawChart = this.drawChart.bind(this);
    this.refreshOnWindowSizeChange = this.refreshOnWindowSizeChange.bind(this);
    this.convertDataObjectToArray = this.convertDataObjectToArray.bind(this);
  }

  componentDidMount(){
    this.drawChart(this.mydata);
    this.refreshOnWindowSizeChange();
  }

  convertDataObjectToArray(data){
    return Object.keys(data).map(itemName => {
      return {
        name: itemName,
        value: data[itemName]
      };
    });
  }

  drawChart(mydata){
    const baseElement = document.getElementById('chartContainer');
    baseElement.querySelector("svg").innerHTML = '';
    const fullWidth = document.getElementById('chartContainer').clientWidth;
    const chartHeight = window.innerHeight / 2;
    const barWidth = fullWidth * 0.9;
    const barHeight = chartHeight * 0.7;

    const labels = mydata.map(x => x.name);

    const labelScaler = d3.scaleBand()
      .domain(labels)
      .range([0, barWidth]);
    const xAxis = d3.axisBottom(labelScaler);

    const dataInfo = mydata.reduce((p, x) => {
      const max = (p.max > x.value) ? p.max : x.value;
      const min = (p.min < x.value) ? p.min : x.value;
      return {max, min};
    }, {max: Number.MIN_VALUE, min: Number.MAX_VALUE});

    const widthScaler = d3.scaleLinear()
      .domain([0, mydata.length])
      .range([0, barWidth]);

    const heightScaler = d3.scaleLinear()
      .domain([0, dataInfo.max])
      .range([barHeight, 0]);

    const yAxis = d3.axisLeft(heightScaler);

    let svg = d3.select("svg")
      .attr("width", fullWidth)
      .attr("height", chartHeight);

    let g = svg.append("g")
      .attr("transform", `translate(${fullWidth - barWidth}, 10)`)
    ;

    g.append("g")
      .call(yAxis)
      .attr("transform", `translate(-1, 0)`);

    g.append("g")
      .attr("transform", `translate(0, ${barHeight})`)
      .call(xAxis)
      .selectAll('text')
      .attr("y", 0)
      .attr("x", 9)
      .attr("dy", ".35em")
      .attr("transform", "rotate(90)")
      .style("text-anchor", "start");

    const dataPoint = g.selectAll(".bar")
      .data(mydata)
      .enter();

    dataPoint.append("rect")
      .attr("width", widthScaler(1) * 0.9 )
      .attr("height", ({value}) => barHeight - heightScaler(value))
      .attr("x", (d, i) => widthScaler(i) + widthScaler(1) * 0.05 )
      .attr("y", ({value}) => heightScaler(value))
      .attr("class", "shadowBar");

    dataPoint.append("rect")
      .attr("width", widthScaler(1) * 0.9 )
      .attr("height", ({value}) => barHeight - heightScaler(value))
      .attr("x", (d, i) => widthScaler(i))
      .attr("y", ({value}) => heightScaler(value))
      .attr("class", "bar");
  }

  refreshOnWindowSizeChange(){
    const mydata = this.mydata;
    const drawChart = this.drawChart;
    window.addEventListener("resize", ()=>drawChart(mydata));
  }

  render(){
    return(

        <svg/>

    );
  }
}
MyPoll.propTypes = {
  data: PropTypes.object,
  min: PropTypes.number
};

export default MyPoll;
<commit_msg>Fixed bug chart would show the last chart loaded<commit_after>import React, { Component, PropTypes } from 'react';

class MyPoll extends Component {
  constructor(props){
    super(props);
    this.min = props.min;
    this.drawChart = this.drawChart.bind(this);
    this.refreshOnWindowSizeChange = this.refreshOnWindowSizeChange.bind(this);
    this.convertDataObjectToArray = this.convertDataObjectToArray.bind(this);
  }

  componentDidMount(){
    this.drawChart(this.props.data);
    this.refreshOnWindowSizeChange();
    console.log('componentDidMount');
  }

  componentDidUpdate(){
    this.drawChart(this.props.data);
  }

  convertDataObjectToArray(data){
    return Object.keys(data).map(itemName => {
      return {
        name: itemName,
        value: data[itemName]
      };
    });
  }

  drawChart(mydata){
    console.log('updating chart with ', mydata)
    mydata = this.convertDataObjectToArray(mydata);
    const baseElement = document.getElementById('chartContainer');
    baseElement.querySelector("svg").innerHTML = '';
    const fullWidth = document.getElementById('chartContainer').clientWidth;
    const chartHeight = window.innerHeight / 2;
    const barWidth = fullWidth * 0.9;
    const barHeight = chartHeight * 0.7;

    const labels = mydata.map(x => x.name);

    const labelScaler = d3.scaleBand()
      .domain(labels)
      .range([0, barWidth]);
    const xAxis = d3.axisBottom(labelScaler);

    const dataInfo = mydata.reduce((p, x) => {
      const max = (p.max > x.value) ? p.max : x.value;
      const min = (p.min < x.value) ? p.min : x.value;
      return {max, min};
    }, {max: Number.MIN_VALUE, min: Number.MAX_VALUE});

    const widthScaler = d3.scaleLinear()
      .domain([0, mydata.length])
      .range([0, barWidth]);

    const heightScaler = d3.scaleLinear()
      .domain([0, dataInfo.max])
      .range([barHeight, 0]);

    const yAxis = d3.axisLeft(heightScaler);

    let svg = d3.select("svg")
      .attr("width", fullWidth)
      .attr("height", chartHeight);

    let g = svg.append("g")
      .attr("transform", `translate(${fullWidth - barWidth}, 10)`)
    ;

    g.append("g")
      .call(yAxis)
      .attr("transform", `translate(-1, 0)`);

    g.append("g")
      .attr("transform", `translate(0, ${barHeight})`)
      .call(xAxis)
      .selectAll('text')
      .attr("y", 0)
      .attr("x", 9)
      .attr("dy", ".35em")
      .attr("transform", "rotate(90)")
      .style("text-anchor", "start");

    const dataPoint = g.selectAll(".bar")
      .data(mydata)
      .enter();

    dataPoint.append("rect")
      .attr("width", widthScaler(1) * 0.9 )
      .attr("height", ({value}) => barHeight - heightScaler(value))
      .attr("x", (d, i) => widthScaler(i) + widthScaler(1) * 0.05 )
      .attr("y", ({value}) => heightScaler(value))
      .attr("class", "shadowBar");

    dataPoint.append("rect")
      .attr("width", widthScaler(1) * 0.9 )
      .attr("height", ({value}) => barHeight - heightScaler(value))
      .attr("x", (d, i) => widthScaler(i))
      .attr("y", ({value}) => heightScaler(value))
      .attr("class", "bar");
  }

  refreshOnWindowSizeChange(){
    const mydata = this.props.data;
    const drawChart = this.drawChart;
    window.addEventListener("resize", ()=>drawChart(mydata));
  }

  render(){
    console.log('from render', this.props.data)
    return(

        <svg/>

    );
  }
}
MyPoll.propTypes = {
  data: PropTypes.object,
  min: PropTypes.number
};

export default MyPoll;
<|endoftext|>


<commit_before>function rWorker(fun, callback) {
	var obj = {
		fun: fun,
		data: function (dat) {
			if (!this._r) {
				this._r = dat;
			}
			else {
				this._r = this.fun(this._r, dat);
			}
		},
		fetch: function () {
			return this._r;
		},
		close: function (silent, cb) {
			if (!silent) {
				cb(this._r);
			}
			self.terminate;
		}
	};
	var worker = object(obj);
	worker.on('message', callback);
	return worker;
}<commit_msg>fix for fake workers<commit_after>function rWorker(fun, callback) {
	var obj = {
		fun: fun,
		data: function (dat) {
			if (!this._r) {
				this._r = dat;
			}
			else {
				this._r = this.fun(this._r, dat);
			}
		},
		fetch: function () {
			this.fire('msg',this._r);
		},
		close: function (silent) {
			if (!silent) {
				this.fire('msg',this._r);
			}
			self.terminate;
		}
	};
	var worker = object(obj);
	worker.on('msg', callback);
	return worker;
}<|endoftext|>