<commit_before>define(["js/core/EventDispatcher", "js/lib/parser", "js/core/Binding","underscore"],

    function (EventDispatcher, Parser, Binding, _) {
        // global invalidation timer
        var globalInvalidationQueue = (function() {

            var id,
                callbacks = [],
                work = function() {
                    for (var i = 0; i < callbacks.length; i++) {
                        try {
                            callbacks[i]();
                        } catch (e) {
                            (console.warn || console.log).call(console, e);
                        }
                    }

                    callbacks = [];
                    id = null;
                };

            return {
                addCallback: function(callback) {
                    callbacks.push(callback);

                    if (!id) {
                        id = setTimeout(work, 1000 / 60);
                    }
                }
            }
        })();

        var Bindable = EventDispatcher.inherit("js.core.Bindable",
            {
                /***
                 * creates a new instance of bindable, which can be bound to components
                 *
                 * @param {Object} [attributes] the default attributes which will be set during instantiation
                 */
                ctor: function (attributes) {
                    this.$eventBindables = [];

                    // call the base class constructor
                    this.callBase(null);

                    this.$ = {};
                    this.$invalidatedProperties = {};
                    this.$invalidated = false;

                    _.extend(this._eventAttributes, this.base._eventAttributes || {});

                    attributes = attributes || {};

                    var defaultAttributes = this._defaultAttributes();
                    for (var key in defaultAttributes) {
                        if (defaultAttributes.hasOwnProperty(key)) {
                            if (!attributes.hasOwnProperty(key)) {
                                if (_.isFunction(defaultAttributes[key])) {
                                    // Function as default -> construct new Object
                                    attributes[key] = new (defaultAttributes[key])();
                                } else {
                                    attributes[key] = _.clone(defaultAttributes[key]);
                                }
                            }
                        }
                    }

                    this.$ = attributes;
                    // TODO: clone and keep prototype for attribute the same -> write own clone method
                    this.$previousAttributes = _.clone(attributes);

                },
                /**
                 * Here you can define the default attributes of the instance.
                 *
                 * @static
                 */
                defaults: {
                },
                /**
                 * Writes attributes back to the source
                 */
                sync: function(){
                    if(this._$source) {
                        var val, attributes = {}, unsetAttributes = {};
                        for (var key in this.$) {
                            if (this.$.hasOwnProperty(key)) {
                                val = this.$[key];
                                if(val instanceof Bindable && val.sync()){
                                    attributes[key] = val._$source;
                                }else{
                                    attributes[key] = val;
                                }
                            }
                        }
                        // remove all attributes, which are not in clone
                        for (var sourceKey in this._$source.$){
                            if(this._$source.$.hasOwnProperty(sourceKey)){
                                if(!attributes.hasOwnProperty(sourceKey)){
                                    unsetAttributes[sourceKey] = "";
                                }
                            }
                        }
                        this._$source.set(unsetAttributes,{unset:true});
                        this._$source.set(attributes);
                        return true;
                    }else{
                        return false;
                    }

                },
                /***
                 * This method returns a copy of the Object
                 * @return js.core.Bindable a fresh copy of the Bindable
                 */
                clone: function () {
                    var ret = {};
                    for (var key in this.$) {
                        if (this.$.hasOwnProperty(key)) {
                            ret[key] = this._cloneAttribute(this.$[key], key);
                        }
                    }
                    var b = new this.factory(ret);
                    b._$source = this;
                    return b;
                },
                /**
                 * Returns a copy of the attribute. This method is a hook for further cloning options
                 * @param attribute
                 * @param key
                 * @private
                 */
                _cloneAttribute: function(attribute, key){
                    if(attribute instanceof Bindable){
                        return attribute.clone();
                    }else if(_.isArray(attribute)){
                        var retArray = [];
                        for(var i = 0; i < attribute.length; i++){
                            retArray.push(this._cloneAttribute(attribute[i]));
                        }
                        return retArray;
                    } else if(_.isObject(attribute)){
                        var retObject = {};
                        for (var attrKey in attribute){
                            if(attribute.hasOwnProperty(attrKey)){
                                retObject[attrKey] = this._cloneAttribute(attribute[attrKey], attrKey);
                            }
                        }
                        return retObject;
                    } else{
                        return attribute;
                    }
                },
                /***
                 *
                 * @return {Object} returns the default attributes and includes the defaults from base classes
                 * @private
                 */
                _defaultAttributes: function () {
                    return this._generateDefaultsChain("defaults");
                },

                /***
                 * generates a default chain containing the values from this instance and base classes
                 *
                 * @param {String} property - the name of the static property used to find defaults
                 * @return {*}
                 * @private
                 */
                _generateDefaultsChain: function (property) {

                    this.factory.chainCache = this.factory.chainCache || {};

                    if (this.factory.chainCache[property]) {
                        return this.factory.chainCache[property];
                    }

                    var ret = this[property],
                        base = this.base;

                    while (base) {
                        _.defaults(ret, base[property]);
                        base = base.base;
                    }

                    // cache the chain
                    this.factory.chainCache[property] = ret;

                    return ret;
                },

                /**
                 * Sets new values for attributes and notify about changes
                 *
                 * @param {String} key The attribute key
                 * @param {String} value The attribute value
                 * @param {Object} options A hash of options
                 * @param {Boolean} [options.silent=false] if true no event is triggered on change
                 * @param {Boolean} [options.unset=false] if true the attribute gets deleted
                 */
                set: function (key, value, options) {
                    var attributes = {};

                    if(_.isNumber(key)){
                        key = String(key);
                    }

                    if (_.isString(key)) {
                        attributes[key] = value;
                    } else {
                        options = value;
                        attributes = key;
                    }

                    options = options || {silent: false, unset: false};

                    // for un-setting attributes
                    if (options.unset) {
                        for (key in attributes) {
                            if (attributes.hasOwnProperty(key)) {
                                attributes[key] = void 0;
                            }
                        }
                    }

                    var changedAttributes = {},
                        changedAttributesCount = 0,
                        now = this.$,
                        val, prev;

                    for (key in attributes) {
                        if (attributes.hasOwnProperty(key)) {
                            // get the value
                            val = attributes[key];
                            // unset attribute or change it ...
                            if (options.unset === true) {
                                prev = now[key];
                                delete now[key];
                                changedAttributes[key] = undefined;
                                changedAttributesCount++;
                                this.$previousAttributes[key] = prev;
                            } else {
                                if (!_.isEqual(now[key], attributes[key])) {
                                    prev = now[key];
                                    this.$previousAttributes[key] = prev;
                                    now[key] = attributes[key];
                                    changedAttributes[key] = now[key];

                                    changedAttributesCount++;
                                }
                            }
                        }
                    }

                    if (changedAttributesCount) {
                        for (key in changedAttributes) {
                            if (changedAttributes.hasOwnProperty(key)) {
                                var commitMethodName = '_commit' + key.charAt(0).toUpperCase() + key.substr(1);

                                if (this[commitMethodName] instanceof Function) {
                                    // call method

                                    if (this[commitMethodName](now[key], this.$previousAttributes[key]) === false) {
                                        // false returned rollback
                                        changedAttributesCount--;
                                        now[key] = this.$previousAttributes[key];
                                    }
                                }
                            }
                        }

                        if (changedAttributesCount) {
                            this._commitChangedAttributes(changedAttributes);
                            if (options.silent === false) {
                                for (key in changedAttributes) {
                                    if (changedAttributes.hasOwnProperty(key)) {
                                        this.trigger('change:' + key, changedAttributes[key], this);
                                    }
                                }
                                this.trigger('change', changedAttributes, this);
                            }
                        }

                    }

                    return this;
                },

                setLater: function(key, value) {

                    if (_.isString(key)) {
                        key = {};
                        key[key] = value;
                    }

                    _.extend(this.$invalidatedProperties, key);

                    if (!this.$invalidated) {
                        this.$invalidated = true;
                        globalInvalidationQueue.addCallback(this._commitInvalidatedAttributes);
                    }

                },

                _commitInvalidatedAttributes: function() {
                    this.set(this.$invalidatedProperties);
                    this.$invalidatedProperties = {};
                },

                /***
                 * evaluates a path to retrieve a value
                 *
                 * @param {Object} [scope=this] the scope where the path is evaluated
                 * @param {String} key
                 * @returns the value for the path or undefined
                 */
                get: function(scope, key) {
                    if(!key){
                        key = scope;
                        scope = this;
                    }

                    if(!key) {
                        return null;
                    }

                    var path;

                    // if we have a path object
                    if(_.isArray(key)){
                        path = key;
                    // path element
                    }else if(_.isObject(key)){
                        path = [key];
                    }else{
                        path = Parser.parse(key, "path");
                    }

                    var pathElement, val;
                    for (var j = 0; scope && j < path.length; j++) {
                        pathElement = path[j];
                        if (pathElement.type == "fnc") {
                            var fnc = scope[pathElement.name];
                            var parameters = pathElement.parameter;
                            for (var i = 0; i < parameters.length; i++) {
                                var param = parameters[i];
                                if (_.isObject(param)) {
                                    param.type = "static";
                                    parameters[i] = this.get(param.path);
                                }
                            }
                            scope = fnc.apply(scope, parameters);
                        } else if (pathElement.type == "var") {
                            if (scope instanceof Bindable) {
                                if (path.length - 1 === j ) {
                                    val = scope.$[pathElement.name];
                                    if(_.isUndefined(val)){
                                        val = scope[pathElement.name];
                                    }
                                    scope = val;
                                } else {
                                    scope = scope.get(pathElement.name);
                                }

                            } else {
                                scope = scope[pathElement.name];
                            }
                        }

                        if (scope && pathElement.index !== '') {
                            // if it's an array
                            if (_.isArray(scope)) {
                                scope = scope[pathElement.index];
                                // if it's a list
                            } else if (scope.at) {
                                scope = scope.at(pathElement.index);
                            }
                        }
                    }
                    return scope;
                },

                /***
                 * determinate if a attribute is available
                 *
                 * @param {String} path - to get the value
                 * @returns {Boolean} true if attribute is not undefined
                 */
                has: function (path) {
                    return !_.isUndefined(this.get(path));
                },

                /***
                 * called after attributes has set and some of the has been changed
                 *
                 * @param {Object} attributes - contains the changed attributes
                 *
                 * @abstract
                 * @private
                 */
                _commitChangedAttributes: function (attributes) {
                    // override
                },

                /***
                 * Unset attribute on $
                 * @param {String|Object} key - the attribute or attributes to unset
                 * @param {Object} [options]
                 * @return {this}
                 */
                unset: function (key, options) {
                    (options || (options = {})).unset = true;
                    return this.set(key, null, options);
                },

                /***
                 * Clears all attributes
                 * @return {this}
                 */
                clear: function () {
                    return this.set(this.$, {unset: true});
                },

                /***
                 * Binds an event handler function to an EventDispatcher
                 *
                 * @param {String} path - a binding path e.g. a.b.c
                 * @param {String} event - the type of the event which should be bound
                 * @param {Function} callback - the event handler function
                 * @param {Object} [thisArg] - the thisArg used for calling the event handler
                 */
                bind: function (path, event, callback, thisArg) {
                    if (event instanceof Function && _.isString(path)) {
                        this.callBase(path, event, callback);
                    } else {
                        if(_.isArray(path) && path.length > 0){
                            thisArg = callback;
                            callback = event;
                            event = path[1];
                            path = path[0];
                        }
                        var eb = new EventBindable({
                            path: path,
                            event: event,
                            scope: thisArg,
                            callback: callback,
                            value: null
                        });
                        eb.set('binding', new Binding({path: path, scope: this, target: eb, targetKey: 'value'}));
                        this.$eventBindables.push(eb);
                    }
                },

                /***
                 * Unbinds an bound event handler from an EventDispatcher.
                 *
                 * @param {String} [path] - the path from which the event should be unbound
                 * @param {String} event - the type of the event
                 * @param {Function} callback - the event handler which is currently bound
                 * TODO: why a scope is passed here?
                 * @param {Object} [scope]
                 */
                unbind: function (path, event, callback, scope) {
                    if (event instanceof Function) {
                        this.callBase(path, event, callback);
                    } else {
                        var eb;
                        for (var i = this.$eventBindables.length - 1; i >= 0; i--) {
                            eb = this.$eventBindables[i];
                            if (eb.$.scope === scope && eb.$.path === path && eb.$.event === event && eb.$.callback === callback) {
                                // unbind
                                eb.destroy();
                                this.$eventBindables.slice(i, 1);
                            }
                        }
                    }
                },

                /***
                 * Destroys all event bindings and triggers a destroy event
                 * @return {this}
                 */
                destroy: function() {
                    this.callBase();
                    for(var i = 0; i < this.$eventBindables.length; i++){
                        this.$eventBindables[i].destroy();
                    }

                    this.trigger('destroy',this);
                    return this;
                }
            });

        var EventBindable = Bindable.inherit({
            _commitChangedAttributes: function(attributes){
                this.callBase();
                if(attributes.binding){
                    this.set('value', attributes.binding.getValue());
                }else if(attributes.value){
                    var value = attributes.value;
                    this._unbindEvent(this.$previousAttributes['value']);
                    if (!_.isUndefined(value)) {
                        this._bindEvent(value);
                    }
                }

            },
            _unbindEvent: function (value) {
                if (value && value instanceof EventDispatcher) {
                    value.unbind(this.$.event, this.$.callback, this.$.scope);
                }
            },
            _bindEvent: function (value) {
                if (value && value instanceof EventDispatcher) {
                    value.bind(this.$.event, this.$.callback, this.$.scope);
                }
            },
            destroy: function () {
                this._unbindEvent(this.$.value);
                this.$.binding.destroy();

                this.callBase();
            }
        });

        return Bindable;

    });<commit_msg>initialize previousAttributes as empty hash<commit_after>define(["js/core/EventDispatcher", "js/lib/parser", "js/core/Binding","underscore"],

    function (EventDispatcher, Parser, Binding, _) {
        // global invalidation timer
        var globalInvalidationQueue = (function() {

            var id,
                callbacks = [],
                work = function() {
                    for (var i = 0; i < callbacks.length; i++) {
                        try {
                            callbacks[i]();
                        } catch (e) {
                            (console.warn || console.log).call(console, e);
                        }
                    }

                    callbacks = [];
                    id = null;
                };

            return {
                addCallback: function(callback) {
                    callbacks.push(callback);

                    if (!id) {
                        id = setTimeout(work, 1000 / 60);
                    }
                }
            }
        })();

        var Bindable = EventDispatcher.inherit("js.core.Bindable",
            {
                /***
                 * creates a new instance of bindable, which can be bound to components
                 *
                 * @param {Object} [attributes] the default attributes which will be set during instantiation
                 */
                ctor: function (attributes) {
                    this.$eventBindables = [];

                    // call the base class constructor
                    this.callBase(null);

                    this.$ = {};
                    this.$invalidatedProperties = {};
                    this.$invalidated = false;

                    _.extend(this._eventAttributes, this.base._eventAttributes || {});

                    attributes = attributes || {};

                    var defaultAttributes = this._defaultAttributes();
                    for (var key in defaultAttributes) {
                        if (defaultAttributes.hasOwnProperty(key)) {
                            if (!attributes.hasOwnProperty(key)) {
                                if (_.isFunction(defaultAttributes[key])) {
                                    // Function as default -> construct new Object
                                    attributes[key] = new (defaultAttributes[key])();
                                } else {
                                    attributes[key] = _.clone(defaultAttributes[key]);
                                }
                            }
                        }
                    }

                    this.$ = attributes;

                    this.$previousAttributes = {}

                },
                /**
                 * Here you can define the default attributes of the instance.
                 *
                 * @static
                 */
                defaults: {
                },
                /**
                 * Writes attributes back to the source
                 */
                sync: function(){
                    if(this._$source) {
                        var val, attributes = {}, unsetAttributes = {};
                        for (var key in this.$) {
                            if (this.$.hasOwnProperty(key)) {
                                val = this.$[key];
                                if(val instanceof Bindable && val.sync()){
                                    attributes[key] = val._$source;
                                }else{
                                    attributes[key] = val;
                                }
                            }
                        }
                        // remove all attributes, which are not in clone
                        for (var sourceKey in this._$source.$){
                            if(this._$source.$.hasOwnProperty(sourceKey)){
                                if(!attributes.hasOwnProperty(sourceKey)){
                                    unsetAttributes[sourceKey] = "";
                                }
                            }
                        }
                        this._$source.set(unsetAttributes,{unset:true});
                        this._$source.set(attributes);
                        return true;
                    }else{
                        return false;
                    }

                },
                /***
                 * This method returns a copy of the Object
                 * @return js.core.Bindable a fresh copy of the Bindable
                 */
                clone: function () {
                    var ret = {};
                    for (var key in this.$) {
                        if (this.$.hasOwnProperty(key)) {
                            ret[key] = this._cloneAttribute(this.$[key], key);
                        }
                    }
                    var b = new this.factory(ret);
                    b._$source = this;
                    return b;
                },
                /**
                 * Returns a copy of the attribute. This method is a hook for further cloning options
                 * @param attribute
                 * @param key
                 * @private
                 */
                _cloneAttribute: function(attribute, key){
                    if(attribute instanceof Bindable){
                        return attribute.clone();
                    }else if(_.isArray(attribute)){
                        var retArray = [];
                        for(var i = 0; i < attribute.length; i++){
                            retArray.push(this._cloneAttribute(attribute[i]));
                        }
                        return retArray;
                    } else if(_.isObject(attribute)){
                        var retObject = {};
                        for (var attrKey in attribute){
                            if(attribute.hasOwnProperty(attrKey)){
                                retObject[attrKey] = this._cloneAttribute(attribute[attrKey], attrKey);
                            }
                        }
                        return retObject;
                    } else{
                        return attribute;
                    }
                },
                /***
                 *
                 * @return {Object} returns the default attributes and includes the defaults from base classes
                 * @private
                 */
                _defaultAttributes: function () {
                    return this._generateDefaultsChain("defaults");
                },

                /***
                 * generates a default chain containing the values from this instance and base classes
                 *
                 * @param {String} property - the name of the static property used to find defaults
                 * @return {*}
                 * @private
                 */
                _generateDefaultsChain: function (property) {

                    this.factory.chainCache = this.factory.chainCache || {};

                    if (this.factory.chainCache[property]) {
                        return this.factory.chainCache[property];
                    }

                    var ret = this[property],
                        base = this.base;

                    while (base) {
                        _.defaults(ret, base[property]);
                        base = base.base;
                    }

                    // cache the chain
                    this.factory.chainCache[property] = ret;

                    return ret;
                },

                /**
                 * Sets new values for attributes and notify about changes
                 *
                 * @param {String} key The attribute key
                 * @param {String} value The attribute value
                 * @param {Object} options A hash of options
                 * @param {Boolean} [options.silent=false] if true no event is triggered on change
                 * @param {Boolean} [options.unset=false] if true the attribute gets deleted
                 */
                set: function (key, value, options) {
                    var attributes = {};

                    if(_.isNumber(key)){
                        key = String(key);
                    }

                    if (_.isString(key)) {
                        attributes[key] = value;
                    } else {
                        options = value;
                        attributes = key;
                    }

                    options = options || {silent: false, unset: false, force: false};

                    // for un-setting attributes
                    if (options.unset) {
                        for (key in attributes) {
                            if (attributes.hasOwnProperty(key)) {
                                attributes[key] = void 0;
                            }
                        }
                    }

                    var changedAttributes = {},
                        changedAttributesCount = 0,
                        now = this.$,
                        val, prev;

                    for (key in attributes) {
                        if (attributes.hasOwnProperty(key)) {
                            // get the value
                            val = attributes[key];
                            // unset attribute or change it ...
                            if (options.unset === true) {
                                prev = now[key];
                                delete now[key];
                                changedAttributes[key] = undefined;
                                changedAttributesCount++;
                                this.$previousAttributes[key] = prev;
                            } else {
                                if (options.force || !_.isEqual(now[key], attributes[key])) {
                                    prev = now[key];
                                    this.$previousAttributes[key] = prev;
                                    now[key] = attributes[key];
                                    changedAttributes[key] = now[key];

                                    changedAttributesCount++;
                                }
                            }
                        }
                    }

                    if (changedAttributesCount) {
                        for (key in changedAttributes) {
                            if (changedAttributes.hasOwnProperty(key)) {
                                var commitMethodName = '_commit' + key.charAt(0).toUpperCase() + key.substr(1);

                                if (this[commitMethodName] instanceof Function) {
                                    // call method

                                    if (this[commitMethodName](now[key], this.$previousAttributes[key]) === false) {
                                        // false returned rollback
                                        changedAttributesCount--;
                                        now[key] = this.$previousAttributes[key];
                                    }
                                }
                            }
                        }

                        if (changedAttributesCount) {
                            this._commitChangedAttributes(changedAttributes);
                            if (options.silent === false) {
                                for (key in changedAttributes) {
                                    if (changedAttributes.hasOwnProperty(key)) {
                                        this.trigger('change:' + key, changedAttributes[key], this);
                                    }
                                }
                                this.trigger('change', changedAttributes, this);
                            }
                        }

                    }

                    return this;
                },

                setLater: function(key, value) {

                    if (_.isString(key)) {
                        key = {};
                        key[key] = value;
                    }

                    _.extend(this.$invalidatedProperties, key);

                    if (!this.$invalidated) {
                        this.$invalidated = true;
                        globalInvalidationQueue.addCallback(this._commitInvalidatedAttributes);
                    }

                },

                _commitInvalidatedAttributes: function() {
                    this.set(this.$invalidatedProperties);
                    this.$invalidatedProperties = {};
                },

                /***
                 * evaluates a path to retrieve a value
                 *
                 * @param {Object} [scope=this] the scope where the path is evaluated
                 * @param {String} key
                 * @returns the value for the path or undefined
                 */
                get: function(scope, key) {
                    if(!key){
                        key = scope;
                        scope = this;
                    }

                    if(!key) {
                        return null;
                    }

                    var path;

                    // if we have a path object
                    if(_.isArray(key)){
                        path = key;
                    // path element
                    }else if(_.isObject(key)){
                        path = [key];
                    }else{
                        path = Parser.parse(key, "path");
                    }

                    var pathElement, val;
                    for (var j = 0; scope && j < path.length; j++) {
                        pathElement = path[j];
                        if (pathElement.type == "fnc") {
                            var fnc = scope[pathElement.name];
                            var parameters = pathElement.parameter;
                            for (var i = 0; i < parameters.length; i++) {
                                var param = parameters[i];
                                if (_.isObject(param)) {
                                    param.type = "static";
                                    parameters[i] = this.get(param.path);
                                }
                            }
                            scope = fnc.apply(scope, parameters);
                        } else if (pathElement.type == "var") {
                            if (scope instanceof Bindable) {
                                if (path.length - 1 === j ) {
                                    val = scope.$[pathElement.name];
                                    if(_.isUndefined(val)){
                                        val = scope[pathElement.name];
                                    }
                                    scope = val;
                                } else {
                                    scope = scope.get(pathElement.name);
                                }

                            } else {
                                scope = scope[pathElement.name];
                            }
                        }

                        if (scope && pathElement.index !== '') {
                            // if it's an array
                            if (_.isArray(scope)) {
                                scope = scope[pathElement.index];
                                // if it's a list
                            } else if (scope.at) {
                                scope = scope.at(pathElement.index);
                            }
                        }
                    }
                    return scope;
                },

                /***
                 * determinate if a attribute is available
                 *
                 * @param {String} path - to get the value
                 * @returns {Boolean} true if attribute is not undefined
                 */
                has: function (path) {
                    return !_.isUndefined(this.get(path));
                },

                /***
                 * called after attributes has set and some of the has been changed
                 *
                 * @param {Object} attributes - contains the changed attributes
                 *
                 * @abstract
                 * @private
                 */
                _commitChangedAttributes: function (attributes) {
                    // override
                },

                /***
                 * Unset attribute on $
                 * @param {String|Object} key - the attribute or attributes to unset
                 * @param {Object} [options]
                 * @return {this}
                 */
                unset: function (key, options) {
                    (options || (options = {})).unset = true;
                    return this.set(key, null, options);
                },

                /***
                 * Clears all attributes
                 * @return {this}
                 */
                clear: function () {
                    return this.set(this.$, {unset: true});
                },

                /***
                 * Binds an event handler function to an EventDispatcher
                 *
                 * @param {String} path - a binding path e.g. a.b.c
                 * @param {String} event - the type of the event which should be bound
                 * @param {Function} callback - the event handler function
                 * @param {Object} [thisArg] - the thisArg used for calling the event handler
                 */
                bind: function (path, event, callback, thisArg) {
                    if (event instanceof Function && _.isString(path)) {
                        this.callBase(path, event, callback);
                    } else {
                        if(_.isArray(path) && path.length > 0){
                            thisArg = callback;
                            callback = event;
                            event = path[1];
                            path = path[0];
                        }
                        var eb = new EventBindable({
                            path: path,
                            event: event,
                            scope: thisArg,
                            callback: callback,
                            value: null
                        });
                        eb.set('binding', new Binding({path: path, scope: this, target: eb, targetKey: 'value'}));
                        this.$eventBindables.push(eb);
                    }
                },

                /***
                 * Unbinds an bound event handler from an EventDispatcher.
                 *
                 * @param {String} [path] - the path from which the event should be unbound
                 * @param {String} event - the type of the event
                 * @param {Function} callback - the event handler which is currently bound
                 * TODO: why a scope is passed here?
                 * @param {Object} [scope]
                 */
                unbind: function (path, event, callback, scope) {
                    if (event instanceof Function) {
                        this.callBase(path, event, callback);
                    } else {
                        var eb;
                        for (var i = this.$eventBindables.length - 1; i >= 0; i--) {
                            eb = this.$eventBindables[i];
                            if (eb.$.scope === scope && eb.$.path === path && eb.$.event === event && eb.$.callback === callback) {
                                // unbind
                                eb.destroy();
                                this.$eventBindables.slice(i, 1);
                            }
                        }
                    }
                },

                /***
                 * Destroys all event bindings and triggers a destroy event
                 * @return {this}
                 */
                destroy: function() {
                    this.callBase();
                    for(var i = 0; i < this.$eventBindables.length; i++){
                        this.$eventBindables[i].destroy();
                    }

                    this.trigger('destroy',this);
                    return this;
                }
            });

        var EventBindable = Bindable.inherit({
            _commitChangedAttributes: function(attributes){
                this.callBase();
                if(attributes.binding){
                    this.set('value', attributes.binding.getValue());
                }else if(attributes.value){
                    var value = attributes.value;
                    this._unbindEvent(this.$previousAttributes['value']);
                    if (!_.isUndefined(value)) {
                        this._bindEvent(value);
                    }
                }

            },
            _unbindEvent: function (value) {
                if (value && value instanceof EventDispatcher) {
                    value.unbind(this.$.event, this.$.callback, this.$.scope);
                }
            },
            _bindEvent: function (value) {
                if (value && value instanceof EventDispatcher) {
                    value.bind(this.$.event, this.$.callback, this.$.scope);
                }
            },
            destroy: function () {
                this._unbindEvent(this.$.value);
                this.$.binding.destroy();

                this.callBase();
            }
        });

        return Bindable;

    });<|endoftext|>


<commit_before>$(document).ready(function() {
    $('#inputConsole').find('input').focus();
});
$('#inputConsole').cssConsole({
	inputName:'console',
	charLimit: 60,
	onEnter: function(){
		addLine("> "+$('#inputConsole').find('input').val());
		execCommand($('#inputConsole').find('input').val());
		$('#inputConsole').cssConsole('reset');
		$('#inputConsole').find('input').focus();
	}
});
var lineLimit = 28;
$('.containerConsole').on('click', function() {
	$('#inputConsole').find('input').focus();
});
function addLine(input, style, color) {
		if($('.consoleConsole div').length==lineLimit) {
			$('.consoleConsole div').eq(0).remove();
		}
		style = typeof style !== 'undefined' ? style : 'line';
		color = typeof color !== 'undefined' ? color : 'white';
		$('.consoleConsole').append('
'+input+'
');
}
function execCommand(command){
    if ( commands[command] ) {
      return commands[command]();
    } else {
      addLine("Command '" + command + "' was not found.");
    }
}
var commands = {
	help: function (){
		addLine("Available command list:");
		addLine("Type `help name' to find out more about the function `name'.");
		addLine("git init", 'margin');
		addLine("git status", 'margin');
		addLine("git add", 'margin');
		addLine("git commit", 'margin');
		addLine("git clone", 'margin');
		addLine("git pull", 'margin');
	},

	"help git init": function (){
		addLine("Command description:");
		addLine("$ git init [project-name]", 'margin');
		addLine("Creates a new local repository with the specified name.");
	},

	"help git status": function (){
		addLine("Command description:");
		addLine("$ git status", 'margin');
		addLine("Lists all new or modified files to be commited.");
	},

	"help git add": function (){
		addLine("Command description:");
		addLine("$ git add [file]", 'margin');
		addLine("Snapshots the file in preparation for versioning.");
	},

	"help git commit": function (){
		addLine("Command description:");
		addLine("$ git commit -m [descriptive message]", 'margin');
		addLine("Records file snapshots permanently in version history.");
	},

	"help git clone": function (){
		addLine("Command description:");
		addLine("$ git clone [url]", 'margin');
		addLine("Downloads a project and its entire version history.");
	},

	"help git pull": function (){
		addLine("Command description:");
		addLine("$ git pull", 'margin');
		addLine("Downloads bookmark history and incorporates changes.");
	},

	"git init": function (){
		addLine("Initialized empty Git repository in /home/root/.git/");
	},

	"git status": function (){
		addLine("On branch master");
		addLine("Initial commit", 'margin');
		addLine(" ", 'margin');
		addLine("Untracked files:");
		addLine("./html",'margin');
		addLine("./html/index.html",'margin');
		addLine(' (use "git add <file>..." to include in what will be committed) ');
	},

	dir: function(){
		addLine(".");
		addLine("..");
		addLine("Applications", 'margin', 'blue');
		addLine("Documents", 'margin', 'blue');
		addLine("Downloads", 'margin', 'blue');
		addLine("Movies", 'margin', 'blue');
		addLine("Music", 'margin', 'blue');
		addLine("System", 'margin', 'blue');
	},
	ps: function() {
		addLine("Running processes:");
		addLine("name: browser pid:8876", 'margin');
		addLine("name: movie player pid:3213", 'margin');
		addLine("name: system pid:0012", 'margin');
	}
}<commit_msg>bugfix css og js terminal<commit_after>$(document).ready(function() {
    $('#inputConsole').find('input').focus();
});
$('#inputConsole').cssConsole({
	inputName:'consoleConsole',
	charLimit: 60,
	onEnter: function(){
		addLine("> "+$('#inputConsole').find('input').val());
		execCommand($('#inputConsole').find('input').val());
		$('#inputConsole').cssConsole('reset');
		$('#inputConsole').find('input').focus();
	}
});
var lineLimit = 28;
$('.containerConsole').on('click', function() {
	$('#inputConsole').find('input').focus();
});
function addLine(input, style, color) {
		if($('.consoleConsole div').length==lineLimit) {
			$('.consoleConsole div').eq(0).remove();
		}
		style = typeof style !== 'undefined' ? style : 'line';
		color = typeof color !== 'undefined' ? color : 'white';
		$('.consoleConsole').append('
'+input+'
');
}
function execCommand(command){
    if ( commands[command] ) {
      return commands[command]();
    } else {
      addLine("Command '" + command + "' was not found.");
    }
}
var commands = {
	help: function (){
		addLine("Available command list:");
		addLine("Type `help name' to find out more about the function `name'.");
		addLine("git init", 'margin');
		addLine("git status", 'margin');
		addLine("git add", 'margin');
		addLine("git commit", 'margin');
		addLine("git clone", 'margin');
		addLine("git pull", 'margin');
	},

	"help git init": function (){
		addLine("Command description:");
		addLine("$ git init [project-name]", 'margin');
		addLine("Creates a new local repository with the specified name.");
	},

	"help git status": function (){
		addLine("Command description:");
		addLine("$ git status", 'margin');
		addLine("Lists all new or modified files to be commited.");
	},

	"help git add": function (){
		addLine("Command description:");
		addLine("$ git add [file]", 'margin');
		addLine("Snapshots the file in preparation for versioning.");
	},

	"help git commit": function (){
		addLine("Command description:");
		addLine("$ git commit -m [descriptive message]", 'margin');
		addLine("Records file snapshots permanently in version history.");
	},

	"help git clone": function (){
		addLine("Command description:");
		addLine("$ git clone [url]", 'margin');
		addLine("Downloads a project and its entire version history.");
	},

	"help git pull": function (){
		addLine("Command description:");
		addLine("$ git pull", 'margin');
		addLine("Downloads bookmark history and incorporates changes.");
	},

	"git init": function (){
		addLine("Initialized empty Git repository in /home/root/.git/");
	},

	"git status": function (){
		addLine("On branch master");
		addLine("Initial commit", 'margin');
		addLine(" ", 'margin');
		addLine("Untracked files:");
		addLine("./html",'margin');
		addLine("./html/index.html",'margin');
		addLine(' (use "git add <file>..." to include in what will be committed) ');
	},

	dir: function(){
		addLine(".");
		addLine("..");
		addLine("Applications", 'margin', 'blue');
		addLine("Documents", 'margin', 'blue');
		addLine("Downloads", 'margin', 'blue');
		addLine("Movies", 'margin', 'blue');
		addLine("Music", 'margin', 'blue');
		addLine("System", 'margin', 'blue');
	},
	ps: function() {
		addLine("Running processes:");
		addLine("name: browser pid:8876", 'margin');
		addLine("name: movie player pid:3213", 'margin');
		addLine("name: system pid:0012", 'margin');
	}
}<|endoftext|>


<commit_before>/**
 * Created by nick on 5/24/17.
 *
 * All honeypot handling code in javascript should be in this file.
 */

var NIForm = NIForm || {};

/**
 * Set to true to debug the cookie alternate way of managing the honeypot
 */
NIForm.debugCookieAlt = false;

/**
 * Returns if cookies are enabled in the current browser.
 *
 * This acts as a wrapper to navigator.cookieEnabled if the browser
 * supports that parameter; Added a check for older browsers that
 * do not have this parameter
 *
 * Copied code from here: https://stackoverflow.com/a/6663901/1946899
 *
 * @return boolean
 */
NIForm.cookieEnabled = function () {
    // Check for debug mode.
    if (NIForm.debugCookieAlt) {
        return false;
    }

    if (navigator.cookieEnabled) return true;

// set and read cookie
    document.cookie = "cookietest=1";
    var ret = document.cookie.indexOf("cookietest=") != -1;

// delete cookie
    document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";

    return ret;
};

/**
 * Generates a GUID value.
 *
 * Copied function contents from here: https://stackoverflow.com/a/105074/1946899
 *
 * @returns {string}
 */
NIForm.generateGuid = function () {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    }

    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
        s4() + '-' + s4() + s4() + s4();
};

/**
 * This class handles setting up and
 * @param formId
 * @param tokenUrl
 * @param fieldName
 * @param honeypotIdFieldName
 * @constructor
 */
NIForm.Honeypot = function (formId, tokenUrl, fieldName, honeypotIdFieldName) {

    // Base submit data to send to honeypot token generator
    var submitData = {
        formId: formId
    };

    var honeypotId = null;
    // Need to add additional parameters if cookies are not supported
    if (!NIForm.cookieEnabled()) {
        honeypotId = NIForm.generateGuid();
        submitData.honeypotId = honeypotId;
    }

    jQuery.post(
        tokenUrl,
        submitData,
        function (data, textStatus, jqXHR) {
            var token = data.token;

            var $form = jQuery('#' + formId);
            // Add hidden fields to form.
            $form.append('<input type="hidden" name="' + fieldName + '" value="' + token + '">');

            if (honeypotId) {
                $form.append('<input type="hidden" name="' + honeypotIdFieldName + '" value="' + honeypotId + '">');
            }
        },
        'json'
    );
};

<commit_msg>Removed cookie check from Javascript code<commit_after>/**
 * Created by nick on 5/24/17.
 *
 * All honeypot handling code in javascript should be in this file.
 */

var NIForm = NIForm || {};

/**
 * Generates a GUID value.
 *
 * Copied function contents from here: https://stackoverflow.com/a/105074/1946899
 *
 * @returns {string}
 */
NIForm.generateGuid = function () {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    }

    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
        s4() + '-' + s4() + s4() + s4();
};

/**
 * This class handles setting up and
 * @param formId
 * @param tokenUrl
 * @param fieldName
 * @param honeypotIdFieldName
 * @constructor
 */
NIForm.Honeypot = function (formId, tokenUrl, fieldName, honeypotIdFieldName) {
    honeypotId = NIForm.generateGuid();

    // Base submit data to send to honeypot token generator
    var submitData = {
        formId: formId,
        honeypotId: honeypotId
    };

    jQuery.post(
        tokenUrl,
        submitData,
        function (data, textStatus, jqXHR) {
            var token = data.token;

            var $form = jQuery('#' + formId);
            // Add hidden fields to form.
            $form.append('<input type="hidden" name="' + fieldName + '" value="' + token + '">');

            $form.append('<input type="hidden" name="' + honeypotIdFieldName + '" value="' + honeypotId + '">');

        },
        'json'
    );
};

<|endoftext|>


<commit_before>import koa_router from 'koa-router';
import koa_body from 'koa-body';
import models from 'db/models';
import config from 'config';
import {esc, escAttrs} from 'db/models';
import {getRemoteIp, rateLimitReq, checkCSRF} from '../utils';
import destinationBtcAddress from 'shared/icoAddress'
import coRequest from 'co-request'
import {getLogger} from '../../app/utils/Logger'
import Apis from 'shared/api_client/ApiInstances';

const cypherToken = config.blockcypher_token
const print = getLogger('API - ico').print

export default function useIcoApi(app) {
  const router = koa_router();
  app.use(router.routes());
  const koaBody = koa_body();

  router.get('/api/v1/get_golos_current_supply', function * () {
    try {
      const data = yield Apis.instance().db_api.exec( 'get_dynamic_global_properties', []);
      //this.body = JSON.stringify({status: 'ok', data: data});
      this.body = data.current_supply.split(' ')[0];
    } catch (error) {
        console.error('Error in /api/v1/get_current_supply', error);
        this.body = JSON.stringify({
            error: error.message
        });
        this.status = 500;
    }
  })

  router.get('/api/v1/get_raised_amounts', function * () {
    let responce = this;
    try {
      const data = yield models.List.findAll({kk: {$like:"icoBalance_Nov"}});
      this.body = JSON.stringify({status: 'ok', data: data});
    } catch (error) {
        console.error('Error in /get_raised_amounts api call', this.session.uid, error.toString());
        this.body = JSON.stringify({
            error: error.message
        });
        this.status = 500;
    }

  });

  router.post('/api/v1/generate_ico_address', koaBody, function * () {
    console.log(destinationBtcAddress);
    if (rateLimitReq(this, this.req))
      return;

    const params = this.request.body;
    const {csrf} = typeof(params) === 'string'
    ? JSON.parse(params)
    : params;
    if (!checkCSRF(this, csrf))
      return;
    const cypher = yield coRequest(`https://api.blockcypher.com/v1/btc/main/payments?token=${cypherToken}`, {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-type': 'application/json'
      },
      body: JSON.stringify({"destination": destinationBtcAddress})
    });
    try {
      let cypherParsed = JSON.parse(cypher.body);
      print('blockcypher generated payment forwarding address', cypherParsed);
      const icoAddress = cypherParsed.input_address;
      this.body = JSON.stringify({status: 'ok', icoAddress: icoAddress});
    } catch (error) {
      console.error(error);
      this.body = JSON.stringify({status: "error", error: error.message});
      this.status = 500;
    }
  });
}
<commit_msg>Close #307, implements api call 'get_gbg_current_supply'<commit_after>import koa_router from 'koa-router';
import koa_body from 'koa-body';
import models from 'db/models';
import config from 'config';
import {esc, escAttrs} from 'db/models';
import {getRemoteIp, rateLimitReq, checkCSRF} from '../utils';
import destinationBtcAddress from 'shared/icoAddress'
import coRequest from 'co-request'
import {getLogger} from '../../app/utils/Logger'
import Apis from 'shared/api_client/ApiInstances';

const cypherToken = config.blockcypher_token
const print = getLogger('API - ico').print

export default function useIcoApi(app) {
  const router = koa_router();
  app.use(router.routes());
  const koaBody = koa_body();

  router.get('/api/v1/get_golos_current_supply', function * () {
    try {
      const data = yield Apis.instance().db_api.exec( 'get_dynamic_global_properties', []);
      this.body = data.current_supply.split(' ')[0];
    } catch (error) {
        console.error('Error in /api/v1/get_current_supply', error);
        this.body = JSON.stringify({
            error: error.message
        });
        this.status = 500;
    }
  })

  router.get('/api/v1/get_gbg_current_supply', function * () {
    try {
      const data = yield Apis.instance().db_api.exec( 'get_dynamic_global_properties', []);
      this.body = data.current_sbd_supply.split(' ')[0];
    } catch (error) {
        console.error('Error in /api/v1/get_gbg_current_supply', error);
        this.body = JSON.stringify({
            error: error.message
        });
        this.status = 500;
    }
  })

  router.get('/api/v1/get_raised_amounts', function * () {
    let responce = this;
    try {
      const data = yield models.List.findAll({kk: {$like:"icoBalance_Nov"}});
      this.body = JSON.stringify({status: 'ok', data: data});
    } catch (error) {
        console.error('Error in /get_raised_amounts api call', this.session.uid, error.toString());
        this.body = JSON.stringify({
            error: error.message
        });
        this.status = 500;
    }

  });

  router.post('/api/v1/generate_ico_address', koaBody, function * () {
    console.log(destinationBtcAddress);
    if (rateLimitReq(this, this.req))
      return;

    const params = this.request.body;
    const {csrf} = typeof(params) === 'string'
    ? JSON.parse(params)
    : params;
    if (!checkCSRF(this, csrf))
      return;
    const cypher = yield coRequest(`https://api.blockcypher.com/v1/btc/main/payments?token=${cypherToken}`, {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-type': 'application/json'
      },
      body: JSON.stringify({"destination": destinationBtcAddress})
    });
    try {
      let cypherParsed = JSON.parse(cypher.body);
      print('blockcypher generated payment forwarding address', cypherParsed);
      const icoAddress = cypherParsed.input_address;
      this.body = JSON.stringify({status: 'ok', icoAddress: icoAddress});
    } catch (error) {
      console.error(error);
      this.body = JSON.stringify({status: "error", error: error.message});
      this.status = 500;
    }
  });
}
<|endoftext|>


<commit_before>
var colors = 6;
var sizeX = 8;
var sizeY = 8;
var sizeCell = 35;
var board = [];
var moves = 0;

var generateBoard = function(){
	for (var j = 0; j < sizeX; j++) {
		board[j] = [];
	  for (var i = 0; i < sizeY; i++) {
			board[j][i] = ~~(Math.random()*7);
		}
	}
};

var colors = [
	'rgb(255,255,255)',
	'rgb(254,202,8)',
	'rgb(248,162,40)',
	'rgb(18,148,223)',
	'rgb(16,129,199)',
	'rgb(234,47,29)',
	'rgb(229,19,25)',
];

function draw () {
  var canvas = document.getElementById('canvas');
	canvas.width = sizeX * sizeCell;
	canvas.height = sizeY * sizeCell;
  var ctx = canvas.getContext('2d');
  for (var j = 0; j < board.length; j++) {
	  for (var i = 0; i < board.length; i++) {

	    ctx.fillStyle = colors[board[j][i]];

	    ctx.fillRect (i*sizeCell, j*sizeCell, sizeCell, sizeCell);
	  }
  }
}

function setUpClickHandlers () {
	var forEach = Array.prototype.forEach;
	var buttons = document.querySelectorAll("button");

	forEach.call(buttons, function(button) {
		button.addEventListener('click', destColor ,false);
	});
}

function destColor (evt) {
	moves++;
	document.querySelector('#moves span').textContent = moves;
	checkedSquares = [];
	var color = evt.target.getAttribute('data-color');
	checkElement(0, 0, checkColor(0, 0), parseInt(color, 10));
	draw();
}

function checkWinningConditions(){
	var win = true;
	var winningColor = board[0][0];
	for (var j = 0; j < board.length; j++) {
	  for (var i = 0; i < board.length; i++) {
	    if (parseInt([board[j][i]], 10) !== winningColor) {
				win = false;
			}
	  }
  }

	return win;
}

function checkColor (x, y) {
	sourceColor = board[y][x];
	return parseInt(sourceColor, 10);
}

var checkedSquares = [];

function checkElement(x, y, sourceColor, destColor) {
	if (checkedSquares.indexOf(x + '-' + y) > -1) {
		return;
	}

	checkedSquares.push(x + '-' + y);

	if ((x === 0 && y === 0) || checkColor(x, y) === sourceColor) {
		updateElement(x, y, destColor);

		// W neighbour
		if (x > 0) {
			// console.log('W');
			checkElement(x-1, y, sourceColor, destColor);
		}

		// N neighbour
		if (y > 0) {
			// console.log('N');
			checkElement(x, y-1, sourceColor, destColor);
		}

		// E neighbour
		if (x < board.length-1) {
			// console.log('E');
			checkElement(x+1, y, sourceColor, destColor);
		}

		// S neighbour
		if (y < board[0].length-1) {
			// console.log('S');
			checkElement(x, y+1, sourceColor, destColor);
		}
	}
}

function updateElement(x, y, color) {
	//console.log('coords', x, y, 'color', color);
	board[y][x] = color;
}

generateBoard();
setUpClickHandlers();
draw();
var lowest = 9999;
var highest = 0;
var games = 0;

var timeout;
var autoPlay = function(){
	var buttons = document.querySelectorAll('button');
	var button = buttons[~~(Math.random()*buttons.length)];
	button.click();
	if (!checkWinningConditions()) {
		timeout = setTimeout(autoPlay, 1);
	} else {
		games++;
		if (moves < lowest) {
			lowest = moves;
			moves = '' + moves + '';
			console.log('Lowest:', lowest);
		} else if (moves > highest) {
			highest = moves;
			moves = '' + moves + '';
			console.log('Highest:', highest);
		}
		document.querySelector('#moves div').textContent = games;

		document.getElementById('history').innerHTML += moves + ', ';
		document.getElementById('history').scrollTop = 100000;
		moves = 0;
		generateBoard();
		draw();
		autoPlay();
	}
};
<commit_msg>check most popular block<commit_after>
var colors = 6;
var sizeX = 8;
var sizeY = 8;
var sizeCell = 35;
var board = [];
var moves = 0;

var generateBoard = function(){
	for (var j = 0; j < sizeX; j++) {
		board[j] = [];
	  for (var i = 0; i < sizeY; i++) {
			board[j][i] = ~~(Math.random()*7);
		}
	}
};

var colors = [
	'rgb(255,255,255)',
	'rgb(254,202,8)',
	'rgb(248,162,40)',
	'rgb(18,148,223)',
	'rgb(16,129,199)',
	'rgb(234,47,29)',
	'rgb(229,19,25)',
];

function draw () {
  var canvas = document.getElementById('canvas');
	canvas.width = sizeX * sizeCell;
	canvas.height = sizeY * sizeCell;
  var ctx = canvas.getContext('2d');
  for (var j = 0; j < board.length; j++) {
	  for (var i = 0; i < board.length; i++) {

	    ctx.fillStyle = colors[board[j][i]];

	    ctx.fillRect (i*sizeCell, j*sizeCell, sizeCell, sizeCell);
	  }
  }
}

function setUpClickHandlers () {
	var forEach = Array.prototype.forEach;
	var buttons = document.querySelectorAll("button");

	forEach.call(buttons, function(button) {
		button.addEventListener('click', destColor ,false);
	});
}

function destColor (evt) {
	moves++;
	document.querySelector('#moves span').textContent = moves;
	checkedSquares = [];
	borderBlocks = {};
	var color = evt.target.getAttribute('data-color');
	checkElement(0, 0, checkColor(0, 0), parseInt(color, 10));
	draw();
	console.log(borderBlocks);
}

function checkWinningConditions(){
	var win = true;
	var winningColor = board[0][0];
	for (var j = 0; j < board.length; j++) {
	  for (var i = 0; i < board.length; i++) {
	    if (parseInt([board[j][i]], 10) !== winningColor) {
				win = false;
			}
	  }
  }

	return win;
}

function checkColor (x, y) {
	sourceColor = board[y][x];
	return parseInt(sourceColor, 10);
}

var checkedSquares = [];
var borderBlocks = {};

function checkElement(x, y, sourceColor, destColor) {
	if (checkedSquares.indexOf(x + '-' + y) > -1) {
		return;
	}

	checkedSquares.push(x + '-' + y);

	if ((x === 0 && y === 0) || checkColor(x, y) === sourceColor) {
		updateElement(x, y, destColor);

		// W neighbour
		if (x > 0) {
			// console.log('W');
			checkElement(x-1, y, sourceColor, destColor);
		}

		// N neighbour
		if (y > 0) {
			// console.log('N');
			checkElement(x, y-1, sourceColor, destColor);
		}

		// E neighbour
		if (x < board.length-1) {
			// console.log('E');
			checkElement(x+1, y, sourceColor, destColor);
		}

		// S neighbour
		if (y < board[0].length-1) {
			// console.log('S');
			checkElement(x, y+1, sourceColor, destColor);
		}
	} else {
		var blockColor = checkColor(x, y);
		if (blockColor in borderBlocks) {
			borderBlocks[blockColor]++;
		} else {
			borderBlocks[blockColor] = 1;
		}
	}
}

function updateElement(x, y, color) {
	//console.log('coords', x, y, 'color', color);
	board[y][x] = color;
}

generateBoard();
setUpClickHandlers();
draw();
var lowest = 9999;
var highest = 0;
var games = 0;

var checkMostPopularNeighbour = function(){

};

var timeout;
var autoPlay = function(){
	var buttons = document.querySelectorAll('button');
	var button = buttons[~~(Math.random()*buttons.length)];
	button.click();
	if (!checkWinningConditions()) {
		timeout = setTimeout(autoPlay, 1);
	} else {
		games++;
		if (moves < lowest) {
			lowest = moves;
			moves = '' + moves + '';
			console.log('Lowest:', lowest);
		} else if (moves > highest) {
			highest = moves;
			moves = '' + moves + '';
			console.log('Highest:', highest);
		}
		document.querySelector('#moves div').textContent = games;

		document.getElementById('history').innerHTML += moves + ', ';
		document.getElementById('history').scrollTop = 100000;
		moves = 0;
		generateBoard();
		draw();
		autoPlay();
	}
};
<|endoftext|>


<commit_before>/*!
 * Start Bootstrap - Grayscale v3.3.7+1 (http://startbootstrap.com/template-overviews/grayscale)
 * Copyright 2013-2016 Start Bootstrap
 * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE)
 */
/*jslint browser: true*/
/*global $, jQuery, alert*/

function collapseNavbar() {"use strict";
                           $(".navbar").offset().top > 50 ? $(".navbar-fixed-top").addClass("top-nav-collapse") : $(".navbar-fixed-top").removeClass("top-nav-collapse"); }
function init()
 var e = {
        zoom: 15,
        center: new google.maps.LatLng(40.67, (-73.94)),
        disableDefaultUI: !0,
        scrollwheel: !1,
        draggable: !1,
        styles: [{
            featureType: "water",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }]
        }, {
            featureType: "landscape",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 20
            }]
        }, {
            featureType: "road.highway",
            elementType: "geometry.fill",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }]
        }, {
            featureType: "road.highway",
            elementType: "geometry.stroke",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 29
            }, {
                weight: .2
            }]
        }, {
            featureType: "road.arterial",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 18
            }]
        }, {
            featureType: "road.local",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 16
            }]
        }, {
            featureType: "poi",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 21
            }]
        }, {
            elementType: "labels.text.stroke",
            stylers: [{
                visibility: "on"
            }, {
                color: "#000000"
            }, {
                lightness: 16
            }]
        }, {
            elementType: "labels.text.fill",
            stylers: [{
                saturation: 36
            }, {
                color: "#000000"
            }, {
                lightness: 40
            }]
        }, {
            elementType: "labels.icon",
            stylers: [{
                visibility: "off"
            }]
        }, {
            featureType: "transit",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 19
            }]
        }, {
            featureType: "administrative",
            elementType: "geometry.fill",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 20
            }]
        }, {
            featureType: "administrative",
            elementType: "geometry.stroke",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }, {
                weight: 1.2
            }]
        }]
    },
    t = document.getElementById("map");
map = new google.maps.Map(t, e);
var l = "img/map-marker.png",
    o = new google.maps.LatLng(40.67, (-73.94));
new google.maps.Marker({
    position: o,
    map: map,
    icon: l
})
}
$(window).scroll(collapseNavbar), $(document).ready(collapseNavbar), $(function() {
    $("a.page-scroll").bind("click", function(e) {
        var t = $(this);
        $("html, body").stop().animate({
            scrollTop: $(t.attr("href")).offset().top
        }, 1500, "easeInOutExpo"), e.preventDefault()
    })
}), $(".navbar-collapse ul li a").click(function() {
    $(this).closest(".collapse").collapse("toggle")
});
var map = null;
google.maps.event.addDomListener(window, "load", init), google.maps.event.addDomListener(window, "resize", function() {
    map.setCenter(new google.maps.LatLng(40.67, (-73.94)))
});<commit_msg>Javascript<commit_after>/*!
 * Start Bootstrap - Grayscale v3.3.7+1 (http://startbootstrap.com/template-overviews/grayscale)
 * Copyright 2013-2016 Start Bootstrap
 * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE)
 */
/*jslint browser: true*/
/*global $, jQuery, alert*/

function collapseNavbar() {"use strict";
                           $(".navbar").offset().top > 50 ? $(".navbar-fixed-top").addClass("top-nav-collapse") : $(".navbar-fixed-top").removeClass("top-nav-collapse"); }
function init() {"use strict";
        var e = {
        zoom: 15,
        center: new google.maps.LatLng(40.67, (-73.94)),
        disableDefaultUI: !0,
        scrollwheel: !1,
        draggable: !1,
        styles: [{
            featureType: "water",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }]
        }, {
            featureType: "landscape",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 20
            }]
        }, {
            featureType: "road.highway",
            elementType: "geometry.fill",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }]
        }, {
            featureType: "road.highway",
            elementType: "geometry.stroke",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 29
            }, {
                weight: 0.2
            }]
        }, {
            featureType: "road.arterial",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 18
            }]
        }, {
            featureType: "road.local",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 16
            }]
        }, {
            featureType: "poi",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 21
            }]
        }, {
            elementType: "labels.text.stroke",
            stylers: [{
                visibility: "on"
            }, {
                color: "#000000"
            }, {
                lightness: 16
            }]
        }, {
            elementType: "labels.text.fill",
            stylers: [{
                saturation: 36
            }, {
                color: "#000000"
            }, {
                lightness: 40
            }]
        }, {
            elementType: "labels.icon",
            stylers: [{
                visibility: "off"
            }]
        }, {
            featureType: "transit",
            elementType: "geometry",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 19
            }]
        }, {
            featureType: "administrative",
            elementType: "geometry.fill",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 20
            }]
        }, {
            featureType: "administrative",
            elementType: "geometry.stroke",
            stylers: [{
                color: "#000000"
            }, {
                lightness: 17
            }, {
                weight: 1.2
            }]
        }]
    },
        t = document.getElementById("map");
     map = new google.maps.Map(t, e);
     var l = "img/map-marker.png",
        o = new google.maps.LatLng(40.67, (-73.94));
     new google.maps.Marker({
        position: o,
        map: map,
        icon: l
    });
    }
$(window).scroll(collapseNavbar);
$(document).ready(collapseNavbar);
$(function () {"use strict";
    $("a.page-scroll").bind("click", function (e) {
        var t = $(this);
        $("html, body").stop().animate({
            scrollTop: $(t.attr("href")).offset().top
        }, 1500, "easeInOutExpo");
        e.preventDefault();
    });
    });
$(".navbar-collapse ul li a").click(function () {"use strict";
    $(this).closest(".collapse").collapse("toggle");
    });
var map = null;
google.maps.event.addDomListener(window, "load", init);
google.maps.event.addDomListener(window, "resize", function () {"use strict";
    map.setCenter(new google.maps.LatLng(40.67, (-73.94)));
    });<|endoftext|>


<commit_before>"use strict";
var URL = require("url");
var Http2ServerResponse = require("http2").Http2ServerResponse;
var headersKeys = "Content-Type,Content-Length,User-Agent,Accept-Language,Accept-Encoding,Range,If-Range,Last-Modified".split(",");
var privateKeys = Object.create(null);
"Cookie,Connection,Referer,Host,Origin".split(",").forEach(k => privateKeys[k] = privateKeys[k.toLowerCase()] = true);
var options = {};
-function () {
    var fs = require("fs");
    var path = require("path");
    var loadCertFile = function (keyname, reletivepath) {
        var fullpath = path.join(__dirname, "../data/keystore", reletivepath);
        fs.readFile(fullpath, function (err, chunk) {
            if (err) console.error(err);
            else options[keyname] = chunk;
        });
    }
    loadCertFile("key", "cross-key.pem");
    loadCertFile("cert", "cross-cert.pem");
}();
function parseUrl(hostpath, real) {
    var { pathname, search } = URL.parse(hostpath);
    var slice_end = pathname.indexOf("@");
    if (slice_end < 0) slice_end = pathname.length;
    var jsonlike = pathname.slice(1, slice_end);
    var realpath = real ? real.slice(1) : pathname.slice(slice_end + 1) + (search || "");
    var matchlike = /^(?:\{|%7b)(s?)(\/|%2f)\2(.*?)\2(.*?)(?:\}|%7d)$/i.exec(jsonlike);
    if (matchlike) {
        // {s//wx2.qq.com/k=v,k=v,k=v}
        var headers = {};
        let [, s, , host, header] = matchlike;
        var hostpath = `http${s}://${host}/`;
        header.split(/[,&]/).forEach(function (kv) {
            var [k, v] = kv.split("=");
            if (k && v) headers[decodeURIComponent(k)] = decodeURIComponent(v);
        });
    } else {
        var { url: hostpath, token, headers = {} } = JSON.parse(decodeURIComponent(jsonlike));
        if (!token) throw new Error("验证身份失败！");
        hostpath = escape(hostpath);
    }
    return { jsonlike, realpath, hostpath, headers };
}
// https://github.com/nodejs/node/blob/02a0c74861c3107e6a9a1752e91540f8d4c49a76/lib/_http_common.js :204
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
/**
 * Verifies that the given val is a valid HTTP token
 * per the rules defined in RFC 7230
 * See https://tools.ietf.org/html/rfc7230#section-3.2.6
 */
function checkIsHttpToken(val) {
    return tokenRegExp.test(val);
}
function cross(req, res, referer) {
    try {
        if (referer) {
            var { jsonlike, realpath, hostpath, headers } = parseUrl(referer, req.url);
            if (/head|get/i.test(req.method)) {
                var redirect = "/" + escape(jsonlike) + "@" + realpath;
                res.writeHead(302, {
                    "Location": redirect
                });
                return res.end();
            }
        } else {
            var { jsonlike, realpath, hostpath, headers } = parseUrl(req.url);
        }
        var
            $url = hostpath + realpath,
            // $data = $cross['data'],//不再接受数据参数，如果是get请直接写入$url，如果是post，请直接post
            method = req.method;//$_SERVER['REQUEST_METHOD'];
        var _headers = req.headers;
        var is_proxy = false;
        if (/^https?\:\/\/[^\/]*\/(?:\{|%7b)/i.test(_headers.referer) && !headers.referer) {
            headers.referer = hostpath + parseUrl(_headers.referer).realpath;
            is_proxy = true;
        } else if (_headers.referer) {
            headers.referer = hostpath;
        }
        for (let key in headers) {
            let k = key.toLowerCase();
            if (k !== key) {
                headers[k] = headers[key];
                delete headers[key];
            }
        }
        headersKeys.forEach(function (key) {
            key = key.toLowerCase();
            if (!headers[key] && _headers[key]) {
                headers[key] = _headers[key];
            }
        });
        for (var k in _headers) {
            if ({}.hasOwnProperty.call(privateKeys, k)) {
                continue;
            }
            if (!headers[k] && _headers[k] && checkIsHttpToken(k)) {
                headers[k] = _headers[k];
            }
        }
        var http;
        if (/^https\:/i.test(hostpath)) {
            http = require("https");
        } else {
            http = require("http");
        }
        var request = http.request(Object.assign({
            method: method,
            headers: headers,
            rejectUnauthorized: false// 放行证书不可用的网站
        }, URL.parse($url), options), function (response) {
            var headers = response.headers;
            var setCookie = headers["set-cookie"];
            if (setCookie && !is_proxy) headers["efront-cookie"] = setCookie, delete headers["set-cookie"];
            if (headers.location) {
                headers["efront-location"] = headers.location;
                delete headers.location;
            }
            headers["Access-Control-Expose-Headers"] = ["efront-location", "efront-cookie"].join();
            delete headers["access-control-allow-origin"];
            delete headers["access-control-allow-methods"];
            if (res instanceof Http2ServerResponse) {
                delete headers["transfer-encoding"];
                delete headers["connection"];
            }
            res.writeHead(response.statusCode, headers);
            response.pipe(res);
        });
        request.setTimeout(36000/*support for wechat long pull*/);
        request.on("error", function (e) {
            res.writeHead(403, {});
            res.end(String(e));
        });
        req.pipe(request);
    } catch (e) {
        res.writeHead(403, {});
        res.end(String(e));
    }
}
module.exports = cross;<commit_msg>防止请求意外关闭<commit_after>"use strict";
var URL = require("url");
var Http2ServerResponse = require("http2").Http2ServerResponse;
var headersKeys = "Content-Type,Content-Length,User-Agent,Accept-Language,Accept-Encoding,Range,If-Range,Last-Modified".split(",");
var privateKeys = Object.create(null);
"Cookie,Connection,Referer,Host,Origin".split(",").forEach(k => privateKeys[k] = privateKeys[k.toLowerCase()] = true);
var options = {};
-function () {
    var fs = require("fs");
    var path = require("path");
    var loadCertFile = function (keyname, reletivepath) {
        var fullpath = path.join(__dirname, "../data/keystore", reletivepath);
        fs.readFile(fullpath, function (err, chunk) {
            if (err) console.error(err);
            else options[keyname] = chunk;
        });
    }
    loadCertFile("key", "cross-key.pem");
    loadCertFile("cert", "cross-cert.pem");
}();
function parseUrl(hostpath, real) {
    var { pathname, search } = URL.parse(hostpath);
    var slice_end = pathname.indexOf("@");
    if (slice_end < 0) slice_end = pathname.length;
    var jsonlike = pathname.slice(1, slice_end);
    var realpath = real ? real.slice(1) : pathname.slice(slice_end + 1) + (search || "");
    var matchlike = /^(?:\{|%7b)(s?)(\/|%2f)\2(.*?)\2(.*?)(?:\}|%7d)$/i.exec(jsonlike);
    if (matchlike) {
        // {s//wx2.qq.com/k=v,k=v,k=v}
        var headers = {};
        let [, s, , host, header] = matchlike;
        var hostpath = `http${s}://${host}/`;
        header.split(/[,&]/).forEach(function (kv) {
            var [k, v] = kv.split("=");
            if (k && v) headers[decodeURIComponent(k)] = decodeURIComponent(v);
        });
    } else {
        var { url: hostpath, token, headers = {} } = JSON.parse(decodeURIComponent(jsonlike));
        if (!token) throw new Error("验证身份失败！");
        hostpath = escape(hostpath);
    }
    return { jsonlike, realpath, hostpath, headers };
}
// https://github.com/nodejs/node/blob/02a0c74861c3107e6a9a1752e91540f8d4c49a76/lib/_http_common.js :204
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/
/**
 * Verifies that the given val is a valid HTTP token
 * per the rules defined in RFC 7230
 * See https://tools.ietf.org/html/rfc7230#section-3.2.6
 */
function checkIsHttpToken(val) {
    return tokenRegExp.test(val);
}
function cross(req, res, referer) {
    try {
        if (referer) {
            var { jsonlike, realpath, hostpath, headers } = parseUrl(referer, req.url);
            if (/head|get/i.test(req.method)) {
                var redirect = "/" + escape(jsonlike) + "@" + realpath;
                res.writeHead(302, {
                    "Location": redirect
                });
                return res.end();
            }
        } else {
            var { jsonlike, realpath, hostpath, headers } = parseUrl(req.url);
        }
        var
            $url = hostpath + realpath,
            // $data = $cross['data'],//不再接受数据参数，如果是get请直接写入$url，如果是post，请直接post
            method = req.method;//$_SERVER['REQUEST_METHOD'];
        var _headers = req.headers;
        var is_proxy = false;
        if (/^https?\:\/\/[^\/]*\/(?:\{|%7b)/i.test(_headers.referer) && !headers.referer) {
            headers.referer = hostpath + parseUrl(_headers.referer).realpath;
            is_proxy = true;
        } else if (_headers.referer) {
            headers.referer = hostpath;
        }
        for (let key in headers) {
            let k = key.toLowerCase();
            if (k !== key) {
                headers[k] = headers[key];
                delete headers[key];
            }
        }
        headersKeys.forEach(function (key) {
            key = key.toLowerCase();
            if (!headers[key] && _headers[key]) {
                headers[key] = _headers[key];
            }
        });
        for (var k in _headers) {
            if ({}.hasOwnProperty.call(privateKeys, k)) {
                continue;
            }
            if (!headers[k] && _headers[k] && checkIsHttpToken(k)) {
                headers[k] = _headers[k];
            }
        }
        var http;
        if (/^https\:/i.test(hostpath)) {
            http = require("https");
        } else {
            http = require("http");
        }
        var request = http.request(Object.assign({
            method: method,
            headers: headers,
            rejectUnauthorized: false// 放行证书不可用的网站
        }, URL.parse($url), options), function (response) {
            var headers = response.headers;
            var setCookie = headers["set-cookie"];
            if (setCookie && !is_proxy) headers["efront-cookie"] = setCookie, delete headers["set-cookie"];
            if (headers.location) {
                headers["efront-location"] = headers.location;
                delete headers.location;
            }
            headers["Access-Control-Expose-Headers"] = ["efront-location", "efront-cookie"].join();
            delete headers["access-control-allow-origin"];
            delete headers["access-control-allow-methods"];
            if (res instanceof Http2ServerResponse) {
                delete headers["transfer-encoding"];
                delete headers["connection"];
            }
            res.writeHead(response.statusCode, headers);
            response.pipe(res);
        });
        request.setTimeout(36000/*support for wechat long pull*/);
        request.on("error", function (e) {
            try {
                res.writeHead(403, {});
                res.end(String(e));
            } catch{
                res.end();
            }
        });
        req.pipe(request);
    } catch (e) {
        try {
            res.writeHead(403, {});
            res.end(String(e));
        } catch{
            res.end();
        }
    }
}
module.exports = cross;<|endoftext|>


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

"use strict";

var config  = require('../lib/config');
var debug   = require('debug')('hftsite');
var io      = require('../lib/io');

var g = {
  throttleTime: 1000,
};

var getTime = function() {
  return Date.now();
};

// Sends the local ip address and port
var inform = (function() {
  var lastAddressesAsStr;
  var lastPort;
  var lastTime = 0;

  return function() {
    if (!g.privateServer && g.port && g.addresses) {
      var now = getTime();
      var elapsedTime = now - lastTime;
      if (lastAddressesAsStr != g.addressesAsStr ||
          lastPort != g.port ||
          elapsedTime > g.throttleTime) {
        lastTime = now;
        lastAddressesAsStr = g.addressesAsStr;
        lastPort = g.port;
        var url = process.env.HFT_RENDEZVOUS_URL || config.getSettings().settings.rendezvousUrl;
        debug("ping: " + url);
        io.sendJSON(url, { addresses: g.addresses, port: g.port }, {}, function(err, result) {
          // do I care?
          if (err) {
            console.error(err);
          }
        });
      }
    }
  };
}());

/**
 * @typedef {Object} HFTSite~Options
 * @property {string?} addresses ip address eg. "1.2.3.4"
 * @property {string?} port port eg "18679"
 * @property {boolean?} privateServer true = don't send info to hftsite
 */

/**
 * Set options for hftSite
 *
 * @param {HFTSite~Options} options
 */
var setup = function(options) {
  ["addresses", "port", "privateServer"].forEach(function(key) {
    var value = options[key];
    if (value !== undefined) {
      g[key] = value;
    }
  });

  if (g.addresses) {
    g.addressesAsStr = g.addresses.sort().join(",");
  }
};

exports.inform = inform;
exports.setup = setup;

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

"use strict";

var config  = require('../lib/config');
var debug   = require('debug')('hftsite');
var io      = require('../lib/io');
var url     = require('url');

var g = {
  throttleTime: 1000,
};

var getTime = function() {
  return Date.now();
};

// Sends the local ip address and port
var inform = (function() {
  var lastAddressesAsStr;
  var lastPort;
  var lastTime = 0;

  return function() {
    if (!g.privateServer && g.port && g.addresses) {
      var now = getTime();
      var elapsedTime = now - lastTime;
      if (lastAddressesAsStr != g.addressesAsStr ||
          lastPort != g.port ||
          elapsedTime > g.throttleTime) {
        lastTime = now;
        lastAddressesAsStr = g.addressesAsStr;
        lastPort = g.port;
        var hftUrl = process.env.HFT_RENDEZVOUS_URL || config.getSettings().settings.rendezvousUrl;
        var parsedUrl = url.parse(hftUrl);
        debug("ping: " + hftUrl);
        io.sendJSON(hftUrl, { addresses: g.addresses, port: g.port }, {}, function(err, result) {
          // do I care?
          if (err) {
            console.error("Could not contact: " + parsedUrl.host);
            console.error(err);
          }
        });
      }
    }
  };
}());

/**
 * @typedef {Object} HFTSite~Options
 * @property {string?} addresses ip address eg. "1.2.3.4"
 * @property {string?} port port eg "18679"
 * @property {boolean?} privateServer true = don't send info to hftsite
 */

/**
 * Set options for hftSite
 *
 * @param {HFTSite~Options} options
 */
var setup = function(options) {
  ["addresses", "port", "privateServer"].forEach(function(key) {
    var value = options[key];
    if (value !== undefined) {
      g[key] = value;
    }
  });

  if (g.addresses) {
    g.addressesAsStr = g.addresses.sort().join(",");
  }
};

exports.inform = inform;
exports.setup = setup;

<|endoftext|>


<commit_before>class BotGui{
    constructor(){
        this.generalMenuOptions = {
            "parse_mode": "Markdown",
            "reply_markup": JSON.stringify({
                "keyboard": [
                [{ text: "Get" }, {text: "Start Alert"}],
                [{ text: "Stop Alert" }, {text: "Show Alerts"}]
                ]
            })
        };
    }

}
<commit_msg>fixed menu<commit_after>
exports.generateKeyboardOptions = function () {
    return  {
        "parse_mode": "Markdown",
        "reply_markup": {
            "keyboard": [
            [{ text: "Get" }],
            [{text: "Start Alert"},{ text: "Stop Alert" }, {text: "Show Alerts"}]
            ]
        }
    };
}

<|endoftext|>