-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathObservableProperty.js
More file actions
129 lines (111 loc) · 3.86 KB
/
Copy pathObservableProperty.js
File metadata and controls
129 lines (111 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
var ObservableProperty = function (options) {
'use strict';
if (typeof options === 'undefined') {
throw new Error("ObservableProperty options is undefined");
}
if (typeof options.name === 'undefined') {
throw new Error("ObservableProperty options.name is undefined");
}
this.name = options.name;
this.value = options.value || null;
this.originalValue = options.value || null;
this.previousValue = options.value || null;
this.beforeChange = options.beforeChange || [];
this.afterChange = options.change || [];
this.dirty = false;
/**
* set the value of this property
* @method
* @return boolean true if value was updated
**/
this.set = function (newValue) {
if (!this.compare(newValue, this.value)) {
this._handleBeforeChange();
this.previousValue = this.value;
this.value = newValue;
this.dirty = true;
this._handleAfterChange();
return true;
}
return false;
};
/**
* get the value of this property
* @method
* @return {*}
**/
this.get = function() {
return this.value;
};
/**
* execute afterChange handlers
* @private
**/
this._handleAfterChange = function () {
for (var i = 0, l = this.afterChange.length; i < l; i++) {
this.afterChange[i](this);
}
}
/**
* execute beforeChange handlers
* @private
**/
this._handleBeforeChange = function () {
for (var i = 0, l = this.beforeChange.length; i < l; i++) {
this.beforeChange[i](this);
}
}
/**
* bind a change handler
* @type {string} type of handler
* @fn {fn} handler
**/
this.bind = function (type, fn) {
switch (type) {
case "before":
this.beforeChange.push(fn);
break;
case "after":
case "change":
this.afterChange.push(fn);
break;
default:
throw new Error("Unknown binding");
break;
}
}
/**
* @see http://stackoverflow.com/questions/1068834/object-comparison-in-javascript
* @author http://stackoverflow.com/users/228259/jean-vincent
* @param {*} object to compare to
* @return {boolean} true if the newValue matches this.value
**/
this.compare = function (newValue) {
var x = this.value;
var y = newValue;
if ( x === y ) return true;
// if both x and y are null or undefined and exactly the same
if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;
// if they are not strictly equal, they both need to be Objects
if ( x.constructor !== y.constructor ) return false;
// they must have the exact same prototype chain, the closest we can do is
// test there constructor.
for ( var p in x ) {
if ( ! x.hasOwnProperty( p ) ) continue;
// other properties were tested using x.constructor === y.constructor
if ( ! y.hasOwnProperty( p ) ) return false;
// allows to compare x[ p ] and y[ p ] when set to undefined
if ( x[ p ] === y[ p ] ) continue;
// if they have the same strict value or identity then they are equal
if ( typeof( x[ p ] ) !== "object" ) return false;
// Numbers, Strings, Functions, Booleans must be strictly equal
if ( ! Object.equals( x[ p ], y[ p ] ) ) return false;
// Objects and Arrays must be tested recursively
}
for ( p in y ) {
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) ) return false;
// allows x[ p ] to be set to undefined
}
return true;
}
};