/**
* @module proact-arrays
*/
/**
* <p>
* Constructs a `ProAct.ArrayProperty`.
* A property is a simple {{#crossLink "ProAct.Actor"}}{{/crossLink}} with state.
* </p>
* <p>
* The value of `ProAct.ArrayProperty` is an array, turned to reactive ProAct.js array -
* {{#crossLink "ProAct.Array"}}{{/crossLink}}.
* </p>
* <p>
* On changing the array value to another array the listeners for indices/length are moved from the old value to the new value.
* </p>
* <p>
* If set to null or undefined, the property is re-defined, using
* {{#crossLink "ProAct.Property/reProb:method"}}{{/crossLink}}.
* </p>
* <p>
* `ProAct.ArrayProperty` is lazy - its object is made reactive on the first read of the property.
* Its state is set to {{#crossLink "ProAct.States/ready:property"}}{{/crossLink}} on the first read too.
* </p>
* <p>
* `ProAct.ArrayProperty` is part of the proact-arrays module of ProAct.js.
* </p>
*
* @class ProAct.ArrayProperty
* @extends ProAct.Property
* @constructor
* @param {String} queueName
* The name of the queue all the updates should be pushed to.
* <p>
* If this parameter is null/undefined the default queue of
* {{#crossLink "ProAct/flow:property"}}{{/crossLink}} is used.
* </p>
* <p>
* If this parameter is not a string it is used as the
* <i>proObject</i>.
* </p>
* @param {Object} proObject
* A plain JavaScript object, holding a field, this property will represent.
* @param {String} property
* The name of the field of the object, this property should represent.
*/
function ArrayProperty (queueName, proObject, property) {
if (queueName && !P.U.isString(queueName)) {
property = proObject;
proObject = queueName;
queueName = null;
}
var self = this, getter;
getter = function () {
var isPA = P.AU.isProArray;
self.addCaller();
if (!isPA(self.val)) {
self.val = new P.A(self.val);
}
var get = P.P.defaultGetter(self),
set = function (newVal) {
if (self.val == newVal || self.val.valueOf() == newVal) {
return;
}
self.oldVal = self.val;
self.val = newVal;
if (self.val === null || self.val === undefined) {
P.P.reProb(self).update();
return self;
}
if (!isPA(self.val)) {
self.val = new P.A(self.val);
if (queueName) {
self.val.core.queueName = queueName;
}
}
if (self.oldVal) {
var i, listener,
toRemove = [], toRemoveLength,
oldIndListeners = self.oldVal.__pro__.listeners.index,
oldIndListenersLn = oldIndListeners.length,
newIndListeners = self.val.__pro__.listeners.index,
oldLenListeners = self.oldVal.__pro__.listeners.length,
oldLenListenersLn = oldLenListeners.length,
newLenListeners = self.val.__pro__.listeners.length;
for (i = 0; i < oldIndListenersLn; i++) {
listener = oldIndListeners[i];
if (listener.property && listener.property.proObject === self.proObject) {
newIndListeners.push(listener);
toRemove.push(i);
}
}
toRemoveLength = toRemove.length;
for (i = 0; i < toRemoveLength; i++) {
oldIndListeners.splice[toRemove[i], 1];
}
toRemove = [];
for (i = 0; i < oldLenListenersLn; i++) {
listener = oldLenListeners[i];
if (listener.property && listener.property.proObject === self.proObject) {
newLenListeners.push(listener);
toRemove.push(i);
}
}
toRemoveLength = toRemove.length;
for (i = 0; i < toRemoveLength; i++) {
oldLenListeners.splice[toRemove[i], 1];
}
toRemove = [];
}
ActorUtil.update.call(self);
};
P.P.defineProp(self.proObject, self.property, get, set);
self.state = P.States.ready;
return self.val;
};
P.P.call(this, queueName, proObject, property, getter, function () {});
}
ProAct.ArrayProperty = P.AP = ArrayProperty;
ProAct.ArrayProperty.prototype = P.U.ex(Object.create(P.P.prototype), {
/**
* Reference to the constructor of this object.
*
* @property constructor
* @type ProAct.ArrayProperty
* @final
* @for ProAct.ArrayProperty
*/
constructor: ProAct.ArrayProperty,
/**
* Retrieves the {{#crossLink "ProAct.Property.Types"}}{{/crossLink}} value of <i>this</i> property.
* <p>
* For instances of the `ProAct.ArrayProperty` class, it is
* {{#crossLink "ProAct.Property.Types/array:property"}}{{/crossLink}}.
* </p>
*
* @for ProAct.ArrayProperty
* @instance
* @method type
* @return {Number}
* The right type of the property.
*/
type: function () {
return P.P.Types.array;
},
/**
* Called automatically after initialization of this property.
* <p>
* For `ProAct.ArrayProperty` it does nothing -
* the real initialization is lazy and is performed on the first read of <i>this</i>.
* </p>
*
* @for ProAct.ArrayProperty
* @protected
* @instance
* @method afterInit
*/
afterInit: function () {}
});
/**
* <p>
* Constructor for `ProAct.ArrayPropertyProvider`.
* </p>
* <p>
* Provides {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instances for fields pointing to arrays.
* </p>
* <p>
* `ProAct.ArrayPropertyProvider` is part of the proact-properties module of ProAct.js.
* </p>
*
* @for ProAct.ArrayPropertyProvider
* @extends ProAct.PropertyProvider
* @constructor
*/
ProAct.ArrayPropertyProvider = P.APP = function () {
P.PP.call(this);
};
ProAct.ArrayPropertyProvider.prototype = P.U.ex(Object.create(P.PP.prototype), {
/**
* Reference to the constructor of this object.
*
* @property constructor
* @type ProAct.ArrayPropertyProvider
* @final
* @for ProAct.ArrayPropertyProvider
*/
constructor: ProAct.ArrayPropertyProvider,
/**
* Used to check if this `ProAct.ArrayPropertyProvider` is compliant with the field and meta data.
*
* @for ProAct.ArrayPropertyProvider
* @instance
* @method filter
* @param {Object} object
* The object to which a new {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instance should be provided.
* @param {String} property
* The field name of the <i>object</i> to turn into a {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}}.
* @param {String|Array} meta
* Meta information to be used for filtering and configuration of the {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instance to be provided.
* @return {Boolean}
* True if the value of <b>object[property]</b> an array.
*/
filter: function (object, property, meta) {
return P.AU.isArrayObject(object[property]);
},
/**
* Provides an instance of {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}}.
*
* @for ProAct.ArrayPropertyProvider
* @instance
* @method provide
* @param {String} queueName
* The name of the queue all the updates should be pushed to.
* <p>
* If this parameter is null/undefined the default queue of
* {{#crossLink "ProAct/flow:property"}}{{/crossLink}} is used.
* </p>
* @param {Object} object
* The object to which a new {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instance should be provided.
* @param {String} property
* The field of the <i>object</i> to turn into a {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}}.
* @param {String|Array} meta
* Meta information to be used for filtering and configuration of the {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instance to be provided.
* @return {ProAct.ArrayProperty}
* A {{#crossLink "ProAct.ArrayProperty"}}{{/crossLink}} instance provided by <i>this</i> provider.
*/
provide: function (queueName, object, property, meta) {
return new P.AP(queueName, object, property);
}
});
P.PP.registerProvider(new P.ArrayPropertyProvider());
var oldTypeFunction = P.P.Types.type;
P.U.ex(P.Property.Types, {
/**
* ProAct.Property for array types - fields containing arrays.
*
* @property array
* @type Number
* @final
* @for ProAct.Property.Types
*/
array: {}, // arrays
type: function (value) {
if (P.U.isArray(value)) {
return P.P.Types.array;
}
return oldTypeFunction(value);
}
});