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 ++-------------------- node_modules/montage/core/event/event-manager.js | 56 +- node_modules/montage/core/event/key-manager.js | 978 +++++++++++++++++++++++ 3 files changed, 1073 insertions(+), 914 deletions(-) create mode 100644 node_modules/montage/core/event/key-manager.js (limited to 'node_modules/montage/core/event') 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]; diff --git a/node_modules/montage/core/event/event-manager.js b/node_modules/montage/core/event/event-manager.js index d38b9755..9d7ae4e8 100755 --- a/node_modules/montage/core/event/event-manager.js +++ b/node_modules/montage/core/event/event-manager.js @@ -18,6 +18,7 @@ */ var Montage = require("montage").Montage, + UUID = require("core/uuid"), MutableEvent = require("core/event/mutable-event").MutableEvent, Serializer = require("core/serializer").Serializer, Deserializer = require("core/deserializer").Deserializer, @@ -73,6 +74,37 @@ Montage.defineProperty(Element.prototype, "controller", { enumerable: false }); +/** + 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. +*/ +Montage.defineProperty(Object.prototype, "addEventListener", { + value: function addEventListener(type, listener, useCapture) { + if (listener) { + 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. +*/ +Montage.defineProperty(Object.prototype, "removeEventListener", { + value: function removeEventListener(type, listener, useCapture) { + if (listener) { + this.dispatchChangeEvent = true; + defaultEventManager.unregisterEventListener(this, type, listener, useCapture); + } + } +}); /** @function external:Object#dispatchEvent @@ -393,7 +425,7 @@ var EventManager = exports.EventManager = Montage.create(Montage,/** @lends modu } if (!aWindow.uuid || aWindow.uuid.length === 0) { - aWindow.uuid = Montage.generateUID(); + aWindow.uuid = UUID.generate(); } if (this._windowsAwaitingFinalRegistration[aWindow.uuid] === aWindow) { @@ -448,15 +480,16 @@ var EventManager = exports.EventManager = Montage.create(Montage,/** @lends modu && 'addEventListener' in window.HTMLElement.prototype && window.Components && window.Components.interfaces) { - var candidate; + var candidate, candidatePrototype; for(candidate in Components.interfaces) { if(candidate.match(/^nsIDOMHTML\w*Element$/)) { candidate = candidate.replace(/^nsIDOM/, ''); if(candidate = window[candidate]) { - candidate.prototype.nativeAddEventListener = candidate.prototype.addEventListener; - candidate.prototype.addEventListener = Element.prototype.addEventListener; - candidate.prototype.nativeRemoveEventListener = candidate.prototype.removeEventListener; - candidate.prototype.removeEventListener = Element.prototype.removeEventListener; + candidatePrototype = candidate.prototype; + candidatePrototype.nativeAddEventListener = candidatePrototype.addEventListener; + candidatePrototype.addEventListener = Element.prototype.addEventListener; + candidatePrototype.nativeRemoveEventListener = candidatePrototype.removeEventListener; + candidatePrototype.removeEventListener = Element.prototype.removeEventListener; } } } @@ -787,6 +820,12 @@ var EventManager = exports.EventManager = Montage.create(Montage,/** @lends modu }, + _nonDelegateableEventTypes: { + enumerable: false, + distinct: true, + value: ["load", "resize", "message", "orientationchange", "beforeunload", "unload", + "dragenter", "dragleave", "drop", "dragover", "dragend"] + }, /** Determines the actual target to observe given a target and an eventType. This correctly decides whether to observe the element specified or to observe some other element to leverage event delegation. This should be consulted whenever starting or stopping the observation of a target for a given eventType. @@ -806,10 +845,7 @@ var EventManager = exports.EventManager = Montage.create(Montage,/** @lends modu // We install all native event listeners on the document, except for a few special event types // TODO this may be problematic for some events in some browsers, I'm afraid there will be too // many exceptions to do this in a generic manner - var nonDelegateableEventTypes = ["load", "resize", "message", "orientationchange", - "beforeunload", "unload", "dragenter", "dragleave", "drop", "dragover", "dragend"]; - - if ((/*isDocument*/!!target.defaultView) || nonDelegateableEventTypes.indexOf(eventType) >= 0) { + if ((/*isDocument*/!!target.defaultView) || this._nonDelegateableEventTypes.indexOf(eventType) >= 0) { return target; } else { return /* isWindow*/target.screen ? target.document : target.ownerDocument; diff --git a/node_modules/montage/core/event/key-manager.js b/node_modules/montage/core/event/key-manager.js new file mode 100644 index 00000000..c5728b06 --- /dev/null +++ b/node_modules/montage/core/event/key-manager.js @@ -0,0 +1,978 @@ +/* + This file contains proprietary software owned by Motorola Mobility, Inc.
+ No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
+ (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. +
*/ +/** + @module montage/core/event/key-manager + @requires montage/core/core + */ + +var Montage = require("montage").Montage, + defaultEventManager = require("core/event/event-manager").defaultEventManager, + MutableEvent = require("core/event/mutable-event").MutableEvent; + +var KEYNAMES_TO_KEYCODES = { + // W3C Key Code + backspace: 8, + tab: 9, + enter: 13, + shift: 16, + control: 17, + alt: 18, + capslock: 20, + escape: 27, + space: 32, + pageup: 33, + pagedown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + delete: 46, + + // W3C Optional Key Code (mostly for US keyboard layout) + semicolon: 186, + colon: 186, + equal: 187, + plus: 187, + comma: 188, + less: 188, + minus: 189, + underscore: 189, + period: 190, + greater: 190, + slash: 191, + questionmark: 191, + backtick: 192, + tilde: 192, + openingsquarebracket: 219, + openingcurlybracket: 219, + backslash: 220, + pipe: 220, + closingsquarebracket: 221, + closingcurlybracket: 221, + singlequote: 222, + doublequote: 222, + + + // Non standard Virtal key code but commonly used + clear: 12, + meta: 91, + contextmenu: 93, + numpad0: 96, + numpad1: 97, + numpad2: 98, + numpad3: 99, + numpad4: 100, + numpad5: 101, + numpad6: 102, + numpad7: 103, + numpad8: 104, + numpad9: 105, + multiply: 106, + add: 107, + subtract: 109, + decimal: 110, + divide: 111, + f1: 112, + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + f13: 124, + f14: 125, + f15: 126, + f16: 127, + f17: 128, + f18: 129, + f19: 130, + f20: 131, + f21: 132, + f22: 133, + f23: 134, + f24: 135 +}, + +KEYCODES_TO_KEYNAMES = null, // Will be build from the KEYNAMES_TO_KEYCODES dictionary + +OPERAMAC_KEYNAMES_TO_KEYCODES = { + meta: 17, + control: 57392, + f17: 63252, + f18: 63253, + f19: 63254, + f20: 63255, + f21: 63256, + f22: 63257, + f23: 63258, + f24: 63259 +}, + +FIREFOX_KEYNAMES_TO_KEYCODES = { + f13: 44, + f14: 145, + f15: 19, + f16: 63251, + f17: 63252, + f18: 63253, + f19: 63254, + f20: 63255, + f21: 63256, + f22: 63257, + f23: 63258, + f24: 63259, + meta: 224 +}, + +KEYNAMES_ALIASES = { + cmd: "command", + ctl: "control", + ctrl: "control", + win: "meta", + windows: "meta", + opt: "alt", + option: "alt" +}, + +MODIFIERS = { + meta: {name:"meta", value:1}, + alt: {name:"alt", value:2}, + control: {name:"control", value:4}, + shift: {name:"shift", value:8} +}, + +NORMALIZED_KEYS = { + semicolon: ";", + colon: ":", + equal: "=", + plus: "+", + comma: ",", + less: "<", + minus: "-", + underscore: "_", + period: ".", + greater: ">", + slash: "/", + questionmark: "?", + backtick: "`", + tilde: "~", + openingsquarebracket: "[", + openingcurlybracket: "{", + backslash: "\\", + pipe: "|", + closingsquarebracket: "]", + closingcurlybracket: "}", + singlequote: "'", + doublequote: "\"", + multiply: "*", + add: "+", + subtract: "-", + decimal: ".", + divide: "/" +}, + +NORMALIZED_CHARS = null; // Will be generated from the NORMALIZED_KEYS + + +/* Event type dispatched by KeyComposer */ +var KEYPRESS_EVENT_TYPE = "keyPress", + LONGKEYPRESS_EVENT_TYPE = "longKeyPress", + KEYRELEASE_EVENT_TYPE = "keyRelease"; + + +/** + @class module:montage/core/event/key-manager.KeyManager + @classdesc The KeyManager dispatches KeyComposer events when it detects a keyComposer has been pressed or released. + Do not create a KeyManager directly but instead require for the defaultKeyManager: require("core/event/key-manager").defaultKeyManager + @extends module:montage/core/core.Montage +*/ +var KeyManager = exports.KeyManager = Montage.create(Montage,/** @lends module:montage/core/event/key-manager.KeyManager# */ { + + /** + @private + */ + _keyEventsListenerInstalled: { + value: false + }, + + /** + @private + */ + _composerKeyMap: { + value: {} + }, + + /** + @private + */ + _triggeredKeys: { + value: {} + }, + + /** + @private + */ + _longPressKeys: { + value: {}