'::hui_basemodel::';
'use strict';
// __ __ ______ ______ _____ __ __
// /\ \ /\ \ /'\_/`\ /\ _ \ /\__ _\/\ __`\ /\ \/\ \
// \ `\`\\/'//\ \\ \ \/\ \\/_/\ \/\ \ \/\ \\ \ \ \ \
// `\ `\ /' \ \ \__\ \\ \ __ \ \ \ \ \ \ \ \ \\ \ \ \ \
// `\ \ \ \ \ \_/\ \\ \ \/\ \ \ \ \ \ \ \_\ \\ \ \_\ \
// \ \_\ \ \_\\ \_\\ \_\ \_\ \ \_\ \ \_____\\ \_____\
// \/_/ \/_/ \/_/ \/_/\/_/ \/_/ \/_____/ \/_____/
//
/**
* @name @name 基础数据模型类
* @public
* @author wanghaiyang
* @date 2014/05/05
* @param {Object} options 控件初始化参数.
*/
hui.define('hui_basemodel', ['hui_eventdispatcher'], function () {
hui.BaseModel = function (data) {
hui.EventDispatcher.call(this);
var _model = {};
/**
* @name 设置新的值,如果两个值不同,就会触发PropertyChangedEvent.
* @param {String|Object} propertyName 需要设置的属性或数据对象.
* @param {Any} value 属性的值.
* @comment 接受`"key", value` 和 `{key: value}`两种的方式赋值.
*/
this.set = function (propertyName, newValue) {
var attr,
attrs,
changes = [],
newValue,
className = Object.prototype.toString.call(propertyName);
if ((className !== '[object Object]' && className !== '[object String]') ||
(className === '[object Object]' && newValue !== undefined)) {
return this.trigger('SET_ERROR', propertyName, newValue);
}
if (className == '[object String]') {
attrs = {};
attrs[propertyName] = newValue;
}
else {
attrs = propertyName;
}
for (attr in attrs) {
if (!Object.prototype.hasOwnProperty.call(_model, attr)) {
changes.push([attr, undefined, hui.BaseModel.clone(attrs[attr])]);
_model[attr] = newValue;
}
else if (!hui.BaseModel.isEqual(_model[attr], attrs[attr])) {
changes.push([attr, hui.BaseModel.clone(_model[attr]), hui.BaseModel.clone(attrs[attr])]);
_model[attr] = attrs[attr];
}
// IE6,7 can not use JSON.stringify(), just use simple compare.
else if (_model[attr] !== attrs[attr]) {
changes.push([attr, hui.BaseModel.clone(_model[attr]), hui.BaseModel.clone(attrs[attr])]);
_model[attr] = attrs[attr];
}
}
// Trigger all relevant attribute changes.
for (var i = 0, len = changes.length; i < len; i++) {
this.trigger('change:' + changes[i][0], changes[i][1], changes[i][2]);
}
if (changes.length) {
this.trigger('change');
}
};
/**
* @name 获取指定属性值
* @param {String} propertyName 属性名.
* @return {*} 属性的值.
*/
this.get = function (propertyName) {
return hui.BaseModel.clone(_model[propertyName]);
};
/**
* @name 获取所有的属性值
* @return {Map} 所有的属性值.
*/
this.getData = function () {
return hui.BaseModel.clone(_model);
};
/**
* @name 移除指定属性值
* @param {String} propertyName 属性名.
* @return {*} 属性的值.
*/
this.remove = function (propertyName) {
var value = _model[propertyName];
this.set(propertyName, undefined);
delete _model[propertyName];
return value;
};
/**
* @name 销毁Model
* @return {void}
*/
this.dispose = function () {
this._listeners = undefined;
_model = undefined;
};
var child = _model,
parent = data;
for (var key in parent) {
if (parent.hasOwnProperty(key)) {
child[key] = parent[key];
}
}
};
hui.BaseModel.isEqual = function (a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) {
return a !== 0 || 1 / a == 1 / b;
}
// A strict comparison is necessary because `null == undefined`.
if (a === null || b === null || a === undefined || b === undefined) {
return a === b;
}
if (aStack === undefined || bStack === undefined) {
aStack = [];
bStack = [];
}
// Compare `[[Class]]` names.
var className = Object.prototype.toString.call(a);
if (className != Object.prototype.toString.call(b)) {
return false;
}
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a === 0 ? 1 / a === 1 / b : a === +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0,
result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = hui.BaseModel.isEqual(a[size], b[size], aStack, bStack))) break;
}
}
}
else {
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor,
bCtor = b.constructor;
if (aCtor !== bCtor && !(Object.prototype.toString.call(aCtor) == '[object Function]' && (aCtor instanceof aCtor) &&
Object.prototype.toString.call(bCtor) == '[object Function]' && (bCtor instanceof bCtor))) {
return false;
}
// Deep compare objects.
for (var key in a) {
if (Object.prototype.hasOwnProperty.call(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = Object.prototype.hasOwnProperty.call(b, key) && hui.BaseModel.isEqual(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (Object.prototype.hasOwnProperty.call(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
hui.inherits(hui.BaseModel, hui.EventDispatcher);
/**
* @name对一个object进行深度拷贝
* @param {Any} source 需要进行拷贝的对象.
* @param {Array} oldArr 源对象树索引.
* @param {Array} newArr 目标对象树索引.
* @return {Any} 拷贝后的新对象.
*/
hui.BaseModel.clone = function (source, oldArr, newArr) {
if (typeof source === 'undefined') {
return undefined;
}
if (typeof JSON !== 'undefined') {
return JSON.parse(JSON.stringify(source));
}
var result = source,
i,
len,
j,
len2,
exist = -1;
oldArr = oldArr || [];
newArr = newArr || [];
if (source instanceof Date) {
result = new Date(source.getTime());
}
else if ((source instanceof Array) || (Object.prototype.toString.call(source) == '[object Object]')) {
for (j = 0, len2 = oldArr.length; j < len2; j++) {
if (oldArr[j] == source) {
exist = j;
break;
}
}
if (exist != -1) {
result = newArr[exist];
exist = -1;
}
else {
if (source instanceof Array) {
result = [];
oldArr.push(source);
newArr.push(result);
var resultLen = 0;
for (i = 0, len = source.length; i < len; i++) {
result[resultLen++] = hui.BaseModel.clone(source[i], oldArr, newArr);
}
}
else if (!!source && Object.prototype.toString.call(source) == '[object Object]') {
result = {};
oldArr.push(source);
newArr.push(result);
for (i in source) {
if (source.hasOwnProperty(i)) {
result[i] = hui.BaseModel.clone(source[i], oldArr, newArr);
}
}
}
}
}
return result;
};
});