var M = require("core/core"); // lazy bound because of dependency cycle var MODIFY = "modify"; /** @function external:Object#getProperty @param {Object} aPropertyPath @param {Property} unique @param {Property} preserve @param {Function} visitedComponentCallback @param {Array} currentIndex @returns result */ Object.defineProperty(Object.prototype, "getProperty", { value: function(aPropertyPath, unique, preserve, visitedComponentCallback, currentIndex) { var dotIndex, result, currentPathComponent, nextDotIndex, remainingPath = null; if (aPropertyPath == null) { return; } dotIndex = aPropertyPath.indexOf(".", currentIndex); currentIndex = currentIndex || 0; currentPathComponent = aPropertyPath.substring(currentIndex, (dotIndex === -1 ? aPropertyPath.length : dotIndex)); if (currentPathComponent in this) { result = this[currentPathComponent]; } else { result = typeof this.undefinedGet === "function" ? this.undefinedGet(currentPathComponent) : undefined; } if (visitedComponentCallback) { nextDotIndex = aPropertyPath.indexOf(".", currentIndex); if (nextDotIndex != -1) { remainingPath = aPropertyPath.substr(nextDotIndex+1); } visitedComponentCallback(this, currentPathComponent, result, null, remainingPath); } if (visitedComponentCallback && result && -1 === dotIndex) { // We resolved the last object on the propertyPath, be sure to give the visitor a chance to handle this one //visitedComponentCallback(result, null, null, null, null); } else if (result && dotIndex !== -1) { // We resolved that component of the path, but there's more path components; go to the next if (result.getProperty) { result = result.getProperty(aPropertyPath, unique, preserve, visitedComponentCallback, dotIndex + 1); } else { // TODO track when this happens, right now it's only happening with CanvasPixelArray in WebKit result = Object.prototype.getProperty.call(result, aPropertyPath, unique, preserve, visitedComponentCallback, dotIndex + 1); } } // Otherwise, we reached the end of the propertyPath, or at least as far as we could; stop return result; }, enumerable: false }); /** @private */ Object.defineProperty(Object.prototype, "_propertySetterNamesByName", { value: {} }); /** @private */ Object.defineProperty(Object.prototype, "_propertySetterByName", { value: {} }); /** Description @member external:Object#setProperty @function @param {Object} aPropertyPath @param {Object} value @returns itself */ Object.defineProperty(Object.prototype, "setProperty", { value: function(aPropertyPath, value) { var propertyIsNumber = !isNaN(aPropertyPath), lastDotIndex = propertyIsNumber ? -1 : aPropertyPath.lastIndexOf("."), setObject, lastObjectAtPath, propertyToSetOnArray; if (lastDotIndex !== -1) { //The propertyPath describes a property that is deeper inside this object setObject = this.getProperty(aPropertyPath.substring(0, lastDotIndex)); if (!setObject) { this.undefinedSet(aPropertyPath); return; } aPropertyPath = aPropertyPath.substring(lastDotIndex + 1); } else { // The property path describes a property on this object setObject = this; } lastObjectAtPath = setObject.getProperty(aPropertyPath); // TODO clean up some of the duplicated code here if (lastObjectAtPath && Array.isArray(lastObjectAtPath)) { if (Array.isArray(setObject)) { // If the setObject is an array itself; splice (typically called by set) to trigger bindings, do it here to save time propertyToSetOnArray = parseInt(aPropertyPath, 10); if (!isNaN(propertyToSetOnArray)) { if (setObject.length < propertyToSetOnArray) { // TODO while I could set the value here I'm setting null and letting the splice, // which we need to do anyway to trigger bindings, do the actual setting setObject[propertyToSetOnArray] = null; } setObject.splice(propertyToSetOnArray, 1, value); } else { setObject[aPropertyPath] = value; } } else { setObject[aPropertyPath] = value; } } else if (Array.isArray(setObject)) { // If the setObject is an array itself; splice (typically called by set) to trigger bindings, do it here to save time propertyToSetOnArray = parseInt(aPropertyPath, 10); if (!isNaN(propertyToSetOnArray)) { if (setObject.length < propertyToSetOnArray) { // TODO while I could set the value here I'm setting null and letting the splice, // which we need to do anyway to trigger bindings, do the actual setting setObject[propertyToSetOnArray] = null; } } setObject.splice(propertyToSetOnArray, 1, value); } else { setObject[aPropertyPath] = value; } }, enumerable: false }); /** @member external:Object#parentProperty @default null */ Object.defineProperty(Object.prototype, "parentProperty", { enumerable: false, value: null, writable: true }); /** @function module:montage/core/core.Montage#undefinedGet @param {Object} aPropertyName The object property name. */ Object.defineProperty(Object.prototype, "undefinedGet", { value: function(aPropertyName) { console.warn("get undefined property -" + aPropertyName + "-"); }, writable: true }); /** @function module:montage/core/core.Montage#undefinedSet @param {Object} aPropertyName The object property name. */ Object.defineProperty(Object.prototype, "undefinedSet", { value: function(aPropertyName) { console.warn("set undefined property -" + aPropertyName + "-"); }, writable: true }); /** Returns the descriptor object for an object's property. @function external:Object#getPropertyDescriptor @param {Object} anObject The object containing the property. @param {String} propertyName The name of the property. @returns {Object} The object's property descriptor. */ Object.getPropertyDescriptor = function(anObject, propertyName) { var current = anObject, currentDescriptor; do { currentDescriptor = Object.getOwnPropertyDescriptor(current, propertyName); } while (!currentDescriptor && (current = current.__proto__ || Object.getPrototypeOf(current))); return currentDescriptor; }; /** Returns the prototype object and property descriptor for a property belonging to an object. @function external:Object#getPrototypeAndDescriptorDefiningProperty @param {Object} anObject The object to return the prototype for. @param {String} propertyName The name of the property. @returns {Object} An object containing two properties named prototype and propertyDescriptor that contain the object's prototype object and property descriptor, respectively. */ Object.getPrototypeAndDescriptorDefiningProperty = function(anObject, propertyName) { var current = anObject, currentDescriptor; if (propertyName) { do { currentDescriptor = Object.getOwnPropertyDescriptor(current, propertyName); } while (!currentDescriptor && (current = current.__proto__ || Object.getPrototypeOf(current))); return { prototype: current, propertyDescriptor: currentDescriptor }; } }; /** Removes all properties owned by this object making the object suitable for reuse @function module:montage/core/core.Object.wipe */ Object.defineProperty(Object.prototype, "wipe", { value: function() { var keys = Object.keys(this), i = keys.length; while(i) delete this[keys[--i]]; return this; } });