<commit_before>
  basis.require('basis.timer');
  basis.require('basis.event');
  basis.require('basis.data');

 /**
  * Namespace overview:
  * - {basis.data.value.BindValue}
  * - {basis.data.value.Property}
  * - {basis.data.value.ObjectSet} (alias basis.data.value.DataObjectSet, but it's deprecate)
  * - {basis.data.value.Expression}
  *
  * @see ./demo/data/basic.html
  *
  * @namespace basis.data.value
  */

  var namespace = this.path;

  // import names

  var getter = basis.getter;
  var cleaner = basis.cleaner;
  var Emitter = basis.event.Emitter;
  var AbstractData = basis.data.AbstractData;
  var Value = basis.data.Value;
  var STATE = basis.data.STATE;

  var instanceMap = {};

  //
  // BindValue
  //

  var EMMITER_HANDLER = {
    destroy: function(object){
      this.removeLink(object);
    }
  };

 /**
  * @class
  */
  var BindValue = Value.subclass({
    className: namespace + '.BindValue',

   /**
    * @type {Array.<object>}
    * @private
    */
    links_: null,

   /**
    * Settings for bindings.
    */
    bindingBridge: {
      attach: function(property, handler, context){
        return property.addLink(context, handler);
      },
      detach: function(property, handler, context){
        return property.removeLink(context, handler);
      },
      get: function(property){
        return property.value;
      }
    },

   /**
    * @event
    */
    emit_change: function(value, oldValue){
      Value.prototype.emit_change.call(this, value, oldValue);

      if (cleaner.globalDestroy)
        return;

      var cursor = this;
      while (cursor = cursor.links_)
        this.apply_(cursor, oldValue);
    },

   /**
    * @constructor
    */
    init: function(){
      Value.prototype.init.call(this);

      instanceMap[this.basisObjectId] = this;
    },

   /**
    * Adds link to object property or method. Optional parameter `format` using for
    * value convertation to another value or type.
    * If object is instance of {basis.event.Emitter}, BindValue attach handler which
    * removes property links on object destroy.
    *
    * @example
    *
    *   var value = new basis.data.value.BindValue({ ... });
    *
    *   value.addLink(object, 'property');          // object.property = value;
    *   value.addLink(object, 'property', '[{0}]'); // object.property = '[{0}]'.format(value, oldValue);
    *   value.addLink(object, 'property', Number);  // object.property = Number(value, oldValue);
    *   value.addLink(object, 'property', { a: 1, b: 2});  // object.property = { a: 1, b: 2 }[value];
    *   value.addLink(object, object.method);       // object.method(value, oldValue);
    *
    *   value.addLink(object, function(value, oldValue){ // {function}.call(object, value, oldValue);
    *     // some code
    *     // `this` refer to object
    *   });
    *
    *   // Trace property changes
    *   var historyOfChanges = new Array();
    *   var value = new basis.data.value.BindValue({ value: 1 });
    *   value.addLink(historyOfChanges, historyOfChanges.push);  // historyOfChanges -> [1]
    *   value.set(2);  // historyOfChanges -> [1, 2]
    *   value.set(3);  // historyOfChanges -> [1, 2, 3]
    *   value.set(3);  // property didn't change self value
    *                     // historyOfChanges -> [1, 2, 3]
    *   value.set(1);  // historyOfChanges -> [1, 2, 3, 1]
    *
    *   // Use console for log value changes
    *   value.addLink(console, console.log, 'new value is {0}');
    *
    * @param {object} object Target object.
    * @param {string|function} field Field or method of target object.
    * @param {string|function|object=} format Value modificator.
    * @return {object} Returns object.
    */
    addLink: function(object, field, format){
      // process format argument
      if (typeof format != 'function')
        format = getter(basis.fn.$self, format);

      // check for duplicates
      /** @cut */ var cursor = this;
      /** @cut */ while (cursor = cursor.links_)
      /** @cut */   if (cursor.object === object && cursor.field == field)
      /** @cut */   {
      /** @cut */     basis.dev.warn(this.constructor.className + '#addLink: Duplicate link for property');
      /** @cut */     break;
      /** @cut */   }

      // create link
      this.links_ = {
        object: object,
        field: field,
        format: format,
        links_: this.links_
      };

      // add handler if object is basis.event.Emitter
      if (object instanceof Emitter)
        object.addHandler(EMMITER_HANDLER, this);

      // make effect on object
      this.apply_(this.links_);

      return object;
    },

   /**
    * Removes link from object. Parameters must be the same
    * as for addLink method. If field omited all links are remove.
    *
    * @example
    *   var value = new basis.data.value.BindValue();
    *   // add links
    *   value.addLink(object, 'field');
    *   value.addLink(object, object.method);
    *   // remove links
    *   value.removeLink(object, 'field');
    *   value.removeLink(object, object.method);
    *   // or remove all links for object
    *   value.removeLink(object);
    *
    *   // incorrect usage
    *   value.addLink(object, function(value){ this.field = value * 2; });
    *   ...
    *   value.removeLink(object, function(value){ this.field = value * 2; });
    *   // link to object will not removed
    *
    *   // right way
    *   var linkHandler = function(value){ this.field = value * 2; };
    *   value.addLink(object, linkHandler);
    *   ...
    *   value.removeLink(object, linkHandler);
    *
    *   // for cases when object is instance of {basis.event.Emitter} removing link on destroy is not required
    *   var emitterInstance = new basis.event.Emitter();
    *   value.addLink(emitterInstance, 'title');
    *   ...
    *   emitterInstance.destroy();    // links to emitterInstance will be removed
    *
    * @param {object} object
    * @param {string|function=} field
    */
    removeLink: function(object, field){
      var cursor = this;
      var prev;

      while (prev = cursor, cursor = cursor.links_)
        if (cursor.object === object && (!field || field == cursor.field))
        {
          // prevent apply new values
          cursor.field = null;

          // delete link
          prev.links_ = cursor.links_;

          // remove handler if object is basis.event.Emitter
          if (cursor.object instanceof Emitter)
            cursor.object.removeHandler(EMMITER_HANDLER, this);
        }
    },

   /**
    * Removes all property links to objects.
    */
    clear: function(){
      var cursor = this;

      // remove event handlers from basis.event.Emitter instances
      while (cursor = cursor.links_)
        if (cursor.object instanceof Emitter)
          cursor.object.removeHandler(EMMITER_HANDLER, this);

      // clear links
      this.links_ = null;
    },

   /**
    * @param {object} link
    * @param {*} oldValue Object value before changes.
    * @private
    */
    apply_: function(link, oldValue){
      var field = link.field;

      // field specified
      if (field != null)
      {
        var value = link.format(this.value);
        var object = link.object;

        if (typeof field == 'function')
          field.call(object, value, arguments.length < 2 ? value : link.format(oldValue));
        else
          object[field] = value;
      }
    },

   /**
    * @destructor
    */
    destroy: function(){
      this.clear();

      Value.prototype.destroy.call(this);

      this.links_ = null;
      delete instanceMap[this.basisObjectId];
    }
  });

 /**
  * @class
  */
  var Property = BindValue.subclass({
    className: namespace + '.Property',

    // use custom constructor
    extendConstructor_: false,

   /**
    * @param {object} initValue Initial value for object.
    * @param {object=} handler
    * @param {function()=} proxy
    * @constructor
    */
    init: function(initValue, handler, proxy){
      this.value = initValue;
      this.handler = handler;
      this.proxy = proxy;

      BindValue.prototype.init.call(this);
    }
  });

  //
  //  Object set
  //
                                       // priority: lowest -------------------------------------------------------------> highest
  var OBJECTSET_STATE_PRIORITY = STATE.priority; //[STATE.READY, STATE.DEPRECATED, STATE.UNDEFINED, STATE.ERROR, STATE.PROCESSING];
  var OBJECTSET_HANDLER = {
    stateChanged: function(){
      this.fire(false, true);
    },
    update: function(){
      this.fire(true);
    },
    change: function(){
      this.fire(true);
    },
    destroy: function(object){
      this.remove(object);
    }
  };

 /**
  * @class
  */
  var ObjectSet = BindValue.subclass({
    className: namespace + '.ObjectSet',

   /**
    * @type {Array.<basis.data.Object>}
    */
    objects: null,

   /**
    * @inheritDoc
    */
    value: 0,

   /**
    * @type {boolean}
    * @private
    */
    valueChanged_: false,

   /**
    * @type {function}
    */
    calculateValue: function(){
      return this.value + 1;
    },

   /**
    * @type {boolean}
    */
    calculateOnInit: false,

   /**
    * @type {Array.<basis.data.STATE>}
    */
    statePriority: OBJECTSET_STATE_PRIORITY,

   /**
    * @type {boolean}
    * @private
    */
    stateChanged_: true,

   /**
    * @type {number}
    * @private
    */
    timer_: false,

   /**
    * @constructor
    */
    init: function(){
      BindValue.prototype.init.call(this);

      var objects = this.objects;
      this.objects = [];

      if (objects && Array.isArray(objects))
      {
        this.lock();
        this.add.apply(this, objects);
        this.unlock();
      }

      this.valueChanged_ = this.stateChanged_ = !!this.calculateOnInit;
      this.update();
    },

   /**
    * Adds one or more AbstractData instances to objects collection.
    * @param {...basis.data.AbstractData}
    */
    add: function(/* dataObject1 .. dataObjectN */){
      for (var i = 0, len = arguments.length; i < len; i++)
      {
        var object = arguments[i];
        if (object instanceof AbstractData)
        {
          if (this.objects.add(object))
            object.addHandler(OBJECTSET_HANDLER, this);
        }
        else
          throw this.constructor.className + '#add: Instance of AbstractData required';
      }

      this.fire(true, true);
    },

   /**
    * Removes AbstractData instance from objects collection.
    * @param {basis.data.AbstractData} object
    */
    remove: function(object){
      if (this.objects.remove(object))
        object.removeHandler(OBJECTSET_HANDLER, this);

      this.fire(true, true);
    },

   /**
    * Removes all AbstractData instances from objects collection.
    */
    clear: function(){
      for (var i = 0, object; object = this.objects[i]; i++)
        object.removeHandler(OBJECTSET_HANDLER, this);

      this.objects.clear();

      this.fire(true, true);
    },

   /**
    * @param {boolean=} valueChanged
    * @param {boolean=} stateChanged
    */
    fire: function(valueChanged, stateChanged){
      if (!this.locked)
      {
        this.valueChanged_ = this.valueChanged_ || !!valueChanged;
        this.stateChanged_ = this.stateChanged_ || !!stateChanged;

        if (!this.timer_ && (this.valueChanged_ || this.stateChanged_))
          this.timer_ = basis.timer.setImmediate(this.update.bind(this));
      }
    },

   /**
    * Makes object not sensitive for attached AbstractData changes.
    */
    lock: function(){
      this.locked = true;
    },

   /**
    * Makes object sensitive for attached AbstractData changes.
    */
    unlock: function(){
      this.locked = false;
    },

   /**
    * @private
    */
    update: function(){
      var valueChanged = this.valueChanged_;
      var stateChanged = this.stateChanged_;

      this.valueChanged_ = false;
      this.stateChanged_ = false;

      this.timer_ = basis.timer.clearImmediate(this.timer_);

      if (!cleaner.globalDestroy)
      {
        if (valueChanged)
          this.set(this.calculateValue());

        if (stateChanged)
        {
          var len = this.objects.length;
          if (!len)
            this.setState(STATE.UNDEFINED);
          else
          {
            var maxWeight = -2;
            var curObject;

            for (var i = 0; i < len; i++)
            {
              var object = this.objects[i];
              var weight = this.statePriority.indexOf(String(object.state));
              if (weight > maxWeight)
              {
                curObject = object;
                maxWeight = weight;
              }
            }

            if (curObject)
              this.setState(curObject.state, curObject.state.data);
          }
        }
      }
    },

   /**
    * @destructor
    */
    destroy: function(){
      this.lock();
      this.clear();
      if (this.timer_)
        basis.timer.clearImmediate(this.timer_);

      BindValue.prototype.destroy.call(this);
    }
  });

  //
  // Expression
  //

 /**
  * @class
  */
  var Expression = Property.subclass({
    className: namespace + '.Expression',

    init: function(args, calc){
      BindValue.prototype.init.call(this);

      var args = basis.array(arguments);
      var calc = args.pop();

      if (typeof calc != 'function')
      {
        ;;;basis.dev.warn(this.constructor.className + ': last argument of constructor must be a function');
        calc = basis.fn.$undef;
      }

      if (args.length == 1)
      {
        args[0].addLink(this, function(value){
          this.set(calc.call(this, value));
        });
      }

      if (args.length > 1)
      {
        var changeWatcher = new ObjectSet({
          objects: args,
          calculateOnInit: true,
          calculateValue: function(){
            return calc.apply(this, args.map(function(item){
              return item.value;
            }));
          }
        });

        changeWatcher.addLink(this, this.set);

        this.addHandler({
          destroy: function(){
            if (!cleaner.globalDestroy)
              changeWatcher.destroy();
          }
        });
      }
    }
  });

  //
  // clean up instances
  //

  cleaner.add(function(){
    for (var key in instanceMap)
      instanceMap[key].destroy();

    instanceMap = null;
  });

  //
  // export names
  //

  module.exports = {
    BindValue: BindValue,
    Property: Property,
    ObjectSet: ObjectSet,
    Expression: Expression
  };

  // deprecated
  module.exports.DataObjectSet = ObjectSet;
<commit_msg>remove basis.data.value.DataObjectSet<commit_after>
  basis.require('basis.timer');
  basis.require('basis.event');
  basis.require('basis.data');

 /**
  * Namespace overview:
  * - {basis.data.value.BindValue}
  * - {basis.data.value.Property}
  * - {basis.data.value.ObjectSet}
  * - {basis.data.value.Expression}
  *
  * @see ./demo/data/basic.html
  *
  * @namespace basis.data.value
  */

  var namespace = this.path;

  // import names

  var getter = basis.getter;
  var cleaner = basis.cleaner;
  var Emitter = basis.event.Emitter;
  var AbstractData = basis.data.AbstractData;
  var Value = basis.data.Value;
  var STATE = basis.data.STATE;

  var instanceMap = {};

  //
  // BindValue
  //

  var EMMITER_HANDLER = {
    destroy: function(object){
      this.removeLink(object);
    }
  };

 /**
  * @class
  */
  var BindValue = Value.subclass({
    className: namespace + '.BindValue',

   /**
    * @type {Array.<object>}
    * @private
    */
    links_: null,

   /**
    * Settings for bindings.
    */
    bindingBridge: {
      attach: function(property, handler, context){
        return property.addLink(context, handler);
      },
      detach: function(property, handler, context){
        return property.removeLink(context, handler);
      },
      get: function(property){
        return property.value;
      }
    },

   /**
    * @event
    */
    emit_change: function(value, oldValue){
      Value.prototype.emit_change.call(this, value, oldValue);

      if (cleaner.globalDestroy)
        return;

      var cursor = this;
      while (cursor = cursor.links_)
        this.apply_(cursor, oldValue);
    },

   /**
    * @constructor
    */
    init: function(){
      Value.prototype.init.call(this);

      instanceMap[this.basisObjectId] = this;
    },

   /**
    * Adds link to object property or method. Optional parameter `format` using for
    * value convertation to another value or type.
    * If object is instance of {basis.event.Emitter}, BindValue attach handler which
    * removes property links on object destroy.
    *
    * @example
    *
    *   var value = new basis.data.value.BindValue({ ... });
    *
    *   value.addLink(object, 'property');          // object.property = value;
    *   value.addLink(object, 'property', '[{0}]'); // object.property = '[{0}]'.format(value, oldValue);
    *   value.addLink(object, 'property', Number);  // object.property = Number(value, oldValue);
    *   value.addLink(object, 'property', { a: 1, b: 2});  // object.property = { a: 1, b: 2 }[value];
    *   value.addLink(object, object.method);       // object.method(value, oldValue);
    *
    *   value.addLink(object, function(value, oldValue){ // {function}.call(object, value, oldValue);
    *     // some code
    *     // `this` refer to object
    *   });
    *
    *   // Trace property changes
    *   var historyOfChanges = new Array();
    *   var value = new basis.data.value.BindValue({ value: 1 });
    *   value.addLink(historyOfChanges, historyOfChanges.push);  // historyOfChanges -> [1]
    *   value.set(2);  // historyOfChanges -> [1, 2]
    *   value.set(3);  // historyOfChanges -> [1, 2, 3]
    *   value.set(3);  // property didn't change self value
    *                     // historyOfChanges -> [1, 2, 3]
    *   value.set(1);  // historyOfChanges -> [1, 2, 3, 1]
    *
    *   // Use console for log value changes
    *   value.addLink(console, console.log, 'new value is {0}');
    *
    * @param {object} object Target object.
    * @param {string|function} field Field or method of target object.
    * @param {string|function|object=} format Value modificator.
    * @return {object} Returns object.
    */
    addLink: function(object, field, format){
      // process format argument
      if (typeof format != 'function')
        format = getter(basis.fn.$self, format);

      // check for duplicates
      /** @cut */ var cursor = this;
      /** @cut */ while (cursor = cursor.links_)
      /** @cut */   if (cursor.object === object && cursor.field == field)
      /** @cut */   {
      /** @cut */     basis.dev.warn(this.constructor.className + '#addLink: Duplicate link for property');
      /** @cut */     break;
      /** @cut */   }

      // create link
      this.links_ = {
        object: object,
        field: field,
        format: format,
        links_: this.links_
      };

      // add handler if object is basis.event.Emitter
      if (object instanceof Emitter)
        object.addHandler(EMMITER_HANDLER, this);

      // make effect on object
      this.apply_(this.links_);

      return object;
    },

   /**
    * Removes link from object. Parameters must be the same
    * as for addLink method. If field omited all links are remove.
    *
    * @example
    *   var value = new basis.data.value.BindValue();
    *   // add links
    *   value.addLink(object, 'field');
    *   value.addLink(object, object.method);
    *   // remove links
    *   value.removeLink(object, 'field');
    *   value.removeLink(object, object.method);
    *   // or remove all links for object
    *   value.removeLink(object);
    *
    *   // incorrect usage
    *   value.addLink(object, function(value){ this.field = value * 2; });
    *   ...
    *   value.removeLink(object, function(value){ this.field = value * 2; });
    *   // link to object will not removed
    *
    *   // right way
    *   var linkHandler = function(value){ this.field = value * 2; };
    *   value.addLink(object, linkHandler);
    *   ...
    *   value.removeLink(object, linkHandler);
    *
    *   // for cases when object is instance of {basis.event.Emitter} removing link on destroy is not required
    *   var emitterInstance = new basis.event.Emitter();
    *   value.addLink(emitterInstance, 'title');
    *   ...
    *   emitterInstance.destroy();    // links to emitterInstance will be removed
    *
    * @param {object} object
    * @param {string|function=} field
    */
    removeLink: function(object, field){
      var cursor = this;
      var prev;

      while (prev = cursor, cursor = cursor.links_)
        if (cursor.object === object && (!field || field == cursor.field))
        {
          // prevent apply new values
          cursor.field = null;

          // delete link
          prev.links_ = cursor.links_;

          // remove handler if object is basis.event.Emitter
          if (cursor.object instanceof Emitter)
            cursor.object.removeHandler(EMMITER_HANDLER, this);
        }
    },

   /**
    * Removes all property links to objects.
    */
    clear: function(){
      var cursor = this;

      // remove event handlers from basis.event.Emitter instances
      while (cursor = cursor.links_)
        if (cursor.object instanceof Emitter)
          cursor.object.removeHandler(EMMITER_HANDLER, this);

      // clear links
      this.links_ = null;
    },

   /**
    * @param {object} link
    * @param {*} oldValue Object value before changes.
    * @private
    */
    apply_: function(link, oldValue){
      var field = link.field;

      // field specified
      if (field != null)
      {
        var value = link.format(this.value);
        var object = link.object;

        if (typeof field == 'function')
          field.call(object, value, arguments.length < 2 ? value : link.format(oldValue));
        else
          object[field] = value;
      }
    },

   /**
    * @destructor
    */
    destroy: function(){
      this.clear();

      Value.prototype.destroy.call(this);

      this.links_ = null;
      delete instanceMap[this.basisObjectId];
    }
  });

 /**
  * @class
  */
  var Property = BindValue.subclass({
    className: namespace + '.Property',

    // use custom constructor
    extendConstructor_: false,

   /**
    * @param {object} initValue Initial value for object.
    * @param {object=} handler
    * @param {function()=} proxy
    * @constructor
    */
    init: function(initValue, handler, proxy){
      this.value = initValue;
      this.handler = handler;
      this.proxy = proxy;

      BindValue.prototype.init.call(this);
    }
  });

  //
  //  Object set
  //
                                       // priority: lowest -------------------------------------------------------------> highest
  var OBJECTSET_STATE_PRIORITY = STATE.priority; //[STATE.READY, STATE.DEPRECATED, STATE.UNDEFINED, STATE.ERROR, STATE.PROCESSING];
  var OBJECTSET_HANDLER = {
    stateChanged: function(){
      this.fire(false, true);
    },
    update: function(){
      this.fire(true);
    },
    change: function(){
      this.fire(true);
    },
    destroy: function(object){
      this.remove(object);
    }
  };

 /**
  * @class
  */
  var ObjectSet = BindValue.subclass({
    className: namespace + '.ObjectSet',

   /**
    * @type {Array.<basis.data.Object>}
    */
    objects: null,

   /**
    * @inheritDoc
    */
    value: 0,

   /**
    * @type {boolean}
    * @private
    */
    valueChanged_: false,

   /**
    * @type {function}
    */
    calculateValue: function(){
      return this.value + 1;
    },

   /**
    * @type {boolean}
    */
    calculateOnInit: false,

   /**
    * @type {Array.<basis.data.STATE>}
    */
    statePriority: OBJECTSET_STATE_PRIORITY,

   /**
    * @type {boolean}
    * @private
    */
    stateChanged_: true,

   /**
    * @type {number}
    * @private
    */
    timer_: false,

   /**
    * @constructor
    */
    init: function(){
      BindValue.prototype.init.call(this);

      var objects = this.objects;
      this.objects = [];

      if (objects && Array.isArray(objects))
      {
        this.lock();
        this.add.apply(this, objects);
        this.unlock();
      }

      this.valueChanged_ = this.stateChanged_ = !!this.calculateOnInit;
      this.update();
    },

   /**
    * Adds one or more AbstractData instances to objects collection.
    * @param {...basis.data.AbstractData}
    */
    add: function(/* dataObject1 .. dataObjectN */){
      for (var i = 0, len = arguments.length; i < len; i++)
      {
        var object = arguments[i];
        if (object instanceof AbstractData)
        {
          if (this.objects.add(object))
            object.addHandler(OBJECTSET_HANDLER, this);
        }
        else
          throw this.constructor.className + '#add: Instance of AbstractData required';
      }

      this.fire(true, true);
    },

   /**
    * Removes AbstractData instance from objects collection.
    * @param {basis.data.AbstractData} object
    */
    remove: function(object){
      if (this.objects.remove(object))
        object.removeHandler(OBJECTSET_HANDLER, this);

      this.fire(true, true);
    },

   /**
    * Removes all AbstractData instances from objects collection.
    */
    clear: function(){
      for (var i = 0, object; object = this.objects[i]; i++)
        object.removeHandler(OBJECTSET_HANDLER, this);

      this.objects.clear();

      this.fire(true, true);
    },

   /**
    * @param {boolean=} valueChanged
    * @param {boolean=} stateChanged
    */
    fire: function(valueChanged, stateChanged){
      if (!this.locked)
      {
        this.valueChanged_ = this.valueChanged_ || !!valueChanged;
        this.stateChanged_ = this.stateChanged_ || !!stateChanged;

        if (!this.timer_ && (this.valueChanged_ || this.stateChanged_))
          this.timer_ = basis.timer.setImmediate(this.update.bind(this));
      }
    },

   /**
    * Makes object not sensitive for attached AbstractData changes.
    */
    lock: function(){
      this.locked = true;
    },

   /**
    * Makes object sensitive for attached AbstractData changes.
    */
    unlock: function(){
      this.locked = false;
    },

   /**
    * @private
    */
    update: function(){
      var valueChanged = this.valueChanged_;
      var stateChanged = this.stateChanged_;

      this.valueChanged_ = false;
      this.stateChanged_ = false;

      this.timer_ = basis.timer.clearImmediate(this.timer_);

      if (!cleaner.globalDestroy)
      {
        if (valueChanged)
          this.set(this.calculateValue());

        if (stateChanged)
        {
          var len = this.objects.length;
          if (!len)
            this.setState(STATE.UNDEFINED);
          else
          {
            var maxWeight = -2;
            var curObject;

            for (var i = 0; i < len; i++)
            {
              var object = this.objects[i];
              var weight = this.statePriority.indexOf(String(object.state));
              if (weight > maxWeight)
              {
                curObject = object;
                maxWeight = weight;
              }
            }

            if (curObject)
              this.setState(curObject.state, curObject.state.data);
          }
        }
      }
    },

   /**
    * @destructor
    */
    destroy: function(){
      this.lock();
      this.clear();
      if (this.timer_)
        basis.timer.clearImmediate(this.timer_);

      BindValue.prototype.destroy.call(this);
    }
  });

  //
  // Expression
  //

 /**
  * @class
  */
  var Expression = Property.subclass({
    className: namespace + '.Expression',

    init: function(args, calc){
      BindValue.prototype.init.call(this);

      var args = basis.array(arguments);
      var calc = args.pop();

      if (typeof calc != 'function')
      {
        ;;;basis.dev.warn(this.constructor.className + ': last argument of constructor must be a function');
        calc = basis.fn.$undef;
      }

      if (args.length == 1)
      {
        args[0].addLink(this, function(value){
          this.set(calc.call(this, value));
        });
      }

      if (args.length > 1)
      {
        var changeWatcher = new ObjectSet({
          objects: args,
          calculateOnInit: true,
          calculateValue: function(){
            return calc.apply(this, args.map(function(item){
              return item.value;
            }));
          }
        });

        changeWatcher.addLink(this, this.set);

        this.addHandler({
          destroy: function(){
            if (!cleaner.globalDestroy)
              changeWatcher.destroy();
          }
        });
      }
    }
  });

  //
  // clean up instances
  //

  cleaner.add(function(){
    for (var key in instanceMap)
      instanceMap[key].destroy();

    instanceMap = null;
  });

  //
  // export names
  //

  module.exports = {
    BindValue: BindValue,
    Property: Property,
    ObjectSet: ObjectSet,
    Expression: Expression
  };
<|endoftext|>


<commit_before>const Util = require('../../src/util');
const OFFSET = 12;

module.exports = {
  getDefaultCfg() {
    return {
      formatText(model) { return model.label; }
    };
  },
  getEvents() {
    return {
      'node:mouseenter': 'onMouseEnter',
      'node:mouseleave': 'onMouseLeave',
      'node:mousemove': 'onMouseMove'
    };
  },
  onMouseEnter(e) {
    const self = this;
    if (!self.shouldBegin(e)) {
      return;
    }
    const item = e.item;
    self.currentTarget = item;
    self.showTooltip(e);
  },
  onMouseMove(e) {
    if (!this.shouldUpdate(e)) {
      return;
    }
    this.updatePosition(e);
  },
  onMouseLeave(e) {
    if (!this.shouldEnd(e)) {
      return;
    }
    this.currentTarget = null;
    this.hideTooltip();
  },
  showTooltip(e) {
    const self = this;
    if (!e.item) {
      return;
    }
    let container = self.container;
    if (!container) {
      container = self._createTooltip(self.graph.get('canvas'));
    }
    const text = self.formatText(e.item.get('model'), e);
    container.innerHTML = text;
    this.updatePosition(e);
    Util.modifyCSS(this.container, { visibility: 'visible' });
  },
  hideTooltip() {
    Util.modifyCSS(this.container, {
      visibility: 'hidden'
    });
  },
  updatePosition(e) {
    const width = this.width;
    const height = this.height;
    const container = this.container;
    let x = e.x;
    let y = e.y;
    const bbox = container.getBoundingClientRect();
    if (x > width / 2) {
      x -= (bbox.width + OFFSET);
    } else {
      x += OFFSET;
    }
    if (y > height / 2) {
      y -= (bbox.height + OFFSET);
    } else {
      y += OFFSET;
    }
    const left = x + 'px';
    const top = y + 'px';
    Util.modifyCSS(this.container, { left, top });
  },
  _createTooltip(canvas) {
    const el = canvas.get('el');
    el.style.position = 'relative';
    const container = Util.createDom('

');
    el.parentNode.appendChild(container);
    Util.modifyCSS(container, {
      position: 'absolute',
      visibility: 'visible'
    });
    this.width = canvas.get('width');
    this.height = canvas.get('height');
    this.container = container;
    return container;
  }
};
<commit_msg>fix(tooltip): emit tooltip event<commit_after>const Util = require('../util');
const OFFSET = 12;

module.exports = {
  getDefaultCfg() {
    return {
      formatText(model) { return model.label; }
    };
  },
  getEvents() {
    return {
      'node:mouseenter': 'onMouseEnter',
      'node:mouseleave': 'onMouseLeave',
      'node:mousemove': 'onMouseMove'
    };
  },
  onMouseEnter(e) {
    const self = this;
    if (!self.shouldBegin(e)) {
      return;
    }
    const item = e.item;
    self.currentTarget = item;
    if (self.shouldUpdate(e)) {
      self.showTooltip(e, 'show');
      self.graph.emit('tooltipchange', { item: e.item, action: 'show' });
    }
  },
  onMouseMove(e) {
    if (!this.shouldUpdate(e)) {
      return;
    }
    if (!this.currentTarget || e.item !== this.currentTarget) {
      return;
    }
    this.updatePosition(e);
  },
  onMouseLeave(e) {
    if (!this.shouldEnd(e)) {
      return;
    }
    this.hideTooltip();
    this.graph.emit('tooltipchange', { item: this.currentTarget, action: 'hide' });
    this.currentTarget = null;
  },
  showTooltip(e) {
    const self = this;
    if (!e.item) {
      return;
    }
    let container = self.container;
    if (!container) {
      container = self._createTooltip(self.graph.get('canvas'));
      self.container = container;
    }
    const text = self.formatText(e.item.get('model'), e);
    container.innerHTML = text;
    this.updatePosition(e);
    Util.modifyCSS(this.container, { visibility: 'visible' });
  },
  hideTooltip() {
    Util.modifyCSS(this.container, {
      visibility: 'hidden'
    });
  },
  updatePosition(e) {
    const width = this.width;
    const height = this.height;
    const container = this.container;
    let x = e.x;
    let y = e.y;
    const bbox = container.getBoundingClientRect();
    if (x > width / 2) {
      x -= (bbox.width + OFFSET);
    } else {
      x += OFFSET;
    }
    if (y > height / 2) {
      y -= (bbox.height + OFFSET);
    } else {
      y += OFFSET;
    }
    const left = x + 'px';
    const top = y + 'px';
    Util.modifyCSS(this.container, { left, top });
  },
  _createTooltip(canvas) {
    const el = canvas.get('el');
    el.style.position = 'relative';
    const container = Util.createDom('

');
    el.parentNode.appendChild(container);
    Util.modifyCSS(container, {
      position: 'absolute',
      visibility: 'visible'
    });
    this.width = canvas.get('width');
    this.height = canvas.get('height');
    this.container = container;
    return container;
  }
};
<|endoftext|>


<commit_before>import React , {Component, PropTypes} from 'react';
import DefaultFieldComponent from '../components/field';
import find from 'lodash/find';
import get from 'lodash/get';
const FIELD_CONTEXT_TYPE = {
    fieldHelpers: PropTypes.object
};

// TODO: local override with entity definitions

const getFieldMetadata = (propertyName, entityPath, definitions, domains) => {
    const propertyDefinition = get(definitions, `${entityPath}.${propertyName}`);
    if (!propertyDefinition) throw new Error(`Property ${propertyName} does not exist in definition ${entityPath}`);
    return {
        isRequired: propertyDefinition.isRequired,
        ...domains[propertyDefinition.domain]
    }
}

const getListFieldMetadata = (propertyName, entityPath = {}, definitions, domains) => {
  const propertyDefinition = get(definitions,entityPath)
  return {
    isRequired: propertyDefinition.isRequired,
    ...domains[propertyDefinition.domain]
  };
}

const fieldForBuilder = (props, textOnly = false, multiple = false, list = false, fieldForListBuilder) => (propertyName, {FieldComponent = DefaultFieldComponent, redirectEntityPath, entityPath, onBlur: userDefinedOnBlur, ...options} = {}) => {
    const {fields, definitions, domains, onInputChange, onInputBlur, entityPathArray, editing} = props;
    // Check if the form has multiple entityPath. If it's the case, then check if an entityPath for the field is provided
    // todo: souldn't it check if the property exists in both entity path from the array and throw an error if it is so.
    // Maybe the cost is too high.
    if (entityPathArray.length > 1 && !entityPath) throw new Error(`You must provide an entityPath when calling fieldFor('${propertyName}') since the form has multiple entityPath ${entityPathArray}`);
    entityPath = entityPath ? entityPath : entityPathArray[0];
    const metadata = list ? getListFieldMetadata(propertyName, redirectEntityPath, definitions, domains) :  getFieldMetadata(propertyName, entityPath, definitions, domains);

    const field =  find(fields, {entityPath, name: propertyName});
    const {rawInputValue} = field || {};
    const onChange = rawValue => {
        onInputChange(propertyName, entityPath, rawValue);
        if (options.onChange) options.onChange(rawValue);
    };

    // Construct the onBlur, with the validation if validateOnBlur has not been set to false in the domain
    const onBlur = () => {
        if (get(definitions, `${entityPath}.${propertyName}`).validateOnBlur !== false) onInputBlur(propertyName, entityPath, rawInputValue);
        if (userDefinedOnBlur) userDefinedOnBlur();
    };
    const fieldForLine = list ? fieldForListBuilder(entityPath, propertyName)(props): {};
    const finalEditing = options.editing !== undefined ? options.editing : editing;
    return <FieldComponent {...options} {...field} fieldForLine={fieldForLine} multiple={multiple} list={list} textOnly={textOnly} editing={finalEditing} name={propertyName} onBlur={onBlur} onChange={onChange} metadata={metadata} />;
}

const fieldForListBuilder = (entityPathList, propertyNameList) => {
  const fieldForLineBuilder = (connectedComponentProps) => (propertyName, {FieldComponent = DefaultFieldComponent, entityPath, onBlur: userDefinedOnBlur, ...options} = {}, index) => {
      const {fields, definitions, domains, onInputChange, onInputBlur, entityPathArray, editing, onInputBlurList} = connectedComponentProps;
      const {onChange: optionsOnChange, ...otherOptions} = options;
      const fieldTab = find(fields, {name: propertyNameList});
      const metadata = getFieldMetadata(propertyName, entityPath, definitions, domains);
      const field = {
        rawInputValue : fieldTab.rawInputValue[index][propertyName],
        formattedInputValue: fieldTab.formattedInputValue[index][propertyName]
      }
      const onChange = rawValue => {
       fieldTab.rawInputValue[index][propertyName] = rawValue;
        onInputChange(propertyNameList, entityPathList, fieldTab.rawInputValue);
        if (optionsOnChange) optionsOnChange(rawValue);
      }
      const onBlur = () => {
        if (get(definitions, `${entityPathList}.${propertyNameList}`).validateOnBlur !== false) onInputBlurList(propertyNameList, entityPathList, fieldTab.rawInputValue[index][propertyName], propertyName, index);
        if (userDefinedOnBlur) userDefinedOnBlur();
      }

      return <FieldComponent {...field} error={fieldTab.error && fieldTab.error[index] && fieldTab.error[index][propertyName]} editing={editing} name={propertyName} metadata={metadata} onChange={onChange} onBlur={onBlur} {...otherOptions}/>;
  }
  return fieldForLineBuilder;

}

export function connect() {
    return function connectComponent(ComponentToConnect) {
        function FieldConnectedComponent({_behaviours, ...otherProps}, {fieldHelpers}) {
            const fieldFor = fieldHelpers.fieldForBuilder(otherProps);
            const textFor = fieldHelpers.fieldForBuilder(otherProps, true);
            const selectFor = fieldHelpers.fieldForBuilder(otherProps, false, true);
            const listFor = fieldHelpers.fieldForBuilder(otherProps, false, false, true, fieldHelpers.fieldForListBuilder);
            const behaviours = {connectedToFieldHelpers: true, ..._behaviours};
            return <ComponentToConnect {...otherProps} _behaviours={behaviours} fieldFor={fieldFor} selectFor={selectFor} textFor={textFor} listFor={listFor}/>;
        }
        FieldConnectedComponent.displayName = `${ComponentToConnect.displayName}FieldConnected`;
        FieldConnectedComponent.contextTypes = FIELD_CONTEXT_TYPE;
        return FieldConnectedComponent;
    }
}

class FieldProvider extends Component {
    getChildContext() {
        return {
            fieldHelpers: {
                fieldForBuilder,
                fieldForListBuilder
            }
        }
    }
    render() {
        return this.props.children;
    }
}
FieldProvider.defaultProps = {
    FieldComponent: DefaultFieldComponent
}
FieldProvider.childContextTypes = FIELD_CONTEXT_TYPE;

export const Provider = FieldProvider;
<commit_msg>[fieldFor] Add options and userDefinedOnChange.<commit_after>import React , {Component, PropTypes} from 'react';
import DefaultFieldComponent from '../components/field';
import find from 'lodash/find';
import get from 'lodash/get';
const FIELD_CONTEXT_TYPE = {
    fieldHelpers: PropTypes.object
};

// TODO: local override with entity definitions

const getFieldMetadata = (propertyName, entityPath, definitions, domains) => {
    const propertyDefinition = get(definitions, `${entityPath}.${propertyName}`);
    if (!propertyDefinition) throw new Error(`Property ${propertyName} does not exist in definition ${entityPath}`);
    return {
        isRequired: propertyDefinition.isRequired,
        ...domains[propertyDefinition.domain]
    }
}

const getListFieldMetadata = (propertyName, entityPath = {}, definitions, domains) => {
  const propertyDefinition = get(definitions,entityPath)
  return {
    isRequired: propertyDefinition.isRequired,
    ...domains[propertyDefinition.domain]
  };
}

const fieldForBuilder = (props, textOnly = false, multiple = false, list = false, fieldForListBuilder) => (propertyName, {FieldComponent = DefaultFieldComponent, redirectEntityPath, entityPath, onBlur: userDefinedOnBlur,onChange: userDefinedOnChange, ...options} = {}) => {
    const {fields, definitions, domains, onInputChange, onInputBlur, entityPathArray, editing} = props;
    // Check if the form has multiple entityPath. If it's the case, then check if an entityPath for the field is provided
    // todo: souldn't it check if the property exists in both entity path from the array and throw an error if it is so.
    // Maybe the cost is too high.
    if (entityPathArray.length > 1 && !entityPath) throw new Error(`You must provide an entityPath when calling fieldFor('${propertyName}') since the form has multiple entityPath ${entityPathArray}`);
    entityPath = entityPath ? entityPath : entityPathArray[0];
    const metadata = list ? getListFieldMetadata(propertyName, redirectEntityPath, definitions, domains) :  getFieldMetadata(propertyName, entityPath, definitions, domains);

    const field =  find(fields, {entityPath, name: propertyName});
    const {rawInputValue} = field || {};
    const onChange = rawValue => {
        onInputChange(propertyName, entityPath, rawValue);
        if (userDefinedOnChange) userDefinedOnChange(rawValue);
    };

    // Construct the onBlur, with the validation if validateOnBlur has not been set to false in the domain
    const onBlur = () => {
        if (get(definitions, `${entityPath}.${propertyName}`).validateOnBlur !== false) onInputBlur(propertyName, entityPath, rawInputValue);
        if (userDefinedOnBlur) userDefinedOnBlur();
    };
    const fieldForLine = list ? fieldForListBuilder(entityPath, propertyName)(props): {};
    const finalEditing = options.editing !== undefined ? options.editing : editing;
    return <FieldComponent  {...field} fieldForLine={fieldForLine} multiple={multiple} list={list} textOnly={textOnly} editing={finalEditing} name={propertyName} onBlur={onBlur} onChange={onChange} metadata={metadata} {...options}/>;
}

const fieldForListBuilder = (entityPathList, propertyNameList) => {
  const fieldForLineBuilder = (connectedComponentProps) => (propertyName, {FieldComponent = DefaultFieldComponent, entityPath, onBlur: userDefinedOnBlur,onChange: userDefinedOnChange,  ...options} = {}, index) => {
      const {fields, definitions, domains, onInputChange, onInputBlur, entityPathArray, editing, onInputBlurList} = connectedComponentProps;
      const {onChange: optionsOnChange, ...otherOptions} = options;
      const fieldTab = find(fields, {name: propertyNameList});
      const metadata = getFieldMetadata(propertyName, entityPath, definitions, domains);
      const field = {
        rawInputValue : fieldTab.rawInputValue[index][propertyName],
        formattedInputValue: fieldTab.formattedInputValue[index][propertyName]
      }
      const onChange = rawValue => {
       fieldTab.rawInputValue[index][propertyName] = rawValue;
        onInputChange(propertyNameList, entityPathList, fieldTab.rawInputValue);
        if (userDefinedOnChange) userDefinedOnChange(rawValue);
      }
      const onBlur = () => {
        if (get(definitions, `${entityPathList}.${propertyNameList}`).validateOnBlur !== false) onInputBlurList(propertyNameList, entityPathList, fieldTab.rawInputValue[index][propertyName], propertyName, index);
        if (userDefinedOnBlur) userDefinedOnBlur();
      }

      return <FieldComponent {...field} error={fieldTab.error && fieldTab.error[index] && fieldTab.error[index][propertyName]} editing={editing} name={propertyName} metadata={metadata} onChange={onChange} onBlur={onBlur} {...options}/>;
  }
  return fieldForLineBuilder;

}

export function connect() {
    return function connectComponent(ComponentToConnect) {
        function FieldConnectedComponent({_behaviours, ...otherProps}, {fieldHelpers}) {
            const fieldFor = fieldHelpers.fieldForBuilder(otherProps);
            const textFor = fieldHelpers.fieldForBuilder(otherProps, true);
            const selectFor = fieldHelpers.fieldForBuilder(otherProps, false, true);
            const listFor = fieldHelpers.fieldForBuilder(otherProps, false, false, true, fieldHelpers.fieldForListBuilder);
            const behaviours = {connectedToFieldHelpers: true, ..._behaviours};
            return <ComponentToConnect {...otherProps} _behaviours={behaviours} fieldFor={fieldFor} selectFor={selectFor} textFor={textFor} listFor={listFor}/>;
        }
        FieldConnectedComponent.displayName = `${ComponentToConnect.displayName}FieldConnected`;
        FieldConnectedComponent.contextTypes = FIELD_CONTEXT_TYPE;
        return FieldConnectedComponent;
    }
}

class FieldProvider extends Component {
    getChildContext() {
        return {
            fieldHelpers: {
                fieldForBuilder,
                fieldForListBuilder
            }
        }
    }
    render() {
        return this.props.children;
    }
}
FieldProvider.defaultProps = {
    FieldComponent: DefaultFieldComponent
}
FieldProvider.childContextTypes = FIELD_CONTEXT_TYPE;

export const Provider = FieldProvider;
<|endoftext|>


<commit_before>/**
 * @author Ian
 * @created 2017-04-28 16:48:21
 */
import { BS } from 'dove.max.sdk'

const ToolsType = {
  WS: 'websocket-server',
  NTASK: 'native-task',
  STOP_NTASK: 'stop-native-task'
}

const ToolsMap = {
  // common
  'get.image.thumb': { type: ToolsType.WS, cli: 'aiexifcool/common/index', action: 'getImageThumb' },

  // fix
  'start.fix.image': { type: ToolsType.WS, cli: 'aiexifcool/fix.image/index', action: 'startFix' },
  'stop.fix.image': { type: ToolsType.WS, cli: 'aiexifcool/fix.image/index', action: 'stopFix' },

  // remove
  'start.remove.exif': { type: ToolsType.WS, cli: 'aiexifcool/remove.exif/index', action: 'removeExif' },
  'stop.remove.exif': { type: ToolsType.WS, cli: 'aiexifcool/remove.exif/index', action: 'stopRemove' },

  // modify
  'get.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'getExifInfo' },
  'start.modify.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'modifyExifInfo' },
  'stop.modify.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'stopModify' },

  // ////////////////////////////////////////////////////////////////////////////////////////////////////
  'gif2apng': { type: ToolsType.NTASK, cli: BS.b$.App.getAppPluginDir() + '/cligif2apng', command: [], mainThread: false },
  'stop.gif2apng': { type: ToolsType.STOP_NTASK, command: ['stop', 'calltask'] }
}

// export
export { ToolsMap, ToolsType }
<commit_msg>update tools_map<commit_after>/**
 * @author Ian
 * @created 2017-04-28 16:48:21
 */
import { BS } from 'dove.max.sdk'

const ToolsType = {
  WS: 'websocket-server',
  NTASK: 'native-task',
  STOP_NTASK: 'stop-native-task'
}

const ToolsMap = {
  // common
  'get.image.thumb': { type: ToolsType.WS, cli: 'aiexifcool/common/index', action: 'getImageThumb' },

  // fix
  'start.fix.image': { type: ToolsType.WS, cli: 'aiexifcool/fix.image/index', action: 'startFix' },
  'stop.fix.image': { type: ToolsType.WS, cli: 'aiexifcool/fix.image/index', action: 'stopFix' },

  // remove
  'start.remove.exif': { type: ToolsType.WS, cli: 'aiexifcool/remove.exif/index', action: 'removeExif' },
  'stop.remove.exif': { type: ToolsType.WS, cli: 'aiexifcool/remove.exif/index', action: 'stopRemove' },

  // modify
  'get.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'getExifInfo' },
  'start.modify.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'modifyExifInfo' },
  'stop.modify.exif': { type: ToolsType.WS, cli: 'aiexifcool/edit.image/index', action: 'stopModify' },

  // ////////////////////////////////////////////////////////////////////////////////////////////////////
  'gif2apng': { type: ToolsType.NTASK, cli: BS.b$.App.getAppPluginDir() + '/CLIgif2apng', command: [], mainThread: false },
  'stop.gif2apng': { type: ToolsType.STOP_NTASK, command: ['stop', 'calltask'] }
}

// export
export { ToolsMap, ToolsType }
<|endoftext|>


<commit_before>//TODO: Enable enclosure on production
/*(function () {*/
	var startingAnts = 1,
		//maxAnts = 5000, //Not used temporarily
		width = 300,
		height = 300,
		colors = {
			empty: [255, 255, 255],
			ant: {
				normal: [0, 0, 0, 255],
				dead: [192, 192, 192, 255]
			},
			trail: [255, 0, 0, 255]
		},
		context,
		mainMap,
		canvas,
		ants = [],
		DEBUG_DRAW = { locations: [] },
		food = { locations: [], color: [0, 255, 0] },
		nest = { x: _.random(10, width - 10), y: _.random(10, height - 10) },
		foodStored = 0,
		trail = { count: 0, trigger: 100, wash: 1, color: [255, 0, 0, 255] }, //Every "trigger" times, the pheromones trail will wash by "wash" times
		defaultAnt = {
			x: 10,
			y: 10,
			lastX: 0,
			lastY: 1,
			life: 255,
			foodFound: false //True for an ant that has found food and is returning
		};

	function init() {
		canvas = document.getElementById('world');
		canvas.width = width;
		canvas.height = height;

		canvas.addEventListener('click', function(el) {
			console.log("click");
			console.log(el);

			function getPixelChannel(imgData, x, y, channel) {
				return imgData.data[((y * imgData.width + x) * 4) + channel];
			}

			function getCursorPosition(canvas, event) {
				var rect = canvas.getBoundingClientRect();
				var x = event.clientX - rect.left;
				var y = event.clientY - rect.top;

				return { x: Math.round(x / 2), y: Math.round(y / 2) };
			}

			var rgba = [getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 0),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 1),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 2),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 3)];

			console.log(rgba.join("-"));

			_.times(30, function(i) {
				function getCursorPosition(canvas, event) {
					var rect = canvas.getBoundingClientRect();
					var x = event.clientX - rect.left;
					var y = event.clientY - rect.top;

					return { x: Math.round(x / 2), y: Math.round(y / 2) };
				}
				getCursorPosition(canvas, el);
				var toAdd = getCursorPosition(canvas, el);
				toAdd.x += i;
				//toAdd.y += i;
				DEBUG_DRAW.locations.push(toAdd);
			});

			console.log(DEBUG_DRAW);

		}, false); //DEBUG MOUSE DRAWING

		context = canvas.getContext('2d');
		mainMap = context.createImageData(width, height);

		//Create starting ants
		_.times(startingAnts, function(i) {
			ants.push(_.extend(_.clone(defaultAnt), {
				x: nest.x + _.random(0, 5),
				y: nest.y + _.random(0, 5)
			}));
		});

		//Create starting food locations
		_.times(_.random(5, 10), function(i) {
			var xCenter = _.random(0, width),
				yCenter = _.random(0, height);

			_.times(_.random(3, 8), function(i) {
				food.locations.push({
					"x": xCenter + _.random(-10, 10),
					"y": yCenter + _.random(-10, 10)
				});
			});
		});

		processWorld();
	}

	processWorld = function() {

		context.putImageData(mainMap, 0, 0);

		function getPixelChannel(imgData, x, y, channel) {
			return imgData.data[((y * imgData.width + x) * 4) + channel];
		}

		function setPixelChannel(imgData, x, y, channel, value, alpha, mixAlpha) {
			if (mixAlpha) {
				//value = (imgData.data[((y * imgData.width + x) * 4) + channel] + value) / 2;
				alpha = (imgData.data[((y * imgData.width + x) * 4) + 3] + alpha) / 2;
			}

			imgData.data[((y * imgData.width + x) * 4) + channel] = value;
			if (alpha) imgData.data[((y * imgData.width + x) * 4) + 3] = alpha;
		}

		function paintTrail(x, y) {
			var index = ((y * width + x) * 4);

			mainMap.data[index] = 255;
			mainMap.data[index + 1] = 0;
			mainMap.data[index + 2] = 0;
			mainMap.data[index + 3] = 255;
		}

		//This gets a channel strength (channel * alpha)
		function getTrailStrength(channel, x, y) {
			var trail = getPixelChannel(mainMap, x, y, channel);
			var alpha = getPixelChannel(mainMap, x, y, 3);

			return (alpha * trail) / Math.pow(255, 2);
		}

		trail.count++;
		if (trail.count >= trail.trigger) {
			//console.log("wash");
			trail.count = 0;
			_.times(width, function(x) {
				_.times(height, function(y) {
					var index = ((y * width + x) * 4) + 3;
					mainMap.data[index] = mainMap.data[index] - trail.wash;
					//context.putImageData(newPixel, x, y);
					//data[((y*currentCanvas.width+x) * 4) + 3] = data[((y*currentCanvas.width+x) * 4) + 3] - 1;
				});
			});

			context.putImageData(mainMap, 0, 0);
		}

		//Draw food locations
		_.each(food.locations, function(el) {
			setPixelChannel(mainMap, el.x, el.y, 1, 255, 255); //Paint ant G
		});

		//DRAW DEBUG LOCATIONS
		_.each(DEBUG_DRAW.locations, function(el) {
			setPixelChannel(mainMap, el.x, el.y, 1, 255, 255);
		});

		//Draw nest locations
		_.times(6, function(ix) {
			_.times(6, function(iy) {
				setPixelChannel(mainMap, nest.x + ix - 3, nest.y + iy - 3, 1, 255, 255);
			});
		});

		_.each(ants, function(ant) {
			var diffX,
				diffY;

			if (ant.foodFound) {
				setPixelChannel(mainMap, ant.x, ant.y, 0, 255, 255, true); //Paint food trail
			} else {
				setPixelChannel(mainMap, ant.x, ant.y, 2, 255, 255, true); //Paint walk trail

				_.times(3, function(px) {
					_.times(3, function(py) {
						setPixelChannel(mainMap, ant.x - ant.lastX + (px - 1), ant.y - ant.lastY + (py - 1), 2, 255, 50, true); //Paint walk trail
					});
				});

			}

			var bestPos = [],
				bestScore = -1;

			//Generate initial trail map
			var trailMap = [[0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]];

			//Blue score
			trailMap = _.map(trailMap, function(arr, x) {
				return _.map(arr, function(val, y) {
					var blueScore = getTrailStrength(2, ant.x + (x - 1), ant.y + (y - 1)); //Get blue trail strength

					//if (blueScore) return 0;

					blueScore = Math.abs(blueScore - 1);

					return blueScore * val;
				});
			});

			trailMap[ant.lastX + 1][ant.lastY + 1] = (trailMap[ant.lastX + 1][ant.lastY + 1] + 1) / 2;

			console.log(trailMap);

			//Get the best match by probability
			var getNextLocation = function () {
				var testX = _.random(2),
					testY = _.random(2);
				if (_.random(1, true) < trailMap[testX][testY]) {
					return {x: testX - 1, y: testY - 1};
				} else {
					return getNextLocation();
				}
			}

			var nextPos = getNextLocation();

			//console.log(nextPos);

			ant.lastX = nextPos.x;
			ant.lastY = nextPos.y;
			ant.x += nextPos.x;
			ant.y += nextPos.y;

			//Bounce ant on canvas limits
			if ((ant.x <= 0) || (ant.x >= (width - 1)) || (ant.y <= 0) || (ant.y >= (height - 1))) {
				bounceAnt();
			}

			function insideRange(a, b, range) {
				if (Math.abs(a - b) < range) return true;
				return false;
			}

			//Make a 180 degrees turn
			function bounceAnt() {
				ant.lastX = -ant.lastX;
				ant.lastY = -ant.lastY;
				ant.x += ant.lastX;
				ant.y += ant.lastY;
			}

			_.each(food.locations, function(food) {
				if ((insideRange(food.x, ant.x, 2)) && (insideRange(food.y, ant.y, 2)) && (ant.foodFound == false)) {
					console.log("foodFound");

					ant.foodFound = true; //Take food
					bounceAnt();
					console.log(food);
				}
			});

			//Check if ant is carrying food and is near the base
			if (ant.foodFound) {
				if (insideRange(nest.x, ant.x, 6) && insideRange(nest.y, ant.y, 6)) {
					console.log("nest");

					ant.foodFound = false; //Drop food
					bounceAnt();
					foodStored++;
					document.getElementsByClassName('foodStored')[0].innerHTML = foodStored; //Update food counter
				}
			}
		});

		setTimeout(function() {
			processWorld();
		}, 50);
	}

	init();
/*})();*/
<commit_msg>Remove lastX lastY increased probability<commit_after>//TODO: Enable enclosure on production
/*(function () {*/
	var startingAnts = 1,
		//maxAnts = 5000, //Not used temporarily
		width = 300,
		height = 300,
		colors = {
			empty: [255, 255, 255],
			ant: {
				normal: [0, 0, 0, 255],
				dead: [192, 192, 192, 255]
			},
			trail: [255, 0, 0, 255]
		},
		context,
		mainMap,
		canvas,
		ants = [],
		DEBUG_DRAW = { locations: [] },
		food = { locations: [], color: [0, 255, 0] },
		nest = { x: _.random(10, width - 10), y: _.random(10, height - 10) },
		foodStored = 0,
		trail = { count: 0, trigger: 100, wash: 1, color: [255, 0, 0, 255] }, //Every "trigger" times, the pheromones trail will wash by "wash" times
		defaultAnt = {
			x: 10,
			y: 10,
			lastX: 0,
			lastY: 1,
			life: 255,
			foodFound: false //True for an ant that has found food and is returning
		};

	function init() {
		canvas = document.getElementById('world');
		canvas.width = width;
		canvas.height = height;

		canvas.addEventListener('click', function(el) {
			console.log("click");
			console.log(el);

			function getPixelChannel(imgData, x, y, channel) {
				return imgData.data[((y * imgData.width + x) * 4) + channel];
			}

			function getCursorPosition(canvas, event) {
				var rect = canvas.getBoundingClientRect();
				var x = event.clientX - rect.left;
				var y = event.clientY - rect.top;

				return { x: Math.round(x / 2), y: Math.round(y / 2) };
			}

			var rgba = [getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 0),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 1),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 2),
				getPixelChannel(mainMap, getCursorPosition(canvas, el).x,  getCursorPosition(canvas, el).y, 3)];

			console.log(rgba.join("-"));

			_.times(30, function(i) {
				function getCursorPosition(canvas, event) {
					var rect = canvas.getBoundingClientRect();
					var x = event.clientX - rect.left;
					var y = event.clientY - rect.top;

					return { x: Math.round(x / 2), y: Math.round(y / 2) };
				}
				getCursorPosition(canvas, el);
				var toAdd = getCursorPosition(canvas, el);
				toAdd.x += i;
				//toAdd.y += i;
				DEBUG_DRAW.locations.push(toAdd);
			});

			console.log(DEBUG_DRAW);

		}, false); //DEBUG MOUSE DRAWING

		context = canvas.getContext('2d');
		mainMap = context.createImageData(width, height);

		//Create starting ants
		_.times(startingAnts, function(i) {
			ants.push(_.extend(_.clone(defaultAnt), {
				x: nest.x + _.random(0, 5),
				y: nest.y + _.random(0, 5)
			}));
		});

		//Create starting food locations
		_.times(_.random(5, 10), function(i) {
			var xCenter = _.random(0, width),
				yCenter = _.random(0, height);

			_.times(_.random(3, 8), function(i) {
				food.locations.push({
					"x": xCenter + _.random(-10, 10),
					"y": yCenter + _.random(-10, 10)
				});
			});
		});

		processWorld();
	}

	processWorld = function() {

		context.putImageData(mainMap, 0, 0);

		function getPixelChannel(imgData, x, y, channel) {
			return imgData.data[((y * imgData.width + x) * 4) + channel];
		}

		function setPixelChannel(imgData, x, y, channel, value, alpha, mixAlpha) {
			if (mixAlpha) {
				//value = (imgData.data[((y * imgData.width + x) * 4) + channel] + value) / 2;
				alpha = (imgData.data[((y * imgData.width + x) * 4) + 3] + alpha) / 2;
			}

			imgData.data[((y * imgData.width + x) * 4) + channel] = value;
			if (alpha) imgData.data[((y * imgData.width + x) * 4) + 3] = alpha;
		}

		function paintTrail(x, y) {
			var index = ((y * width + x) * 4);

			mainMap.data[index] = 255;
			mainMap.data[index + 1] = 0;
			mainMap.data[index + 2] = 0;
			mainMap.data[index + 3] = 255;
		}

		//This gets a channel strength (channel * alpha)
		function getTrailStrength(channel, x, y) {
			var trail = getPixelChannel(mainMap, x, y, channel);
			var alpha = getPixelChannel(mainMap, x, y, 3);

			return (alpha * trail) / Math.pow(255, 2);
		}

		trail.count++;
		if (trail.count >= trail.trigger) {
			//console.log("wash");
			trail.count = 0;
			_.times(width, function(x) {
				_.times(height, function(y) {
					var index = ((y * width + x) * 4) + 3;
					mainMap.data[index] = mainMap.data[index] - trail.wash;
					//context.putImageData(newPixel, x, y);
					//data[((y*currentCanvas.width+x) * 4) + 3] = data[((y*currentCanvas.width+x) * 4) + 3] - 1;
				});
			});

			context.putImageData(mainMap, 0, 0);
		}

		//Draw food locations
		_.each(food.locations, function(el) {
			setPixelChannel(mainMap, el.x, el.y, 1, 255, 255); //Paint ant G
		});

		//DRAW DEBUG LOCATIONS
		_.each(DEBUG_DRAW.locations, function(el) {
			setPixelChannel(mainMap, el.x, el.y, 1, 255, 255);
		});

		//Draw nest locations
		_.times(6, function(ix) {
			_.times(6, function(iy) {
				setPixelChannel(mainMap, nest.x + ix - 3, nest.y + iy - 3, 1, 255, 255);
			});
		});

		_.each(ants, function(ant) {
			var diffX,
				diffY;

			if (ant.foodFound) {
				setPixelChannel(mainMap, ant.x, ant.y, 0, 255, 255, true); //Paint food trail
			} else {
				setPixelChannel(mainMap, ant.x, ant.y, 2, 255, 255, true); //Paint walk trail

				_.times(3, function(px) {
					_.times(3, function(py) {
						setPixelChannel(mainMap, ant.x - ant.lastX + (px - 1), ant.y - ant.lastY + (py - 1), 2, 255, 50, true); //Paint walk trail
					});
				});

			}

			var bestPos = [],
				bestScore = -1;

			//Generate initial trail map
			var trailMap = [[0.5, 0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5]];

			//Blue score
			trailMap = _.map(trailMap, function(arr, x) {
				return _.map(arr, function(val, y) {
					var blueScore = getTrailStrength(2, ant.x + (x - 1), ant.y + (y - 1)); //Get blue trail strength

					//if (blueScore) return 0;

					blueScore = Math.abs(blueScore - 1);

					return blueScore * val;
				});
			});

			//trailMap[ant.lastX + 1][ant.lastY + 1] = (trailMap[ant.lastX + 1][ant.lastY + 1] + 1) / 2;

			console.log(trailMap);

			//Take out currentScore calculation
			_.times(3, function (tx) {
				return _.times(3, function(ty) {
					var currentScore = trailMap[tx][ty];

					if (currentScore == bestScore) {
						bestPos.push({ x: (tx - 1), y: (ty - 1) });
					} else if (currentScore > bestScore) {
						bestScore = currentScore;
						bestPos = [{ x: (tx - 1), y: (ty - 1) }];
					}
				});
			});

			console.log(trailMap);

			//console.log(bestPos);

			bestPos = _.sample(bestPos);

			/*//Get the best match by probability
			var getNextLocation = function () {
				var testX = _.random(2),
					testY = _.random(2);
				if (_.random(1, true) < trailMap[testX][testY]) {
					return {x: testX - 1, y: testY - 1};
				} else {
					return getNextLocation();
				}
			}
			var nextPos = getNextLocation();*/

			//console.log(nextPos);

			ant.lastX = bestPos.x;
			ant.lastY = bestPos.y;
			ant.x += bestPos.x;
			ant.y += bestPos.y;

			//Bounce ant on canvas limits
			if ((ant.x <= 0) || (ant.x >= (width - 1)) || (ant.y <= 0) || (ant.y >= (height - 1))) {
				bounceAnt();
			}

			function insideRange(a, b, range) {
				if (Math.abs(a - b) < range) return true;
				return false;
			}

			//Make a 180 degrees turn
			function bounceAnt() {
				ant.lastX = -ant.lastX;
				ant.lastY = -ant.lastY;
				ant.x += ant.lastX;
				ant.y += ant.lastY;
			}

			_.each(food.locations, function(food) {
				if ((insideRange(food.x, ant.x, 2)) && (insideRange(food.y, ant.y, 2)) && (ant.foodFound == false)) {
					console.log("foodFound");

					ant.foodFound = true; //Take food
					bounceAnt();
					console.log(food);
				}
			});

			//Check if ant is carrying food and is near the base
			if (ant.foodFound) {
				if (insideRange(nest.x, ant.x, 6) && insideRange(nest.y, ant.y, 6)) {
					console.log("nest");

					ant.foodFound = false; //Drop food
					bounceAnt();
					foodStored++;
					document.getElementsByClassName('foodStored')[0].innerHTML = foodStored; //Update food counter
				}
			}
		});

		setTimeout(function() {
			processWorld();
		}, 50);
	}

	init();
/*})();*/
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

/*
    This package contains general helpers that will be used everywhere else
*/

    var
        _arrayEachWithResult = function (array, memberCallback, defaultResult) {
            var
                result,
                len = array.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                result = memberCallback.apply(this, [idx, array[idx]]);
                if (undefined !== result) {
                    return result;
                }
            }
            return defaultResult;
        },

        _arrayEach = function (array, memberCallback) {
            var
                len = array.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                memberCallback.apply(this, [idx, array[idx]]);
            }
        },

        _dictionaryEachWithResult = function (dictionary, memberCallback,
            defaultResult) {
            var
                result,
                member;
            for (member in dictionary) {
                if (dictionary.hasOwnProperty(member)) {
                    result = memberCallback.apply(this, [member,
                        dictionary[member]]);
                    if (undefined !== result) {
                        return result;
                    }
                }
            }
            return defaultResult;
        },

        _dictionaryEach = function (dictionary, memberCallback) {
            var
                member;
            for (member in dictionary) {
                if (dictionary.hasOwnProperty(member)) {
                    memberCallback.apply(this, [member, dictionary[member]]);
                }
            }
        },

        _assign = function (member, value) {
            // this = gpf.extend's arguments
            // this[0] is dictionary
            this[0][member] = value;
        },

        _assignOrCall = function (member, value) {
            // this = gpf.extend's arguments
            var
                dictionary = this[0],
                overwriteCallback = this[2];
            // TODO: see if in is faster
            if (undefined !== dictionary[member]) {
                overwriteCallback(dictionary, member, value);
            } else {
                dictionary[member] = value;
            }
        },

        _likeSearchInDone = function /*gpf:inline*/ (array, a, b) {
            var
                idx,
                ia,
                ib,
                len = array.length;
            for (idx = 0; idx < len; ++idx) {
                ia = array[idx].a;
                ib = array[idx].b;
                if ((ia === a && ib === b) || (ib === a && ia === b)) {
                    return idx;
                }
            }
            return undefined;
        },

        _likeTypes = function /*gpf:inline*/ (a, b, alike) {
            if (alike && ("object" === typeof a || "object" === typeof b)) {
                /*
                 One of the two is an object but not the other,
                 Consider downcasting Number and String
                 */
                if (a instanceof String || b instanceof String) {
                    return a.toString() ===  b.toString();
                }
                if (a instanceof Number || b instanceof Number) {
                    return a.valueOf() ===  b.valueOf();
                }
                return false;
            }
            return false;
        },

        _likeCompareMembers = function /*gpf:inline*/ (ma, mb, alike, stacks) {
            if (ma !== mb) {
                if (typeof ma !== typeof mb && !_likeTypes(ma, mb, alike)) {
                    return false;
                }
                if (null === ma || null === mb
                    || "object" !== typeof ma) {
                    return false; // Because we know that ma !== mb
                }
                if (undefined === _likeSearchInDone(stacks.done, ma, mb)) {
                    stacks.todo.push(ma);
                    stacks.todo.push(mb);
                }
            }
            return true;
        },

        _likeMembers = function /*gpf:inline*/ (a, b, alike) {
            var
                member,
                count,
                stacks = {
                    todo: [a, b],
                    done: []
                };
            while (0 !== stacks.todo.length) {
                b = stacks.todo.pop();
                a = stacks.todo.pop();
                if (a.prototype !== b.prototype) {
                    return false;
                }
                stacks.done.push({a: a, b: b });
                count = 0;
                for (member in a) {
                    if (a.hasOwnProperty(member)) {
                        ++count;
                        if (!_likeCompareMembers(a[member], b[member], alike,
                            stacks)) {
                            return false;
                        }
                    }
                }
                for (member in b) {
                    if (b.hasOwnProperty(member)) {
                        --count;
                    }
                }
                if (0 !== count) {
                    return false;
                }
            }
            return true;
        },

        _values = {
            boolean: function (value, valueType, defaultValue) {
                if ("string" === valueType) {
                    if ("yes" === value || "true" === value) {
                        return true;
                    }
                    return 0 !== parseInt(value, 10);
                }
                if ("number" === valueType) {
                    return 0 !== value;
                }
                return defaultValue;
            },

            number:  function (value, valueType, defaultValue) {
                if ("string" === valueType) {
                    return parseFloat(value);
                }
                return defaultValue;
            },

            string: function (value, valueType, defaultValue) {
                gpf.interfaces.ignoreParameter(valueType);
                gpf.interfaces.ignoreParameter(defaultValue);
                if (value instanceof Date) {
                    return gpf.dateToComparableFormat(value);
                }
                return value.toString();
            },

            object: function (value, valueType, defaultValue) {
                if (defaultValue instanceof Date && "string" === valueType) {
                    return gpf.dateFromComparableFormat(value);
                }
                return defaultValue;
            }
        },

        // https://github.com/jshint/jshint/issues/525
        Func = Function; // avoid JSHint error

    /*jshint unused: false */ // Because of arguments
    /*
     * Enumerate dictionary members and call memberCallback for each of them.
     * If defaultResult is defined, memberCallback may return a result.
     * If memberCallback returns anything, the function stops and returns it.
     * Otherwise, the defaultResult is returned.
     * When defaultResult is not defined, memberCallback result is ignored.
     *
     * @param {Object|Array} dictionary
     * @param {Function} memberCallback
     * @param {*} defaultResult
     * @return {*}
     * @chainable
     * @forwardThis
     */
    gpf.each = function (dictionary, memberCallback, defaultResult) {
        if (undefined === defaultResult) {
            if (dictionary instanceof Array) {
                _arrayEach.apply(this, arguments);
            } else {
                _dictionaryEach.apply(this, arguments);
            }
            return undefined;
        }
        if (dictionary instanceof Array) {
            return _arrayEachWithResult.apply(this, arguments);
        }
        return _dictionaryEachWithResult.apply(this, arguments);
    };
    /*jshint unused: true */

    /*
     * Appends members of additionalProperties to the dictionary object.
     * If a conflict has to be handled (i.e. member exists on both objects),
     * the overwriteCallback has to handle it.
     *
     * @param {Object} dictionary
     * @param {Object} additionalProperties
     * @param {Function} overwriteCallback
     * @return {Object} the modified dictionary
     * @chainable
     */
    gpf.extend = function (dictionary, additionalProperties,
        overwriteCallback) {
        var callbackToUse;
        if (undefined === overwriteCallback) {
            callbackToUse = _assign;
        } else {
            callbackToUse = _assignOrCall;
        }
        gpf.each.apply(arguments, [additionalProperties, callbackToUse]);
        return dictionary;
    };

    gpf.extend(gpf, {

        _alpha: "abcdefghijklmnopqrstuvwxyz",
        _ALPHA: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",

        _func: function (source) {
            return new Func(source);
        },

        /*
         * Converts the provided value to match the expectedType.
         * If not specified or impossible to do so, defaultValue is returned.
         * When expectedType is not provided, it is deduced from defaultValue.
         *
         * @param {*} value
         * @param {*} default value
         * @param {String} [expectedType=typeof defaultValue] expected type
         * @return {*}
         */
        value: function (value, defaultValue, expectedType) {
            var valueType = typeof value;
            if (!expectedType) {
                expectedType = typeof defaultValue;
            }
            if (expectedType === valueType) {
                return value;
            }
            if ("undefined" === valueType || !value) {
                return defaultValue;
            }
            return _values[expectedType](value, valueType, defaultValue);
        },

        /*
         * Compares a and b and return true if they are look-alike (all members
         * have the same type and same value).
         *
         * NOTES:
         * 14/04/2013 17:19:43
         * Generates too much recursion, changed the algorithm to avoid
         * recursion using document.body (and any kind of object that references
         * other objects) I found that it was necessary to keep track of already
         * processed objects.
         *
         * @param {*} a
         * @param {*} b
         * @param {Boolean} [alike=false] alike Allow to be tolerant on
         *        primitive types compared with their object equivalent
         * @return {Boolean}
         */
        like: function (a, b, alike) {
            if (a === b) {
                return true;
            }
            if (typeof a !== typeof b) {
                return _likeTypes(a, b, alike);
            }
            if (null === a || null === b || "object" !== typeof a) {
                return false;
            }
            return _likeMembers(a, b, alike);
        },

        /**
         * Shallow copy an object
         *
         * @param {Object} obj
         * @return {Object}
         */
        clone: function (obj) {
            /*
             * http://stackoverflow.com/questions/122102/what-is-the-most-
             * efficient-way-to-clone-an-object/5344074#5344074
             */
            return gpf.json.parse(gpf.json.stringify(obj));
        },

        /*
         * Find the first member of dictionary which value equals to value.
         *
         * @param {Object/array} dictionary
         * @param {*} value
         * @return {String/number/undefined} undefined if not found
         */
        test: function (dictionary, value) {
            var idx;
            if (dictionary instanceof Array) {
                idx = dictionary.length;
                while (idx > 0) {
                    if (dictionary[--idx] === value) {
                        return idx;
                    }
                }
            } else {
                for (idx in dictionary) {
                    if (dictionary.hasOwnProperty(idx)
                        && dictionary[idx] === value) {
                        return idx;
                    }
                }
            }
            return undefined;
        },

        /*
         * Inserts the value in the array if not already present.
         *
         * @param {Array} array
         * @param {*} value
         * @return {Array}
         * @chainable
         */
        set: function (array, value) {
            gpf.ASSERT(array instanceof Array,
                "gpf.set must be used with an Array");
            var idx = array.length;
            while (idx > 0) {
                if (array[--idx] === value) {
                    return array; // Already set
                }
            }
            array.push(value);
            return array;
        },

        /*
         * Removes the member of 'dictionary' which value equals 'value'.
         * NOTE: that the object is modified.
         *
         * @param {Object/array} dictionary
         * @param {*} value
         * @return {Object/array} dictionary
         * @chainable
         */
        clear: function (dictionary, value) {
            var idx;
            if (dictionary instanceof Array) {
                idx = dictionary.length;
                while (idx > 0) {
                    if (dictionary[--idx] === value) {
                        dictionary.splice(idx, 1);
                        break;
                    }
                }
            } else {
                for (idx in dictionary) {
                    if (dictionary.hasOwnProperty(idx)
                        && dictionary[idx] === value) {
                        break;
                    }
                }
            }
            return dictionary;
        },

        /**
         * XOR
         *
         * @param {Boolean} a
         * @param {Boolean} b
         */
        xor: function (a, b) {
            return a && !b || !a && b;
        },

        /**
         * Capitalize the string
         *
         * @param {String} that
         * @return {String}
         */
        capitalize: function (that) {
            return that.charAt(0).toUpperCase() + that.substr(1);
        },

        /**
         * Generic callback handler
         *
         * @param {Function} handler
         * @param {Object} scope
         * @constructor
         * @class gpf.Callback
         */
        Callback: function (handler, scope) {
            if (handler) {
                this._handler = handler;
            }
            if (scope) {
                this._scope = scope;
            }
        }

    });

    gpf.extend(gpf.Callback.prototype, {
        _handler: gpf._func(""),
        _scope: null,

        /**
         * Get the handler function
         *
         * @returns {Function}
         */
        handler: function () {
            return this._handler;
        },

        /**
         * Get the scope
         *
         * @returns {Object}
         */
        scope: function () {
            return this._scope;
        },

        /**
         * Executes the callback and override the scope if not defined
         *
         * @param {Object} scope Scope to apply if none set in the callback
         * @param {*} ... Forwarded to the callback handler
         * @returns {*}
         */
        call: function() {
            var
                scope = arguments[0],
                args = [],
                len = arguments.length,
                idx;
            for (idx = 1; idx < len; ++idx) {
                args.push(arguments[idx]);
            }
            return this.apply(scope, args);
        },

        /**
         * Executes the callback and override the scope if not defined
         *
         * @param {Object} scope Scope to apply if none set in the callback
         * @param {*[]} args Forwarded to the callback handler
         * @returns {*}
         */
        apply: function(scope, args) {
            var finalScope = gpf.Callback.resolveScope(this._scope || scope);
            return this._handler.apply(finalScope, args);
        }
    });

    /**
     * Resolve function scope
     *
     * @static
     */
    gpf.Callback.resolveScope = function (scope) {
        if (!scope) {
            scope = gpf.context();
        }
        return scope;
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<commit_msg>Documented error (DEBUG mode only)<commit_after>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

/*
    This package contains general helpers that will be used everywhere else
*/

    var
        _arrayEachWithResult = function (array, memberCallback, defaultResult) {
            var
                result,
                len = array.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                result = memberCallback.apply(this, [idx, array[idx]]);
                if (undefined !== result) {
                    return result;
                }
            }
            return defaultResult;
        },

        _arrayEach = function (array, memberCallback) {
            var
                len = array.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                memberCallback.apply(this, [idx, array[idx]]);
            }
        },

        _dictionaryEachWithResult = function (dictionary, memberCallback,
            defaultResult) {
            var
                result,
                member;
            for (member in dictionary) {
                if (dictionary.hasOwnProperty(member)) {
                    result = memberCallback.apply(this, [member,
                        dictionary[member]]);
                    if (undefined !== result) {
                        return result;
                    }
                }
            }
            return defaultResult;
        },

        _dictionaryEach = function (dictionary, memberCallback) {
            var
                member;
            for (member in dictionary) {
                if (dictionary.hasOwnProperty(member)) {
                    memberCallback.apply(this, [member, dictionary[member]]);
                }
            }
        },

        _assign = function (member, value) {
            // this = gpf.extend's arguments
            // this[0] is dictionary
            this[0][member] = value;
        },

        _assignOrCall = function (member, value) {
            // this = gpf.extend's arguments
            var
                dictionary = this[0],
                overwriteCallback = this[2];
            // TODO: see if in is faster
            if (undefined !== dictionary[member]) {
                overwriteCallback(dictionary, member, value);
            } else {
                dictionary[member] = value;
            }
        },

        _likeSearchInDone = function /*gpf:inline*/ (array, a, b) {
            var
                idx,
                ia,
                ib,
                len = array.length;
            for (idx = 0; idx < len; ++idx) {
                ia = array[idx].a;
                ib = array[idx].b;
                if ((ia === a && ib === b) || (ib === a && ia === b)) {
                    return idx;
                }
            }
            return undefined;
        },

        _likeTypes = function /*gpf:inline*/ (a, b, alike) {
            if (alike && ("object" === typeof a || "object" === typeof b)) {
                /*
                 One of the two is an object but not the other,
                 Consider downcasting Number and String
                 */
                if (a instanceof String || b instanceof String) {
                    return a.toString() ===  b.toString();
                }
                if (a instanceof Number || b instanceof Number) {
                    return a.valueOf() ===  b.valueOf();
                }
                return false;
            }
            return false;
        },

        _likeCompareMembers = function /*gpf:inline*/ (ma, mb, alike, stacks) {
            if (ma !== mb) {
                if (typeof ma !== typeof mb && !_likeTypes(ma, mb, alike)) {
                    return false;
                }
                if (null === ma || null === mb
                    || "object" !== typeof ma) {
                    return false; // Because we know that ma !== mb
                }
                if (undefined === _likeSearchInDone(stacks.done, ma, mb)) {
                    stacks.todo.push(ma);
                    stacks.todo.push(mb);
                }
            }
            return true;
        },

        _likeMembers = function /*gpf:inline*/ (a, b, alike) {
            var
                member,
                count,
                stacks = {
                    todo: [a, b],
                    done: []
                };
            while (0 !== stacks.todo.length) {
                b = stacks.todo.pop();
                a = stacks.todo.pop();
                if (a.prototype !== b.prototype) {
                    return false;
                }
                stacks.done.push({a: a, b: b });
                count = 0;
                for (member in a) {
                    if (a.hasOwnProperty(member)) {
                        ++count;
                        if (!_likeCompareMembers(a[member], b[member], alike,
                            stacks)) {
                            return false;
                        }
                    }
                }
                for (member in b) {
                    if (b.hasOwnProperty(member)) {
                        --count;
                    }
                }
                if (0 !== count) {
                    return false;
                }
            }
            return true;
        },

        _values = {
            boolean: function (value, valueType, defaultValue) {
                if ("string" === valueType) {
                    if ("yes" === value || "true" === value) {
                        return true;
                    }
                    return 0 !== parseInt(value, 10);
                }
                if ("number" === valueType) {
                    return 0 !== value;
                }
                return defaultValue;
            },

            number:  function (value, valueType, defaultValue) {
                if ("string" === valueType) {
                    return parseFloat(value);
                }
                return defaultValue;
            },

            string: function (value, valueType, defaultValue) {
                gpf.interfaces.ignoreParameter(valueType);
                gpf.interfaces.ignoreParameter(defaultValue);
                if (value instanceof Date) {
                    return gpf.dateToComparableFormat(value);
                }
                return value.toString();
            },

            object: function (value, valueType, defaultValue) {
                if (defaultValue instanceof Date && "string" === valueType) {
                    return gpf.dateFromComparableFormat(value);
                }
                return defaultValue;
            }
        },

        // https://github.com/jshint/jshint/issues/525
        Func = Function; // avoid JSHint error

    /*jshint unused: false */ // Because of arguments
    /*
     * Enumerate dictionary members and call memberCallback for each of them.
     * If defaultResult is defined, memberCallback may return a result.
     * If memberCallback returns anything, the function stops and returns it.
     * Otherwise, the defaultResult is returned.
     * When defaultResult is not defined, memberCallback result is ignored.
     *
     * @param {Object|Array} dictionary
     * @param {Function} memberCallback
     * @param {*} defaultResult
     * @return {*}
     * @chainable
     * @forwardThis
     */
    gpf.each = function (dictionary, memberCallback, defaultResult) {
        if (undefined === defaultResult) {
            if (dictionary instanceof Array) {
                _arrayEach.apply(this, arguments);
            } else {
                _dictionaryEach.apply(this, arguments);
            }
            return undefined;
        }
        if (dictionary instanceof Array) {
            return _arrayEachWithResult.apply(this, arguments);
        }
        return _dictionaryEachWithResult.apply(this, arguments);
    };
    /*jshint unused: true */

    /*
     * Appends members of additionalProperties to the dictionary object.
     * If a conflict has to be handled (i.e. member exists on both objects),
     * the overwriteCallback has to handle it.
     *
     * @param {Object} dictionary
     * @param {Object} additionalProperties
     * @param {Function} overwriteCallback
     * @return {Object} the modified dictionary
     * @chainable
     */
    gpf.extend = function (dictionary, additionalProperties,
        overwriteCallback) {
        var callbackToUse;
        if (undefined === overwriteCallback) {
            callbackToUse = _assign;
        } else {
            callbackToUse = _assignOrCall;
        }
        gpf.each.apply(arguments, [additionalProperties, callbackToUse]);
        return dictionary;
    };

    gpf.extend(gpf, {

        _alpha: "abcdefghijklmnopqrstuvwxyz",
        _ALPHA: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",

        _func: function (source) {
/*#ifdef(DEBUG)*/
            try {
/*#endif*/
                return new Func(source);
/*#ifdef(DEBUG)*/
            } catch (e) {
                console.error('An exception occurred compiling:\r\n'
                    + source);
                return null;
            }
/*#endif*/
        },

        /*
         * Converts the provided value to match the expectedType.
         * If not specified or impossible to do so, defaultValue is returned.
         * When expectedType is not provided, it is deduced from defaultValue.
         *
         * @param {*} value
         * @param {*} default value
         * @param {String} [expectedType=typeof defaultValue] expected type
         * @return {*}
         */
        value: function (value, defaultValue, expectedType) {
            var valueType = typeof value;
            if (!expectedType) {
                expectedType = typeof defaultValue;
            }
            if (expectedType === valueType) {
                return value;
            }
            if ("undefined" === valueType || !value) {
                return defaultValue;
            }
            return _values[expectedType](value, valueType, defaultValue);
        },

        /*
         * Compares a and b and return true if they are look-alike (all members
         * have the same type and same value).
         *
         * NOTES:
         * 14/04/2013 17:19:43
         * Generates too much recursion, changed the algorithm to avoid
         * recursion using document.body (and any kind of object that references
         * other objects) I found that it was necessary to keep track of already
         * processed objects.
         *
         * @param {*} a
         * @param {*} b
         * @param {Boolean} [alike=false] alike Allow to be tolerant on
         *        primitive types compared with their object equivalent
         * @return {Boolean}
         */
        like: function (a, b, alike) {
            if (a === b) {
                return true;
            }
            if (typeof a !== typeof b) {
                return _likeTypes(a, b, alike);
            }
            if (null === a || null === b || "object" !== typeof a) {
                return false;
            }
            return _likeMembers(a, b, alike);
        },

        /**
         * Shallow copy an object
         *
         * @param {Object} obj
         * @return {Object}
         */
        clone: function (obj) {
            /*
             * http://stackoverflow.com/questions/122102/what-is-the-most-
             * efficient-way-to-clone-an-object/5344074#5344074
             */
            return gpf.json.parse(gpf.json.stringify(obj));
        },

        /*
         * Find the first member of dictionary which value equals to value.
         *
         * @param {Object/array} dictionary
         * @param {*} value
         * @return {String/number/undefined} undefined if not found
         */
        test: function (dictionary, value) {
            var idx;
            if (dictionary instanceof Array) {
                idx = dictionary.length;
                while (idx > 0) {
                    if (dictionary[--idx] === value) {
                        return idx;
                    }
                }
            } else {
                for (idx in dictionary) {
                    if (dictionary.hasOwnProperty(idx)
                        && dictionary[idx] === value) {
                        return idx;
                    }
                }
            }
            return undefined;
        },

        /*
         * Inserts the value in the array if not already present.
         *
         * @param {Array} array
         * @param {*} value
         * @return {Array}
         * @chainable
         */
        set: function (array, value) {
            gpf.ASSERT(array instanceof Array,
                "gpf.set must be used with an Array");
            var idx = array.length;
            while (idx > 0) {
                if (array[--idx] === value) {
                    return array; // Already set
                }
            }
            array.push(value);
            return array;
        },

        /*
         * Removes the member of 'dictionary' which value equals 'value'.
         * NOTE: that the object is modified.
         *
         * @param {Object/array} dictionary
         * @param {*} value
         * @return {Object/array} dictionary
         * @chainable
         */
        clear: function (dictionary, value) {
            var idx;
            if (dictionary instanceof Array) {
                idx = dictionary.length;
                while (idx > 0) {
                    if (dictionary[--idx] === value) {
                        dictionary.splice(idx, 1);
                        break;
                    }
                }
            } else {
                for (idx in dictionary) {
                    if (dictionary.hasOwnProperty(idx)
                        && dictionary[idx] === value) {
                        break;
                    }
                }
            }
            return dictionary;
        },

        /**
         * XOR
         *
         * @param {Boolean} a
         * @param {Boolean} b
         */
        xor: function (a, b) {
            return a && !b || !a && b;
        },

        /**
         * Capitalize the string
         *
         * @param {String} that
         * @return {String}
         */
        capitalize: function (that) {
            return that.charAt(0).toUpperCase() + that.substr(1);
        },

        /**
         * Generic callback handler
         *
         * @param {Function} handler
         * @param {Object} scope
         * @constructor
         * @class gpf.Callback
         */
        Callback: function (handler, scope) {
            if (handler) {
                this._handler = handler;
            }
            if (scope) {
                this._scope = scope;
            }
        }

    });

    gpf.extend(gpf.Callback.prototype, {
        _handler: gpf._func(""),
        _scope: null,

        /**
         * Get the handler function
         *
         * @returns {Function}
         */
        handler: function () {
            return this._handler;
        },

        /**
         * Get the scope
         *
         * @returns {Object}
         */
        scope: function () {
            return this._scope;
        },

        /**
         * Executes the callback and override the scope if not defined
         *
         * @param {Object} scope Scope to apply if none set in the callback
         * @param {*} ... Forwarded to the callback handler
         * @returns {*}
         */
        call: function() {
            var
                scope = arguments[0],
                args = [],
                len = arguments.length,
                idx;
            for (idx = 1; idx < len; ++idx) {
                args.push(arguments[idx]);
            }
            return this.apply(scope, args);
        },

        /**
         * Executes the callback and override the scope if not defined
         *
         * @param {Object} scope Scope to apply if none set in the callback
         * @param {*[]} args Forwarded to the callback handler
         * @returns {*}
         */
        apply: function(scope, args) {
            var finalScope = gpf.Callback.resolveScope(this._scope || scope);
            return this._handler.apply(finalScope, args);
        }
    });

    /**
     * Resolve function scope
     *
     * @static
     */
    gpf.Callback.resolveScope = function (scope) {
        if (!scope) {
            scope = gpf.context();
        }
        return scope;
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<|endoftext|>


<commit_before>var NativeSettings = function() {
};

NativeSettings.open = function(setting_name, onsucess, onfail) {
	cordova.exec(onsucess, onfail, "NativeSettings", setting_name, []);
};

module.exports = NativeSettings;<commit_msg>update js<commit_after>var NativeSettings = function() {
};

NativeSettings.open = function(setting_name, onsucess, onfail) {
	cordova.exec(onsucess, onfail, "NativeSettings", "open", setting_name, []);
};

module.exports = NativeSettings;<|endoftext|>