aboutsummaryrefslogtreecommitdiff
path: root/node_modules/montage/core/extras/object.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/montage/core/extras/object.js')
-rw-r--r--node_modules/montage/core/extras/object.js256
1 files changed, 256 insertions, 0 deletions
diff --git a/node_modules/montage/core/extras/object.js b/node_modules/montage/core/extras/object.js
new file mode 100644
index 00000000..0281240f
--- /dev/null
+++ b/node_modules/montage/core/extras/object.js
@@ -0,0 +1,256 @@
1
2var M = require("core/core"); // lazy bound because of dependency cycle
3
4var MODIFY = "modify";
5
6/**
7@function external:Object#getProperty
8@param {Object} aPropertyPath
9@param {Property} unique
10@param {Property} preserve
11@param {Function} visitedComponentCallback
12@param {Array} currentIndex
13@returns result
14*/
15Object.defineProperty(Object.prototype, "getProperty", {
16 value: function(aPropertyPath, unique, preserve, visitedComponentCallback, currentIndex) {
17 var dotIndex,
18 result,
19 currentPathComponent,
20 nextDotIndex,
21 remainingPath = null;
22
23 if (aPropertyPath == null) {
24 return;
25 }
26
27 dotIndex = aPropertyPath.indexOf(".", currentIndex);
28 currentIndex = currentIndex || 0;
29 currentPathComponent = aPropertyPath.substring(currentIndex, (dotIndex === -1 ? aPropertyPath.length : dotIndex));
30
31 if (currentPathComponent in this) {
32 result = this[currentPathComponent];
33 } else {
34 result = typeof this.undefinedGet === "function" ? this.undefinedGet(currentPathComponent) : undefined;
35 }
36
37 if (visitedComponentCallback) {
38 nextDotIndex = aPropertyPath.indexOf(".", currentIndex);
39 if (nextDotIndex != -1) {
40 remainingPath = aPropertyPath.substr(nextDotIndex+1);
41 }
42 visitedComponentCallback(this, currentPathComponent, result, null, remainingPath);
43 }
44
45 if (visitedComponentCallback && result && -1 === dotIndex) {
46
47 // We resolved the last object on the propertyPath, be sure to give the visitor a chance to handle this one
48 //visitedComponentCallback(result, null, null, null, null);
49
50 } else if (result && dotIndex !== -1) {
51 // We resolved that component of the path, but there's more path components; go to the next
52
53 if (result.getProperty) {
54 result = result.getProperty(aPropertyPath, unique, preserve, visitedComponentCallback, dotIndex + 1);
55 } else {
56 // TODO track when this happens, right now it's only happening with CanvasPixelArray in WebKit
57 result = Object.prototype.getProperty.call(result, aPropertyPath, unique, preserve, visitedComponentCallback, dotIndex + 1);
58 }
59 }
60 // Otherwise, we reached the end of the propertyPath, or at least as far as we could; stop
61 return result;
62 },
63 enumerable: false
64});
65
66/**
67 @private
68*/
69Object.defineProperty(Object.prototype, "_propertySetterNamesByName", {
70 value: {}
71});
72
73/**
74 @private
75*/
76Object.defineProperty(Object.prototype, "_propertySetterByName", {
77 value: {}
78});
79
80/**
81Description
82@member external:Object#setProperty
83@function
84@param {Object} aPropertyPath
85@param {Object} value
86@returns itself
87*/
88Object.defineProperty(Object.prototype, "setProperty", {
89 value: function(aPropertyPath, value) {
90 var propertyIsNumber = !isNaN(aPropertyPath),
91 lastDotIndex = propertyIsNumber ? -1 : aPropertyPath.lastIndexOf("."),
92 setObject,
93 lastObjectAtPath,
94 propertyToSetOnArray;
95
96 if (lastDotIndex !== -1) {
97 //The propertyPath describes a property that is deeper inside this object
98 setObject = this.getProperty(aPropertyPath.substring(0, lastDotIndex));
99
100 if (!setObject) {
101 this.undefinedSet(aPropertyPath);
102 return;
103 }
104
105 aPropertyPath = aPropertyPath.substring(lastDotIndex + 1);
106 } else {
107 // The property path describes a property on this object
108 setObject = this;
109 }
110
111 lastObjectAtPath = setObject.getProperty(aPropertyPath);
112
113 // TODO clean up some of the duplicated code here
114
115 if (lastObjectAtPath && Array.isArray(lastObjectAtPath)) {
116 if (lastObjectAtPath !== value) {
117 // if the value does not match the object described by this propertyPath; set it as the new value
118
119 if (Array.isArray(setObject)) {
120 // If the setObject is an array itself; splice (typically called by set) to trigger bindings, do it here to save time
121 propertyToSetOnArray = parseInt(aPropertyPath, 10);
122 if (!isNaN(propertyToSetOnArray)) {
123 if (setObject.length < propertyToSetOnArray) {
124 // TODO while I could set the value here I'm setting null and letting the splice,
125 // which we need to do anyway to trigger bindings, do the actual setting
126 setObject[propertyToSetOnArray] = null;
127 }
128
129 setObject.splice(propertyToSetOnArray, 1, value);
130
131 } else {
132 setObject[aPropertyPath] = value;
133 }
134
135 } else {
136 setObject[aPropertyPath] = value;
137 }
138
139 } else {
140 // Otherwise, they are the same object, a mutation event probably happened
141
142 // If the object at the property we're "setting" is itself an array, see if there was an event passed along
143 // as part of a change and whether we need to call the setObject's changeProperty method
144 var changeEvent = this.setProperty.changeEvent, modify;
145
146 // For these mutation/addition/removal events, use the 'modify' attribute of this property's descriptor
147 if (changeEvent && (changeEvent.currentTarget.getProperty(changeEvent.currentPropertyPath) === lastObjectAtPath) &&
148 (modify = M.Montage.getPropertyAttribute(setObject, aPropertyPath, MODIFY))) {
149 modify.call(setObject, changeEvent.type, changeEvent.newValue, changeEvent.prevValue);
150 }
151 }
152 } else if (Array.isArray(setObject)) {
153 // If the setObject is an array itself; splice (typically called by set) to trigger bindings, do it here to save time
154 propertyToSetOnArray = parseInt(aPropertyPath, 10);
155 if (!isNaN(propertyToSetOnArray)) {
156 if (setObject.length < propertyToSetOnArray) {
157 // TODO while I could set the value here I'm setting null and letting the splice,
158 // which we need to do anyway to trigger bindings, do the actual setting
159 setObject[propertyToSetOnArray] = null;
160 }
161 }
162 setObject.splice(propertyToSetOnArray, 1, value);
163 } else {
164 setObject[aPropertyPath] = value;
165 }
166 },
167 enumerable: false
168});
169
170/**
171@member external:Object#parentProperty
172@default null
173*/
174Object.defineProperty(Object.prototype, "parentProperty", {
175 enumerable: false,
176 value: null,
177 writable: true
178});
179
180/**
181 @function module:montage/core/core.Montage#undefinedGet
182 @param {Object} aPropertyName The object property name.
183 */
184Object.defineProperty(Object.prototype, "undefinedGet", {
185 value: function(aPropertyName) {
186 console.warn("get undefined property -" + aPropertyName + "-");
187 },
188 writable: true
189});
190
191/**
192 @function module:montage/core/core.Montage#undefinedSet
193 @param {Object} aPropertyName The object property name.
194 */
195Object.defineProperty(Object.prototype, "undefinedSet", {
196 value: function(aPropertyName) {
197 console.warn("set undefined property -" + aPropertyName + "-");
198 },
199 writable: true
200});
201
202/**
203 Returns the descriptor object for an object's property.
204 @function external:Object#getPropertyDescriptor
205 @param {Object} anObject The object containing the property.
206 @param {String} propertyName The name of the property.
207 @returns {Object} The object's property descriptor.
208 */
209Object.getPropertyDescriptor = function(anObject, propertyName) {
210 var current = anObject,
211 currentDescriptor;
212
213 do {
214 currentDescriptor = Object.getOwnPropertyDescriptor(current, propertyName);
215 } while (!currentDescriptor && (current = current.__proto__ || Object.getPrototypeOf(current)));
216
217 return currentDescriptor;
218};
219
220/**
221 Returns the prototype object and property descriptor for a property belonging to an object.
222 @function external:Object#getPrototypeAndDescriptorDefiningProperty
223 @param {Object} anObject The object to return the prototype for.
224 @param {String} propertyName The name of the property.
225 @returns {Object} An object containing two properties named <code>prototype</code> and <code>propertyDescriptor</code> that contain the object's prototype object and property descriptor, respectively.
226 */