From 13ae16997d4bbca14e255d5989d1c44a76eac72c Mon Sep 17 00:00:00 2001 From: Valerio Virgillito Date: Wed, 16 May 2012 15:23:48 -0700 Subject: montage v.0.10 integration Signed-off-by: Valerio Virgillito --- node_modules/montage/core/event/binding.js | 953 ++--------------------------- 1 file changed, 49 insertions(+), 904 deletions(-) (limited to 'node_modules/montage/core/event/binding.js') diff --git a/node_modules/montage/core/event/binding.js b/node_modules/montage/core/event/binding.js index 709a5d85..2e226372 100755 --- a/node_modules/montage/core/event/binding.js +++ b/node_modules/montage/core/event/binding.js @@ -14,438 +14,19 @@ */ var Montage = require("montage").Montage, - ChangeEventConstructor = require("core/event/mutable-event")._Change, - ChangeTypes = require("core/event/mutable-event").ChangeTypes, + ChangeNotification = require("core/change-notification").ChangeNotification, Serializer = require("core/serializer").Serializer, Deserializer = require("core/deserializer").Deserializer, - logger = require("core/logger").logger("binding"); + logger = require("core/logger").logger("binding"), defaultEventManager = require("core/event/event-manager").defaultEventManager, AT_TARGET = 2, UNDERSCORE = "_"; - -/** - @member external:Array#dispatchChangeEvent -*/ -Object.defineProperty(Array.prototype, "dispatchChangeEvent", {value: false, enumerable: false, writable: true}); - -/** - @member external:Array#dispatchChangeAtIndexEvent -*/ -Object.defineProperty(Array.prototype, "dispatchChangeAtIndexEvent", {value: false, enumerable: false, writable: true}); - -/** - @member external:Array#dispatchChangeAtLengthEvent -*/ -Object.defineProperty(Array.prototype, "dispatchChangeAtLengthEvent", {value: false, enumerable: false, writable: true}); - /** @class module:montage/core/event/binding.ChangeEventDispatchingArray */ var ChangeEventDispatchingArray = exports.ChangeEventDispatchingArray = []; -/** - @function external:Array#addContentEventListener - @param {string} type Event type - @param {object|function} listener Event listener. - @param {boolean} useCapture Specifies whether to listen for the event during the capture phase. -*/ -Object.defineProperty(Array.prototype, "addContentEventListener", { - value: function(type, listener, useCapture) { - }, - enumerable: false, - configurable: true -}); - - -Object.defineProperty(ChangeEventDispatchingArray, "_splice", { - value: Array.prototype.splice, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#splice - @param {string} type Event type - @param {object|function} listener Event listener. - @param {boolean} useCapture Specifies whether to listen for the event during the capture phase. -*/ -Object.defineProperty(ChangeEventDispatchingArray, "splice", { - value: function(index, howMany/*[, element1[, ...[, elementN]]]*/) { - - var originalCount = this.length, - addedCount = arguments.length - 2, /* elements to add less the index and howMany parameters*/ - removedCount, - netChange, - i, changeType, changeEvent, affectedIndexCount, startIndex, changeIndex, - removedMembers, - addedMembers = []; - - if (addedCount > 0) { - addedMembers = this.slice.call(arguments, 2); - } - - // Index may be positive (from the front) or negative (from the back) figure out the positive one - // now in anticipation of needing it when dispatching events - startIndex = index >= 0 ? index : this.length + index; - removedMembers = this._splice.apply(this, arguments); - removedCount = removedMembers.length; - - netChange = addedCount - removedCount; - - // Find the most accurate propertyChange type for this splice, - // For the most part it's considered a modification unless the length of the array was modified - // if only to not bother notifying listeners for changes of the length of this array - changeType = ChangeTypes.MODIFICATION; - - if (netChange > 0) { - changeType = ChangeTypes.ADDITION; - } else if (netChange < 0) { - changeType = ChangeTypes.REMOVAL; - } - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = removedMembers; - changeEvent.plus = addedMembers; - changeEvent.changeIndex = index; - changeEvent.propertyChange = changeType; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - - if (typeof howMany === "undefined") { - // no howMany argument given: remove all elements after index? - // TODO this may only be in some implementations - affectedIndexCount = originalCount + addedCount; - } else if (0 === netChange) { - // No net change; affects only how many expected - affectedIndexCount = addedCount; - } else if (netChange > 0) { - // Net gain; affects from start to end of original array + net gain - affectedIndexCount = (originalCount - startIndex) + (netChange); - } else { - // Net loss; affects from start to end of original array - affectedIndexCount = originalCount - startIndex; - } - - for (i = 0; i < affectedIndexCount; i++) { - changeIndex = startIndex + i; - - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + changeIndex; - - // old value at changeIndex was either: - // - removed outright, or replaced - // - moved somewhere in the array due to a gain or loss - changeEvent.minus = (i < removedCount) ? removedMembers[i] : this[changeIndex + netChange]; - - changeEvent.plus = this[changeIndex]; - changeEvent.changeIndex = changeIndex; - changeEvent.propertyChange = changeType; - this.dispatchEvent(changeEvent); - } - } - - return removedMembers; - }, - enumerable: false, - configurable: true -}); -//Removes the first element from an array and returns that element. This method changes the length of the array. -Object.defineProperty(ChangeEventDispatchingArray, "_shift", { - value: Array.prototype.shift, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#shift -*/ -Object.defineProperty(ChangeEventDispatchingArray, "shift", { - value: function() { - - if (0 === this.length) { - return; - } - - var result, i, countI, changeEvent; - - result = this._shift.call(this); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = result; - changeEvent.plus = this[0]; - changeEvent.changeIndex = 0; - changeEvent.propertyChange = ChangeTypes.REMOVAL; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - // A single item was just removed form the front; notify all index listeners - // (including listeners for the index that is now undefined at the end) - for (i = 0,countI = this.length + 1; i < countI; i++) { - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + i; - changeEvent.minus = i === 0 ? result : this[i - 1]; - changeEvent.plus = this[i]; - changeEvent.changeIndex = i; - changeEvent.propertyChange = ChangeTypes.REMOVAL; - this.dispatchEvent(changeEvent); - } - } - return result; - }, - enumerable: false, - configurable: true -}); - -//Adds one or more elements to the beginning of an array and returns the new length of the array. -Object.defineProperty(ChangeEventDispatchingArray, "_unshift", { - value: Array.prototype.unshift, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#unshift -*/ -Object.defineProperty(ChangeEventDispatchingArray, "unshift", { - value: function() { - - var addedCount = arguments.length, i, countI, changeEvent; - - countI = this._unshift.apply(this, arguments); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = undefined; - changeEvent.plus = Array.prototype.slice.call(arguments, 0); - changeEvent.changeIndex = 0; - changeEvent.propertyChange = ChangeTypes.ADDITION; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - for (i = 0; i < countI; i++) { - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + (i); - changeEvent.minus = this[addedCount + i]; - changeEvent.plus = this[i]; - changeEvent.changeIndex = i; - changeEvent.propertyChange = ChangeTypes.ADDITION; - this.dispatchEvent(changeEvent); - } - } - - }, - enumerable: false, - configurable: true -}); - -Object.defineProperty(ChangeEventDispatchingArray, "_reverse", { - value: Array.prototype.reverse, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#reverse -*/ -Object.defineProperty(ChangeEventDispatchingArray, "reverse", { - value: function() { - - // There's really no point in reversing an empty array or an array of a single member - if (this.length <= 1) { - return this; - } - - var i, countI = this.length, changeEvent; - - this._reverse.apply(this, arguments); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = null; - changeEvent.plus = this; - changeEvent.changeIndex = 0; - changeEvent.propertyChange = ChangeTypes.MODIFICATION; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - for (i = 0; i < countI; i++) { - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + i; - changeEvent.minus = this[(this.length - 1) - i]; - changeEvent.plus = this[i]; - changeEvent.changeIndex = i; - changeEvent.propertyChange = ChangeTypes.MODIFICATION; - this.dispatchEvent(changeEvent); - } - } - - return this; - }, - enumerable: false, - configurable: true -}); - -Object.defineProperty(ChangeEventDispatchingArray, "_push", { - value: Array.prototype.push, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#push -*/ -Object.defineProperty(ChangeEventDispatchingArray, "push", { - value: function() { - - var mutationStartIndex = this.length, - addedCount = arguments.length, - i, - changeEvent; - - this._push.apply(this, arguments); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.plus = Array.prototype.slice.call(arguments, 0); - changeEvent.minus = undefined; - changeEvent.changeIndex = mutationStartIndex; - changeEvent.propertyChange = ChangeTypes.ADDITION; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - //Tell what happened - for (i = 0; i < addedCount; i++) { - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + (mutationStartIndex + i); - changeEvent.minus = undefined; - changeEvent.plus = arguments[i]; - changeEvent.changeIndex = i; - changeEvent.propertyChange = ChangeTypes.ADDITION; - this.dispatchEvent(changeEvent); - } - } - - }, - enumerable: false, - configurable: true -}); - -Object.defineProperty(ChangeEventDispatchingArray, "_pop", { - value: Array.prototype.pop, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#pop -*/ -Object.defineProperty(ChangeEventDispatchingArray, "pop", { - value: function() { - - if (this.length === 0) { - return; - } - - var result, - changeEvent, - changeIndex = this.length - 1; - - result = this._pop.call(this); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = result; - changeEvent.plus = undefined; - changeEvent.changeIndex = changeIndex; - changeEvent.propertyChange = ChangeTypes.REMOVAL; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - //Tell what happened on that specific index - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + changeIndex; - changeEvent.minus = result; - changeEvent.plus = undefined; - changeEvent.changeIndex = changeIndex; - changeEvent.propertyChange = ChangeTypes.REMOVAL; - this.dispatchEvent(changeEvent); - } - return result; - }, - enumerable: false, - configurable: true -}); - -Object.defineProperty(ChangeEventDispatchingArray, "_sort", { - value: Array.prototype.sort, - enumerable: false, - configurable: true -}); - -/** - @function module:montage/core/event/binding.ChangeEventDispatchingArray#sort -*/ -Object.defineProperty(ChangeEventDispatchingArray, "sort", { - value: function() { - var i, countI = this.length, copy = this.slice(), changeEvent; - - this._sort.apply(this, arguments); - - if (this.dispatchChangeEvent) { - changeEvent = new ChangeEventConstructor(); - changeEvent.minus = null; - changeEvent.plus = this; - changeEvent.changeIndex = 0; - changeEvent.propertyChange = ChangeTypes.MODIFICATION; - this.dispatchEvent(changeEvent); - } - - if (this.dispatchChangeAtIndexEvent) { - for (i = 0; i < countI; i++) { - - // Don't emit an event for an index that was not affected by the sort - if (copy[i] === this[i]) { - continue; - } - - changeEvent = new ChangeEventConstructor(); - changeEvent.type = "change@" + i; - changeEvent.minus = copy[i]; - changeEvent.plus = this[i]; - changeEvent.changeIndex = i; - changeEvent.propertyChange = ChangeTypes.MODIFICATION; - this.dispatchEvent(changeEvent); - } - } - - return this; - }, - enumerable: false, - configurable: true -}); - -/** - @function external:Object#automaticallyDispatchEvent - @param {string} eventType -*/ -Object.defineProperty(Object.prototype, "automaticallyDispatchEvent", { - value: function(eventType) { - return eventType.indexOf("change") === 0; - }, - enumerable: false, - configurable: true -}); - /** @class module:montage/core/event/binding.PropertyChangeBindingListener @extends module:montage/core/core.Montage @@ -501,13 +82,12 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje this.deferredValueTarget = ""; } }, - handleEvent: {value: function(event) { + handleChange: {value: function(event) { var targetPropertyPath = this.targetPropertyPath, target = this.target, localNewValue = event.plus, localPrevValue = event.minus, localTarget = event.target, - type = event.type, boundObjectValue, sourceObjectValue, dotIndex, @@ -519,9 +99,7 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje leftOriginated, changeOriginPropertyPath = null, exploredPath, - remainingPath, - i, - localPrevValueCount; + valueChanged; if (target !== bindingOrigin) { //the left and the right are different objects; easy enough @@ -557,9 +135,6 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje } } - targetPropertyPath = this.bindingPropertyPath; - target = bindingOrigin; - } else if (!this.bindingOriginChangeTriggered) { // If we're handling the event at this point we know the right side triggered it, from somewhere inside the observed propertyPath @@ -583,10 +158,9 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje event.target = this; } - baseType = event.baseType ? event.baseType : ((atSignIndex = type.indexOf("@")) > 0 ? (targetPropertyPath ? type.substring(0, atSignIndex) : type) : type); // The binding listener detected some change along the property path it cared about // make sure the event we "dispatch" has the full change@propertyPath eventType - event.type = "change@" + this.targetPropertyPath; + event.type = this.targetPropertyPath; //For bindings, start the right-to-left value push if (event.target === this.target && this.bindingPropertyPath && bindingOrigin) { @@ -600,15 +174,21 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje boundObjectValue = this.bindingDescriptor.converter.convert(boundObjectValue); } - if (this.bindingOriginValueDeferred === true || bindingOrigin._bindingsDisabled) { - this.deferredValue = boundObjectValue; - this.deferredValueTarget = "bound"; - } else { - // Make the original event available to the setter - this.bindingOrigin.setProperty.changeEvent = event; - // Set the value on the LEFT side now - this.bindingOrigin.setProperty(this.bindingPropertyPath, boundObjectValue); - this.bindingOrigin.setProperty.changeEvent = null; + // If the the value about to be pushed over to the bindingOrigin is already there don't call the setter + valueChanged = boundObjectValue !== event.plus ? + (this.bindingOrigin.getProperty(this.bindingPropertyPath) !== boundObjectValue) : true; + + if (valueChanged) { + if (this.bindingOriginValueDeferred === true || bindingOrigin._bindingsDisabled) { + this.deferredValue = boundObjectValue; + this.deferredValueTarget = "bound"; + } else { + // Make the original event available to the setter + this.bindingOrigin.setProperty.changeEvent = event; + // Set the value on the LEFT side now + this.bindingOrigin.setProperty(this.bindingPropertyPath, boundObjectValue); + this.bindingOrigin.setProperty.changeEvent = null; + } } } // Otherwise, there was probably a listener for a change at this path that was not a part of some binding @@ -648,35 +228,14 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje changeOriginPropertyPath = exploredPath.replace(/^\./, ""); } }); - - if (changeOriginPropertyPath) { - remainingPath = this.targetPropertyPath.replace(new RegExp("^" + changeOriginPropertyPath + "\.?"), ""); - } else { - remainingPath = this.targetPropertyPath; - } - - // NOTE this check works around Safari not having a removeEventListener on its CanvasPixelArray - // TODO investigate if this is an appropriate fix or not - if (typeof localPrevValue.removeEventListener === "function") { - localPrevValue.removeEventListener(baseType + "@" + remainingPath, this, this.useCapture); - } - } - - if (localNewValue) { - // Reinstall listeners along the entire propertyPath from the target - this.target.addEventListener(baseType + "@" + this.targetPropertyPath, this, this.useCapture); - } else if (event._event.plus) { - // TODO removing this causes no spec failures; looks suspicious - this.target.addEventListener(baseType + "@" + this.targetPropertyPath, this, this.useCapture); } - } + targetPropertyPath = null; target = null; localNewValue = null; localPrevValue = null; localTarget = null; - type = null; dotIndex = null; nextPathComponent = null; atSignIndex = null; @@ -700,443 +259,30 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje Object.defineProperty(Object.prototype, "propertyChangeBindingListener", { value: function(type, listener, useCapture, atSignIndex, bindingOrigin, bindingPropertyPath, bindingDescriptor) { - var targetPropertyPath, targetPropertyPathCurrentIndex, /*dotIndex,*/ + var targetPropertyPath, functionListener = PropertyChangeBindingListener.create(); - if (typeof atSignIndex !== "number") { - atSignIndex = type.indexOf("@"); - } - functionListener.useCapture = useCapture; functionListener.target = this; functionListener.originalListener = listener; functionListener.originalListenerIsFunction = (typeof listener === "function"); - if (atSignIndex > -1) { - functionListener.targetPropertyPath = targetPropertyPath = type.substring(atSignIndex + 1); - //This is storing the future prevValue - functionListener.previousTargetPropertyPathValue = this.getProperty(targetPropertyPath); - functionListener.targetPropertyPathCurrentIndex = targetPropertyPathCurrentIndex = 0; - if (bindingOrigin) { - // dotIndex = functionListener.targetPropertyPath.indexOf(".", functionListener.targetPropertyPathCurrentIndex); - // functionListener.currentPropertyComponent = targetPropertyPath.substring(targetPropertyPathCurrentIndex, (dotIndex === -1 ? targetPropertyPath.length: dotIndex)); - functionListener.bindingOrigin = bindingOrigin; - functionListener.bindingPropertyPath = bindingPropertyPath; - functionListener.bindingDescriptor = bindingDescriptor; - functionListener.bindingOriginValueDeferred = bindingDescriptor.deferred ? true : false; - } - } - return functionListener; - }, - writable: true -}); - -/** - Adds an event listener to the object. - @function external:Object#addEventListener - @param {string} type The event type to listen for. - @param {object | function} listener The listener object or function. - @param {boolean} useCapture Specifies whether to listen for the event during the bubble or capture phases. -*/ -Object.defineProperty(Object.prototype, "addEventListener", { - value: function addEventListener(type, listener, useCapture) { - - if (!listener) { - return; - } - var atSignIndex = type.indexOf("@"), - currentPropertyDescriptor, - setter, - getter, - internalStorageProperty, - prototypeAndDescriptor, - currentIndexListeneeProperty, - propertyPath, - propertyPathCurrentIndex, - prototypeDefiningProperty, - currentDispatchingSetterPropertyDescriptor, - changePropertyPath, - independentProperty, - i, - dependencies, - dependencyEntry, - dependencyPropertyPath, - firstDotIndex; - - // Properly configure the listener and/or target for "change" and "change@foo" events - if (atSignIndex > 0 || "change" === type) { - - // The listener for a change@ needs to be considered a propertyChangeBindingListener - // TODO this does seem a little odd seeing as you could just be adding a "change@foo" eventListener outside of bindings - // so the binding portion of this name is a little misleading - if (atSignIndex > 0 && (listener.__proto__ || Object.getPrototypeOf(listener)) !== PropertyChangeBindingListener) { - listener = this.propertyChangeBindingListener(type, listener, useCapture, atSignIndex); - } - - // In either case, if the eventTarget is an array, it needs to be a ChangeEventDispatchingArray - // to properly dispatch events (basically all the mutators need to be wrapped) - if (Array.isArray(this) && this.proto !== ChangeEventDispatchingArray) { - this.__proto__ = ChangeEventDispatchingArray; - } - - changePropertyPath = type.replace("change@", ""); + functionListener.targetPropertyPath = targetPropertyPath = type; + //This is storing the future prevValue + functionListener.previousTargetPropertyPathValue = this.getProperty(targetPropertyPath); + functionListener.targetPropertyPathCurrentIndex = 0; + if (bindingOrigin) { + functionListener.bindingOrigin = bindingOrigin; + functionListener.bindingPropertyPath = bindingPropertyPath; + functionListener.bindingDescriptor = bindingDescriptor; + functionListener.bindingOriginValueDeferred = bindingDescriptor.deferred ? true : false; } - // If this is part of bindings, install the necessary listeners down the entire property path - // Don't do anything special if it's jsut for "change" though. there's no need to traverse a path - if ("change" !== type && (listener.__proto__ || Object.getPrototypeOf(listener)) === PropertyChangeBindingListener) { - - // Listening to Left side - if (changePropertyPath === listener.bindingPropertyPath) { - currentIndexListeneeProperty = "bindingOriginCurrentIndexListenee"; - propertyPath = listener.bindingPropertyPath; - propertyPathCurrentIndex = listener.bindingPropertyPathCurrentIndex; - } - //Listening to Right side - else { - currentIndexListeneeProperty = "currentIndexListenee"; - propertyPath = listener.targetPropertyPath; - propertyPathCurrentIndex = listener.targetPropertyPathCurrentIndex; - } - - var visit = function(currentObject, key, value, index) { - - // TODO I've forgotten why the index is passed along. I think we only ever use it when - // traversing through an array in a property path to indicate we're visiting some member of that collection - // but we're clearly not using it here; it should probably be removed if we're really never using it - - if (!(typeof currentObject === "function" || typeof currentObject === "object") || - (currentObject.constructor === String && "length" === key)) { - // Stop: - // if this isn't a function or an object - // if we're trying to observe the length of a string (we'll get that from the listener on the string) - - return; - } - - if (currentObject._dependenciesForProperty) { - dependencies = currentObject._dependenciesForProperty[key]; - - if (dependencies) { - dependencyEntry = currentObject._dependencyListeners[key]; - - if (!dependencyEntry) { - dependencyEntry = currentObject._dependencyListeners[key] = { - observedDependencies: [], - listener: null - }; - - dependencyEntry.listener = (function(self, key) { - return function(event) { - // Ignore events that have reached this dependency listener via capture/bubble distribution - if (AT_TARGET === event.eventPhase) { - var anEvent = document.createEvent("CustomEvent"); - anEvent.initCustomEvent("change@" + key, true, false, null); - anEvent.propertyName = key; - self.dispatchEvent(anEvent); - } - } - })(currentObject, key); - } - - // Ensure we use the dependencyListener to observe all the dependent keys - // We can use the same listener for al lof them though as a change at - // any independent property is treated as a change at a dependent property - for (i = 0; (independentProperty = dependencies[i]); i++) { - - // Don't double observe a dependency - if (dependencyEntry.observedDependencies.indexOf(independentProperty) >= 0) { - continue; - } - - currentObject.addEventListener("change@" + independentProperty, dependencyEntry.listener, true); - dependencyEntry.observedDependencies.push(independentProperty); - } - } - } - - // Operators aren't to be stored - if (key && key.indexOf("(") > -1) { - key = null; - } - - var type = key ? "change@" + key : "change", - currentPropertySetter, enumerable; - - // TODO what does this block really do? I gather its storing the original value... - if (currentIndexListeneeProperty) { - if (!listener[currentIndexListeneeProperty]) { - listener[currentIndexListeneeProperty] = {}; - } - if (!listener[currentIndexListeneeProperty][currentObject.uuid]) { - listener[currentIndexListeneeProperty][currentObject.uuid] = {}; - } - - // TODO was this right? - if (key) { - listener[currentIndexListeneeProperty][currentObject.uuid][key] = value; - } - } - - // We may have come across an array at this key; make sure we make it a changeDispatchingArray - // TODO should we have just called AddEventListener from visit to keep this consistent? - if (Array.isArray(currentObject) && currentObject.proto !== ChangeEventDispatchingArray) { - currentObject.__proto__ = ChangeEventDispatchingArray; - } - - // Now register listeners depending on the target we are trying to observe - if (currentObject.__proto__ === ChangeEventDispatchingArray) { - - if (key && !isNaN(key)) { - currentObject.dispatchChangeAtIndexEvent = true; - } else { - currentObject.dispatchChangeEvent = true; - } - - defaultEventManager.registerEventListener(currentObject, type, listener, useCapture); - - } else { - - // TODO right now we'll install listeners like change@any we probably shouldn't as any is a function, - // maybe pass along the string or a function ref to determine whether to add a listener or not? - // TODO make any and sum propetyPathOperators, not just array methods? easier to detect? - - if (defaultEventManager.registerEventListener(currentObject, type, listener, useCapture)) { - - if (key && currentObject.automaticallyDispatchEvent(type, key)) { - - prototypeAndDescriptor = Object.getPrototypeAndDescriptorDefiningProperty(currentObject, key); - currentPropertyDescriptor = prototypeAndDescriptor.propertyDescriptor; - - if (currentPropertyDescriptor) { - - currentPropertySetter = currentPropertyDescriptor.set; - getter = currentPropertyDescriptor.get; - - if (!currentPropertySetter || !currentPropertySetter.isDispatchingSetter) { - - //If the property is a value, we need to replace it by a set/get and add a storage. - if ("value" in currentPropertyDescriptor) { - - //Create internal storage: - Object.defineProperty(currentObject, (internalStorageProperty = UNDERSCORE + key), { - value: currentObject.getProperty(key), - configurable: true, - writable: true - }); - //Create getter on internal storage: - getter = function getter() { - //console.log("getter for " + getter.storageProperty); - return this[getter.storageProperty]; - }; - - getter.storageProperty = internalStorageProperty; - if (currentObject.hasOwnProperty(key)) { - enumerable = Object.getOwnPropertyDescriptor(currentObject, key).enumerable; - } else { - enumerable = true; - } - delete currentObject[key]; - Object.defineProperty(currentObject, key, {get: getter,configurable:true, set: setter, enumerable: enumerable}); - } - - //Now build the setter, if needed: Do we have it created on our prototype. - prototypeDefiningProperty = prototypeAndDescriptor.prototype; - currentDispatchingSetterPropertyDescriptor = Object.getPropertyDescriptor(prototypeDefiningProperty, "__dispatching_" + key + "_setter"); - if (!currentDispatchingSetterPropertyDescriptor || !("value" in currentDispatchingSetterPropertyDescriptor)) { - setter = function setter(value) { - - var prevValue = this[setter.property], - anEvent, - acceptedValue; - - // we need to actually use the actual setter before assuming this value will be accepted as is - if (setter.storageProperty) { - //if we had a storage property backing what was originally a 'value' property, use it now - this[setter.storageProperty] = value; - } else if (currentPropertySetter) { - // if we had an original setter function, use it now - currentPropertySetter.apply(this, arguments); - } - - acceptedValue = this[setter.property]; - - //if the passed value is the same as the one already on the object, then we shouldn't do anything, for performance reason, as it would be - //a waste of time - if (acceptedValue !== prevValue || ( (prevValue != null) && (prevValue.equals && !prevValue.equals(acceptedValue)))) { - anEvent = document.createEvent("CustomEvent"); - anEvent.initCustomEvent("change@" + setter.property, true, false, null); - anEvent.minus = prevValue; - anEvent.plus = acceptedValue; - anEvent.propertyChange = ChangeTypes.MODIFICATION; - anEvent.propertyName = setter.property; - this.dispatchEvent(anEvent); - } - }; - - setter.storageProperty = internalStorageProperty; - setter.property = key; - setter.isDispatchingSetter = true; - Object.defineProperty(prototypeDefiningProperty, "__dispatching_" + key + "_setter", { - value: setter - }); - } - else { - setter = currentDispatchingSetterPropertyDescriptor.value; - } - - if (currentObject.hasOwnProperty(key)) { - enumerable = Object.getOwnPropertyDescriptor(currentObject, key).enumerable; - } else { - enumerable = true; - } - delete currentObject[key]; - Object.defineProperty(currentObject, key, { - set: setter, - get: getter, - configurable: true, - enumerable: enumerable - }); - - internalStorageProperty = null; - currentObject = null; - } - } else { - // if there was no property descriptor for this key on this object, how about we add one... - // TODO do we want to do this? we need to in one sense or else we'd never be able to observe - // keys on an object that didn't exist in the same way that we observe indices that - // didn't exist in an array - - //TODO this is all duplicated from above, clean it up - - //Create internal storage: - Object.defineProperty(currentObject, (internalStorageProperty = UNDERSCORE + key), { - value: currentObject.getProperty(key), - configurable: true, - writable: true - }); - //Create getter on internal storage: - getter = function getter() { - //console.log("getter for " + getter.storageProperty); - return this[getter.storageProperty]; - }; - - getter.storageProperty = internalStorageProperty; - - setter = function setter(value) { - - var prevValue = this[setter.property], - anEvent, - acceptedValue; - - // we need to actually use the actual setter before assuming this value will be accepted as is - if (setter.storageProperty) { - this[setter.storageProperty] = value; - } else { - currentPropertySetter.apply(this, arguments); - } - - acceptedValue = this[setter.property]; - - //if the passed value is the same as the one already on the object, then we shouldn't do anything, for performance reason, as it would be - //a waste of time - if (acceptedValue !== prevValue || ( (prevValue != null) && (prevValue.equals && !prevValue.equals(acceptedValue)))) { - anEvent = document.createEvent("CustomEvent"); - anEvent.initCustomEvent("change@" + setter.property, true, false, null); - anEvent.minus = prevValue; - anEvent.plus = acceptedValue; - anEvent.propertyChange = ChangeTypes.MODIFICATION; - anEvent.propertyName = setter.property; - this.dispatchEvent(anEvent); - } - }; - - setter.storageProperty = internalStorageProperty; - setter.property = key; - setter.isDispatchingSetter = true; - - prototypeDefiningProperty = prototypeAndDescriptor.prototype; - if (prototypeDefiningProperty) { - Object.defineProperty(prototypeDefiningProperty, "__dispatching_" + key + "_setter", { - value: setter - }); - } - - Object.defineProperty(currentObject, key, { - set: setter, - get: getter, - configurable: true - }); - - internalStorageProperty = null; - currentObject = null; - - } - } - - - } - - } - }; - - // visit all available objects along the propertyPath - this.getProperty(propertyPath, null, null, visit, propertyPathCurrentIndex); - - } - //otherwise, we're not observing a change@aPropertyPath; just install the listener for the given eventType - else { - this.dispatchChangeEvent = true; - defaultEventManager.registerEventListener(this, type, listener, useCapture); - } - - } -}); - -/** - Removes an event listener from the object. - @function external:Object#removeEventListener - @param {string} type The event type. - @param {object | function} listener The listener object or function. - @param {boolean} useCapture The phase of the event listener. -*/ -Object.defineProperty(Object.prototype, "removeEventListener", { - value: function removeEventListener(type, listener, useCapture) { - - if (!listener) { - return; - } - - var atSignIndex = type.indexOf("@"), - propertyPath; - - // this was a change event listener for some property path, remove listeners along the path - if (atSignIndex !== -1) { - - propertyPath = type.substring(atSignIndex + 1); - - var visit = function(currentObject, key) { - if (!key) { - return; - } - - defaultEventManager.unregisterEventListener(currentObject, "change@" + key, listener, useCapture); - }; - - - this.getProperty(propertyPath, null, null, visit); - - } - //otherwise, we're not removing a change@aPropertyPath; just remove the listener for the given eventType - else { - this.dispatchChangeEvent = true; - defaultEventManager.unregisterEventListener(this, type, listener, useCapture); - } - - - } + return functionListener; + }, + writable: true }); /** @@ -1251,7 +397,6 @@ Object.defineProperty(Object, "defineBinding", {value: function(sourceObject, so boundObjectPropertyPath = bindingDescriptor.boundObjectPropertyPath, boundObjectValue, currentBindingDescriptor, - comboEventType = "change@" + boundObjectPropertyPath , bindingListener; if (!boundObject || !boundObjectPropertyPath) { @@ -1285,7 +430,7 @@ Object.defineProperty(Object, "defineBinding", {value: function(sourceObject, so _bindingDescriptors[sourceObjectPropertyBindingPath] = bindingDescriptor; //Asking the boundObject to give me a propertyChangeBindingListener. - need to rename that - - bindingListener = boundObject.propertyChangeBindingListener(comboEventType, null, true/*useCapture*/, null, sourceObject, sourceObjectPropertyBindingPath, bindingDescriptor); + bindingListener = boundObject.propertyChangeBindingListener(boundObjectPropertyPath, null, true/*useCapture*/, null, sourceObject, sourceObjectPropertyBindingPath, bindingDescriptor); if (!bindingListener) { // The bound object decided it didn't want to install a listener for this binding, for whatever reason @@ -1300,26 +445,24 @@ Object.defineProperty(Object, "defineBinding", {value: function(sourceObject, so boundObject = bindingDescriptor.boundObject; boundObjectPropertyPath = bindingListener.targetPropertyPath; oneway = typeof bindingDescriptor.oneway === "undefined" ? false : bindingDescriptor.oneway; - comboEventType = "change@" + boundObjectPropertyPath; bindingDescriptor.boundObjectPropertyPath = boundObjectPropertyPath; bindingDescriptor.bindingListener = bindingListener; - //I'm setting the listener to be itself bindingListener.listener = bindingListener; - //1) the boundObjectPropertyPath need to be observed on boundObject - boundObject.addEventListener(comboEventType, bindingListener, true); + //1) the boundObjectPropertyPath needs to be observed on boundObject for all bindings + boundObject.addPropertyChangeListener(boundObjectPropertyPath, bindingListener, false); - //2) the sourceObjectPropertyBindingPath needs to be observed on sourceObject, only if sourceObjectPropertyBindingPath is expected to change - //as specified by oneway + //2) the sourceObjectPropertyBindingPath needs to be observed on sourceObject if this is a two-way binding if (!oneway) { - sourceObject.addEventListener("change@" + sourceObjectPropertyBindingPath, bindingListener, true); + sourceObject.addPropertyChangeListener(sourceObjectPropertyBindingPath, bindingListener, false); } - //3) Convert the value if needed prior to deferring the value or setting it on the source + //3) Get the value to set on the source (and convert if necessary) boundObjectValue = boundObject.getProperty(boundObjectPropertyPath); + // Though somewhat deprecated, give the boundValueMutator function precedence over a converter object if (bindingDescriptor.boundValueMutator) { boundObjectValue = bindingDescriptor.boundValueMutator(boundObjectValue); } else if (bindingDescriptor.converter) { @@ -1369,18 +512,20 @@ Object.defineProperty(Object.prototype, "_deserializeProperty_bindingDescriptors @param {string} sourceObjectPropertyBindingPath The key path to the bound object's bound property. */ Object.defineProperty(Object, "deleteBinding", {value: function(sourceObject, sourceObjectPropertyBindingPath) { - var _bindingDescriptors = sourceObject._bindingDescriptors, bindingDescriptor,oneway; + var _bindingDescriptors = sourceObject._bindingDescriptors, + bindingDescriptor, + oneway; + if (sourceObjectPropertyBindingPath in _bindingDescriptors) { bindingDescriptor = _bindingDescriptors[sourceObjectPropertyBindingPath]; oneway = typeof bindingDescriptor.oneway === "undefined" ? true : bindingDescriptor.oneway; - //1) the boundObjectPropertyPath need to be removed as a listener on boundObject - bindingDescriptor.boundObject.removeEventListener("change@" + bindingDescriptor.boundObjectPropertyPath, bindingDescriptor.bindingListener, true); + //1) Stop observing the boundObjectPropertyPath on the boundObject + bindingDescriptor.boundObject.removePropertyChangeListener(bindingDescriptor.boundObjectPropertyPath, bindingDescriptor.bindingListener, false); - //2) the sourceObjectPropertyBindingPath needs to be removed as a listener on sourceObject, only if sourceObjectPropertyBindingPath is expected to change - //as specified by oneway + //2) Stop observing the sourceObjectPropertyBindingPath on the sourceObject this was not a oneway binding if (!oneway) { - sourceObject.removeEventListener("change@" + sourceObjectPropertyBindingPath, bindingDescriptor.bindingListener, true); + sourceObject.removePropertyChangeListener(sourceObjectPropertyBindingPath, bindingDescriptor.bindingListener, false); } delete _bindingDescriptors[sourceObjectPropertyBindingPath]; -- cgit v1.2.3