From f7e4257745ccd44b8d24555f0ef787429d6e472c Mon Sep 17 00:00:00 2001 From: Valerio Virgillito Date: Tue, 5 Jun 2012 00:11:03 -0700 Subject: adding the latest v0.10 montage Signed-off-by: Valerio Virgillito --- js/io/system/ninjalibrary.json | 2 +- node_modules/descriptor.json | 1 - node_modules/montage/CHANGES.md | 347 +++++++++++++++++++++ node_modules/montage/core/change-notification.js | 82 +++-- .../montage/core/converter/bytes-converter.js | 3 +- node_modules/montage/core/converter/converter.js | 3 +- .../montage/core/converter/currency-converter.js | 9 +- .../montage/core/converter/date-converter.js | 12 +- .../montage/core/converter/number-converter.js | 28 +- .../montage/core/converter/upper-case-converter.js | 2 +- node_modules/montage/core/core.js | 32 +- node_modules/montage/core/deserializer.js | 29 +- .../montage/core/event/action-event-listener.js | 6 +- node_modules/montage/core/event/binding.js | 202 ++++-------- node_modules/montage/core/extras/object.js | 41 +-- node_modules/montage/montage.js | 20 +- node_modules/montage/package.json | 2 +- .../autocomplete/autocomplete.reel/autocomplete.js | 15 +- .../results-list.reel/results-list.html | 11 +- .../bluemoon/button-group.reel/button-group.html | 2 +- .../montage/ui/bluemoon/button.reel/button.html | 2 +- .../ui/bluemoon/progress.reel/progress.html | 1 - .../montage/ui/bluemoon/progress.reel/progress.js | 11 +- .../montage/ui/bluemoon/slider.reel/slider.html | 2 +- .../montage/ui/bluemoon/slider.reel/slider.js | 267 +++++----------- node_modules/montage/ui/button.reel/button.js | 2 +- node_modules/montage/ui/component.js | 22 +- node_modules/montage/ui/composer/composer.js | 9 +- .../montage/ui/composer/translate-composer.js | 99 +++--- .../montage/ui/condition.reel/condition.js | 13 +- .../montage/ui/controller/array-controller.js | 247 +++++++-------- .../ui/controller/paged-array-controller.js | 260 --------------- .../montage/ui/dynamic-text.reel/dynamic-text.js | 6 +- node_modules/montage/ui/editable-text.js | 3 +- node_modules/montage/ui/flow.reel/flow.html | 11 +- node_modules/montage/ui/flow.reel/flow.js | 44 ++- node_modules/montage/ui/list.reel/list.html | 6 +- node_modules/montage/ui/list.reel/list.js | 24 +- node_modules/montage/ui/loader.reel/loader.js | 5 + .../ui/loading-panel.reel/loading-panel.html | 11 +- node_modules/montage/ui/loading.reel/loading.html | 2 +- node_modules/montage/ui/loading.reel/loading.js | 3 +- node_modules/montage/ui/native-control.js | 50 +-- .../montage/ui/popup/alert.reel/alert.html | 10 +- .../montage/ui/popup/confirm.reel/confirm.html | 8 +- .../montage/ui/popup/notifier.reel/notifier.html | 2 +- .../montage/ui/popup/notifier.reel/notifier.js | 6 +- .../montage/ui/popup/popup.reel/popup.html | 4 +- node_modules/montage/ui/popup/popup.reel/popup.js | 41 ++- .../montage/ui/repetition.reel/repetition.js | 188 ++++++----- .../rich-text-linkpopup.html | 4 +- .../rich-text-resizer.reel/rich-text-resizer.html | 4 +- .../rich-text-editor.reel/rich-text-editor-base.js | 2 +- .../rich-text-editor.reel/rich-text-editor.css | 7 +- .../rich-text-editor.reel/rich-text-editor.html | 4 +- .../rich-text-editor.reel/rich-text-editor.js | 6 +- .../montage/ui/scroll-bars.reel/scroll-bars.html | 1 - .../montage/ui/scroll-bars.reel/scroll-bars.js | 61 +++- .../montage/ui/scroller.reel/scroller.html | 12 +- node_modules/montage/ui/scroller.reel/scroller.js | 32 +- .../montage/ui/select-input.reel/select-input.js | 27 +- .../ui/skeleton/range-input.reel/range-input.css | 6 +- .../ui/skeleton/range-input.reel/range-input.html | 6 +- .../ui/skeleton/range-input.reel/range-input.js | 173 ++++++---- node_modules/montage/ui/slot.reel/slot.js | 13 +- .../montage/ui/substitution.reel/substitution.js | 10 +- node_modules/montage/ui/tabs.reel/tabs.html | 6 +- node_modules/montage/ui/tabs.reel/tabs.js | 21 +- node_modules/montage/ui/template.js | 13 +- node_modules/montage/ui/text-input.js | 43 +-- .../montage/ui/toggle-button.reel/toggle-button.js | 50 ++- .../token-field/token-field.reel/token-field.html | 12 +- .../ui/token-field/token-field.reel/token-field.js | 24 +- .../montage/ui/token-field/token.reel/token.html | 6 +- .../montage/ui/token-field/token.reel/token.js | 65 ++-- .../montage/ui/video-player.reel/video-player.html | 44 +-- 76 files changed, 1512 insertions(+), 1348 deletions(-) create mode 100755 node_modules/montage/CHANGES.md delete mode 100755 node_modules/montage/ui/controller/paged-array-controller.js diff --git a/js/io/system/ninjalibrary.json b/js/io/system/ninjalibrary.json index 5c1eb875..0b5804c3 100644 --- a/js/io/system/ninjalibrary.json +++ b/js/io/system/ninjalibrary.json @@ -1,6 +1,6 @@ { "libraries": [ - {"name": "Montage", "path": "/node_modules/descriptor.json", "version": "0.10.0.0"}, + {"name": "Montage", "path": "/node_modules/descriptor.json", "version": "0.10.1.0"}, {"name": "RDGE", "path": "/assets/descriptor.json", "version": "0.5.7.0"} ] } \ No newline at end of file diff --git a/node_modules/descriptor.json b/node_modules/descriptor.json index 5617234f..6d29cb58 100644 --- a/node_modules/descriptor.json +++ b/node_modules/descriptor.json @@ -220,7 +220,6 @@ "montage/ui/controller/array-controller.js", "montage/ui/controller/media-controller.js", "montage/ui/controller/object-controller.js", - "montage/ui/controller/paged-array-controller.js", "montage/ui/date-input.reel/date-input.js", "montage/ui/dom.js", "montage/ui/dynamic-text.reel/dynamic-text.js", diff --git a/node_modules/montage/CHANGES.md b/node_modules/montage/CHANGES.md new file mode 100755 index 00000000..5b544221 --- /dev/null +++ b/node_modules/montage/CHANGES.md @@ -0,0 +1,347 @@ +# v0.10.0 + +- **New property change API** + + In prior Montage releases if you wanted to be notified when the value of a property changed, or an array was + modified, you used the addEventListener() API to register a listener object (or function). This API has been + replaced with addPropertyChangeListener(), which does everything that addEventListener() did, plus more. + +- **Changes to data binding** + + In this release, bindings can only be serialized on the source object in the binding. Practically, this means that + the arrow in the binding serialization can only point from right-to-left, not from left-to-right. + Also, the double-arrow syntax (<<->, for example) is no longer valid. + +- **__debugChangeNotifications__()** + + To help debug and inspect change listeners that you’ve registered on an object, call __debugChangeNotifications__() + This is temporary stop gap which will be replaced by better debugging tools soon. + +- **KeyComposer and KeyManager** + + Montage now includes a mechanism for easily listening for and responding to keyboard events, including individual + keys, or key combinations that you specify. It consists of two Montage objects: KeyComposer and KeyManager. You + create a KeyComposer object and specify the keys, or key combinations, that you want to respond to, as well as the + listener object that defines the necessary handlers. A default instance of the KeyManager prototype listens for + native key events on behalf of all active KeyComposers. It then invokes a callback method on the listener object + you specified. + +- **TokenField** + + The TokenField component is a text input component that displays tokens as its content. + +- **Flow component** + + The Flow component now supports selection handling and a feature called “stride”. The stride of a Flow component + defines points at regular intervals where scrolling should stop. For example, suppose you are creating a carousel + of album covers and you want the scrolling to stop when it reaches a position where the next album is always + positioned at the center. In this case the stride value would be the scroll distance between an element at the + center and the next element at the center. If you wanted scrolling to stop at every second or third album you would + set a stride to double or triple of that value. That way you could have, for example, a page showing 10 albums, and + you would be able to scroll 10 albums at a time, never stopping in the middle. + +- **DynamicElement** + + The DynamicElement component takes a string of plain text or HTML and inserts it into the component’s associated DOM + element, clearing any existing content. + + +# v0.9.0 + +- **Draw cycle changes** + + After the willDraw phase all the components needing draw are sorted by their level in the + component hierarchy. They are then processed in reverse order. This ensures that all the + childComponents needing draw have drawn by the time the parent's draw is called. The didDraw + uses the same list but processes the componets in top down order (the reverse of draw) + +- **Components** + + Autocomplete Textfield Added + An Autocomplete Textfield extends the Textfield component to support suggestions for the user to pick values. + The HTML markup for the Autocomplete is the same as the standard HTML5 markup (). + Wrapping the HTMLElement as a Montage Autocomplete component adds Data Binding support for all + writable attributes of this element and allows the Developer to provide suggestions to the user based + on the entered value. + + Popup + Support HTMLElement and Montage Component for the anchor property + Change willPositionPopup(popup, anchor, anchorPosition) to willPosition(popup, defaultPosition). + Change autoDismiss to autoHide + Support autoHide only for non-modal popups + Support string values (eg: 20px, 30%) for position parameters. If a number is provided, default to 'px'. + +- **Template** + + ``instantiateWithDocument(document, callback)`` method is added to use plain html templates without an owner. + + Backwards compatibility for id attribute based references in serilization is removed, you should now use the + data-montage-id attribute. + +# v0.8.0 + +- **Data binding shorthand in serializations** + + This release introduces a new shorthand syntax for declaring data bindings in a serialization. The new syntax + includes an arrow symbol that indicates the source of the binding and whether its one-way or two-way.The symbol can + take one of the following forms: + + `<-` – One-way data binding, bound object on right + + `->` – One-way data binding, bound object on left + + `<<->` – Two-way data binding, bound object on right + + `<->>` – Two-way data binding, bound object on left + + Example: + + ```javascript + { + "inputText": { + "prototype": "textfield", + "bindings": { + "value": {"<-": "@slider.value"} + } + } + } + ``` + +- **RichTextEditor component** + + RichTextEditor is a lightweight component that provides basic HTML editing capability. It is wrapper around the + HTML5 contentEditable attribute, and depends on the browser’s level of support of the execCommand() method. The + RichTextEditor lets you set, on a specific text selection range, various formatting attributes including text and + font styles, colors, justification, list style and paragraph indent level. It also supports drag-and-drop of images, + plain text, or HTML fragments between two HTML documents, or the desktop and the document. Images can also be + resized within the editor. + +- **Flow component** + + This release introduces the first drop of the Flow component. Flow is UI component that allows the design of + scrollable 3D-layouts. Flow is useful for creating a wide range of visual interfaces from 3D carousels to common + vertical and horizontal scrollable lists. + +- **Extending components** + + There are three options to extend a component’s template: + + 1. If the extended component doesn’t wish to introduce changes in the template, the component can set its + templateModuleId property to point to the parent module’s template. + 2. Create a new template that will completely redefine the markup of the component with no relation to the original + template. + 3. Set the extends property of the template that points to the template to be imported and where. This is similar + to the “decorator” pattern of the proposed Web Components feature. This approach is useful when the component + needs to add additional CSS data, or reuse the original markup. The template object will be accessible through + the template label of the serialization. + +- **Auto packaging** + + Many applications will initially only use their own modules and those provided by the Montage package. + As long as that’s the case, you do not need to make a package.json; just put the data-auto-package attribute on + your Montage script tag. + +- **Pop-up component updates** + + The Popup component API has been updated to provide better support for popup positioning. + + - `anchor` The HTMLElement or Montage component below which the popup will be anchored on the page. To specify a + custom position for a popup, use a delegate (see below). If an anchor is not provided, the position property is + used, if provided. If no positioning support is provided, the Popup is displayed at the center of the screen by + default. + - `position` An object with properties named top, left, bottom and right that specify the position of the popup. + This property is used to position the popup if no anchor is specified. + - `delegate` An object that defines methods that are invoked by the Popup component. The only delegate method + currently supported is willPositionPopup(). This method must return an object with any of the following + properties: top, left, bottom or right. Values assigned to these properties must strings in valid + CSS units (“10px” or “50%”). + - `content` The Montage component that will be displayed in the popup. + - `modal` If set to true, the popup is rendered with a mask underneath. A non-modal popup is hidden when the user + presses the Esc key, or clicks outside of the popup. The developer is responsible for hiding a modal popup. + Default is false. Modal popups never auto-hide themselves. + - `autoHide` Optional. The popup will be automatically hidden after the specified number of milliseconds. This + property has no effect on modal popups. + + +# v0.7.0 + +- **Adding `ownerComponent` property to the Component.** + When the template is deserialized we populate the ownerComponent property of any component created within it's + serialization with the owner of the template. +- **Adding `setElementWithParentComponent` function on the Component.** + This formalizes the API to set a detached element to a component. The caller is responsible for appending the + element to the DOM before prepareForDraw is called on the callee. +- **Serialization changes** + - Specifying types and references + 1. Changed the way we specify an object by merging the module id and the name into only one string using + `"[]"` (e.g.: `"montage/ui/button.reel[Button]"`). When the name matches the last part of the module + id then it's automatically inferred and there's no need to provide it. The last part of the module id is considered + to be the last path component (e.g.: `"event-name"` in `"montage/event/event-name"`) transformed to CamelCase with + dashes stripped (e.g.: `"EventName"`). When the last path component ends with `".reel"` then the component is + considered without its `".reel"` suffix (e.g: `"RadioButton"` for `"montage/ui/radio-button.reel"`). Under these new + rules we reach the following equivalence: `"montage/ui/button.reel[Button]" === "montage/ui/button.reel"`. + 2. The possibility to point to an object instead of just being able to declare an instance of a specific prototype. + We were using the pair `module/name` to declare an instance of that prototype, with this new change only one + property is needed -- `prototype` -- using the rules defined in 1). If, instead of a new object, we just want to + point to an existing one the new `object` property name should be used. + + In practice this means a change from: + + ```javascript + { + "label": { + "module": "montage/ui/button.reel", + "name": "Button", + "properties": {...} + } + } + ``` + to + ```javascript + { + "label": { + "prototype": "montage/ui/button.reel", + "properties": {...} + } + } + ``` + - Serialization labels are now used as the value of the identifier property by default. + +- **Properties with a leading underscore in their name are now {enumerable: false} by default.** + i.e. defining a property as + ```javascript + _name: {value: null} + ``` + is equivalent to doing + ```javascript + _name: {value: null, enumerable:false} + ``` +- **Components** + - Repetition: Adding indexMap property to provide the necessary underpinnings for large data handling. + - SelectInput: Adding values and value property to be able to bind directly to the value of the selected option(s) + - Scroller: Replaces Scrollview. Now uses the Translate composer. + - Scrollview: _deprecated_ +- **Browser Support** + - Better support for Firefox + - Better support for Opera + +# v0.6.0 + +- Native Controls based on HTML5 input elements +- Composers to add aggregate events and time dependent behaviors as is needed for scroll momentum and bouncing. +- Component Contents to make it easier to use existing wrapper components such as repetition in your own. +- Condition Component API improvements to lazily load parts of your component tree and easily show or hide sections of your page. +- Misc bug fixes and optimizations mostly in the loading of the app and the require/package system.. + +# v0.5.0 + +- Added UndoManager. + It stores actions that can later be undone or re-done, + example of usage has been added to the PhotoFX example. +- Custom Events now propagate through component tree by default if + dispatched on a component. +- Addition of ``.didCreate()`` + - ``MyPrototype.create()`` or ``Montage.create(MyPrototype)`` is for + **instantiation**. From v0.5, this will call _didCreate()_ on each + begotten instance implicitly for **initialization** purposes. It does + not accept arguments and occurs before the serialization has an + opportunity to set initial properties. + All parameterized initialization must occur as the result of + observing setters on those properties. + - ``Montage.create(MyPrototype, {property descriptors})`` is for + **subtyping**. This will not call _didCreate()_. +- All components now have the ability to easily dispatch an action event. +- Condition component improvements +- PhotoFX example improvements +- Added flow component for reference purposes, the API is not ready yet. +- IE10 Fixes. +- Bug fixes in: + - Packaging for require + - Dependent properties listening + - Promises + - Selection in ArrayController + - Scrollview + - Various other areas + +# v0.4.0 + +- Packaging: + - All applications must be packages with ``package.json`` files. + - The ``data-package`` attribute is no longer necessary and + defaults to ``"./"``, meaning that the application’s + ``package.json`` is in the same directory as the containing + HTML. ``data-package`` may be overridden with a relative URL to + the directory containing ``package.json``, and must contain a + final forward-slash. + - The default directory for packaged modules is now the root of + the package instead of ``lib``. This can be altered from its + default with the ``directories.lib`` property in + ``package.json``. + - require("x.reel") now loads require("x.reel/x.js"). + - Removed the run-time ``mappings`` configuration for module + loading. Use ``mappings`` in ``package.json`` instead. + - require("my-package/my-module") no longer maps to + require("my-module") inside of my-package. + require("my-package") still maps to require("") which maps to + require("my-main-module"). + - You can now require a module with the same name as your package, + provided that you do not override the "main" property of your + package. Previously, there was no default main property. +- Serializer: + - Each package has its own module identifier name space, so + serializations must be scoped to a package. To identify the + containing package, ``Serializer`` must be constructed with a + ``require`` argument if the serialization contains any Montage + objects. + - Added ``Serializer.initWithRequire`` + - Added ``Serializer.initWithStringAndRequire`` + - Swapped the names ``serialize`` and ``serializeObject``. + ``serialize({labels: objects})`` and + ``serializeObject(object)`` are the new signatures. +- Deserializer: + - Swapped ``deserialize`` and ``deserializeObject``. + - Renamed ``deserializeObjectsWithLabels`` to + ``deserializeWithInstances``. + - Consolidated ``deserializeObjectsWithElement`` and + ``deserializeObjectsWithElementAndLabels`` into + ``deserializeWithInstancesAndElementForDocument``. + - Consolidated ``deserializeObjectsWithDocument`` and + ``deserializeObjectsWithDocumentAndLabels`` into + ``deserializeWithInstancesAndDocument``. +- Some names have changed to distinguish reels from templates. A + reel is a directory that encapsulates all parts of a reusable + component. The name only exists as the ``.reel`` extension for + such directories. A template is an HTML file for a reel. + - Renamed ``hasReel`` to ``hasTemplate``. + - Renamed ``reelDidLoad`` to ``templateDidLoad`` + - Renamed ``deserializedFromReel`` to ``deserializedFromTemplate`` + - Renamed ``loadReel`` to ``loadTemplate`` + - Renamed ``reelWithModuleId`` to ``templateWithModuleId`` + - Renamed ``reelWithComponent`` to ``templateWithComponent`` +- Components must be styled with CSS classes instead of element + identifiers. Identifiers for component elements are no longer + generated, and were never generated in a fashion that was consistent + after structural changes. +- Removed ``prototypeMethod``, ``prototypeSet`` and ``prototypeGet`` + convenience methods, any code using these methods will have to be + updated to either use the needed prototype directly or use + ``Object.getPrototypeOf(...)`` + +# v0.3.1 + +- Added copyright notices to HTML, CSS, and JavaScript. +- Normalized files to Unix text encoding. +- Fixed auto-complete textfield. +- Added progress bar test. +- Draw cycle improvements. +- Checkbox animations. + +# v0.3.0 + +- Module namespace restructured. + +# v0.2.0 + +- First version before breaking changes. +- New serialization format introduced. + diff --git a/node_modules/montage/core/change-notification.js b/node_modules/montage/core/change-notification.js index bfe5ff2d..3f6d884e 100644 --- a/node_modules/montage/core/change-notification.js +++ b/node_modules/montage/core/change-notification.js @@ -205,6 +205,7 @@ var ChangeNotificationDescriptor = Montage.create(Montage, { dependentDescriptorsIndex: {value: null}, mutationDependencyIndex: {value: null}, mutationListenersCount: {value: 0}, + observedDependentProperties: {value: null}, initWithTargetPath: { value: function(target, path) { @@ -216,7 +217,8 @@ var ChangeNotificationDescriptor = Montage.create(Montage, { }, registerListener: { value: function(listener, beforeChange, mutation) { - var listenerKey = listener.uuid; + var listenerKey = listener.uuid, + listeners; if (beforeChange) { listeners = this.willChangeListeners; @@ -247,7 +249,8 @@ var ChangeNotificationDescriptor = Montage.create(Montage, { }, unregisterListener: { value: function(listener, beforeChange) { - var listenerKey = listener.uuid; + var listenerKey = listener.uuid, + listeners; if (beforeChange) { listeners = this.willChangeListeners; @@ -533,7 +536,14 @@ var ChangeNotificationDescriptor = Montage.create(Montage, { var listener, dependentDescriptorsIndex = this.dependentDescriptorsIndex, dependenciesIndex = notification._dependenciesIndex, - isMutationNotification; + isMutationNotification, + uuid = this.uuid; + + // we need to stop circular property dependencies. + // e.g.: object.foo depends on object.bar and object.bar depends on object.foo. + if (notification[uuid]) { + return; + } // TODO: maybe I should replicate this if (arguments.length < 2) { @@ -563,7 +573,9 @@ var ChangeNotificationDescriptor = Montage.create(Montage, { if (dependentDescriptorsIndex) { notification._dependenciesIndex = dependentDescriptorsIndex[key]; } + notification[uuid] = true; listener.listenerFunction.call(listener.listenerTarget, notification); + notification[uuid] = false; } } notification._dependenciesIndex = dependenciesIndex; @@ -681,12 +693,14 @@ var ObjectPropertyChangeDispatcherManager = Object.create(null, { notification; if (!descriptor) { + originalSetter.apply(this, arguments); return; } previousValue = this[propertyName]; if (previousValue === value) { - // Nothing to do here + originalSetter.apply(this, arguments); + // Nothing more to do here return; } @@ -756,12 +770,13 @@ Object.defineProperty(Object.prototype, "dispatchPropertyChange", { for (i = 0; i < callbackArgumentIndex; i++) { iProperty = arguments[i]; descriptor = ChangeNotification.getPropertyChangeDescriptor(this, iProperty); - if (descriptor) { + if (descriptor && !descriptor.isActive) { notification = Object.create(PropertyChangeNotification); observedProperties.push(iProperty, descriptor, notification); notification.target = this; notification.minus = this.getProperty(iProperty); + descriptor.isActive = true; descriptor.handleWillChange(notification); } } @@ -775,6 +790,7 @@ Object.defineProperty(Object.prototype, "dispatchPropertyChange", { notification.plus = this.getProperty(iProperty); descriptor.handleChange(notification); + descriptor.isActive = false; } } @@ -818,9 +834,19 @@ Object.defineProperty(Object.prototype, "addPropertyChangeListener", { // TODO should adding a dispatcher on a dependent property also be subjected to checking for // automaticDispatchPropertyChangeListener, probably if (dependentPropertyPaths) { + + if (!descriptor.observedDependentProperties) { + descriptor.observedDependentProperties = {}; + } + for (i = 0; (iPath = dependentPropertyPaths[i]); i++) { - this.addPropertyChangeListener(iPath, descriptor, beforeChange, false); - descriptor.registerDependency(this, iPath, null); + + if (!descriptor.observedDependentProperties[iPath]) { + descriptor.observedDependentProperties[iPath] = true; + + this.addPropertyChangeListener(iPath, descriptor, beforeChange, false); + descriptor.registerDependency(this, iPath, null); + } } } } @@ -861,16 +887,16 @@ var PrefixedPropertyDescriptor = { configurable: true }; -var PropertyChangeNotification = exports.PropertyChangeNotification = Object.create(null, { - phase: {writable: true, value: null}, - target: {writable: true, value: null}, - propertyPath: {writable: true, value: null}, - minus: {writable: true, value: null}, - plus: {writable: true, value: null}, - currentTarget: {writable: true, value: null}, - currentPropertyPath: {writable: true, value: null}, - isMutation: {writable: true, value: false} -}); +var PropertyChangeNotification = exports.PropertyChangeNotification = { + phase: null, + target: null, + propertyPath: null, + minus: null, + plus: null, + currentTarget: null, + currentPropertyPath: null, + isMutation: false +}; var ChangeNotificationDispatchingArray = exports.ChangeNotificationDispatchingArray = []; var _index_array_regexp = /^[0-9]+$/; @@ -878,13 +904,15 @@ var _unobservable_array_property_regexp = /^length$/; Object.defineProperty(Array.prototype, "addPropertyChangeListener", { value: function(path, listener, beforeChange, ignoreMutation) { var listenChange, listenIndexChange, listenFunctionChange, - descriptor; + descriptor, + dotIndex; if (!listener) { return; } - if (path == null || path.indexOf(".") == -1) { + if (path == null || (dotIndex = path.indexOf(".")) == -1) { + if (_unobservable_array_property_regexp.test(path)) { return; @@ -909,6 +937,12 @@ Object.defineProperty(Array.prototype, "addPropertyChangeListener", { } } else { Object.prototype.addPropertyChangeListener.apply(this, arguments); + // We need to do this because the Object.prototype.addPropertyChangeListener doesn't create dependencies + // for no-dot paths, but in array array.path will have dependencies when path is not an index or null. + if (dotIndex == -1) { + descriptor = ChangeNotification.getPropertyChangeDescriptor(this, path); + descriptor.setupDependencies(this, path, beforeChange, !ignoreMutation); + } } } }); @@ -918,6 +952,7 @@ Object.defineProperty(ChangeNotificationDispatchingArray, "_dispatchArrayChangeN configurable: false, value: function(methodName, methodArguments, index, howManyToRemove, newValues) { var descriptor = ChangeNotification.getPropertyChangeDescriptor(this, null), + result, notification, indexNotification = Object.create(PropertyChangeNotification), delta, @@ -1235,6 +1270,7 @@ Object.defineProperty(ChangeNotificationDispatchingArray, "sort", { indexNotification, oldValue, newValue, + indexArray, _sortIndexArray, _sortIndexArrayLength; @@ -1340,6 +1376,7 @@ Object.defineProperty(Object.prototype, "__debugChangeNotifications__", { configurable: false, value: function() { var registry = ChangeNotification._descriptorsRegistry[this.uuid], + path, log = []; if (registry) { @@ -1367,7 +1404,12 @@ Object.defineProperty(Object.prototype, "__debugChangeNotifications__", { var listenerFunctionName = changeListeners[key].listenerFunctionName; var info = Montage.getInfoForObject(listenerTarget); if (info.objectName === "PropertyChangeBindingListener") { - bindings.push("\"" + listenerTarget.bindingPropertyPath + "\" @ " + Montage.getInfoForObject(listenerTarget.bindingOrigin).objectName + "(", listenerTarget.bindingOrigin, ")"); + if (listenerTarget.bindingOrigin === this && listenerTarget.bindingPropertyPath === path) { + bindings.push("\"" + listenerTarget.targetPropertyPath + "\" @ " + (Montage.getInfoForObject(listenerTarget.target).objectName || "") + "(", listenerTarget.target, ")"); + } else { + bindings.push("\"" + listenerTarget.bindingPropertyPath + "\" @ " + (Montage.getInfoForObject(listenerTarget.bindingOrigin).objectName || "") + "(", listenerTarget.bindingOrigin, ")"); + } + bindings.push("\n\t "); } } diff --git a/node_modules/montage/core/converter/bytes-converter.js b/node_modules/montage/core/converter/bytes-converter.js index 1626f352..605d8546 100755 --- a/node_modules/montage/core/converter/bytes-converter.js +++ b/node_modules/montage/core/converter/bytes-converter.js @@ -78,7 +78,8 @@ exports.BytesConverter = Montage.create(Converter, /** @lends module:montage/cor @default {Number} 2 */ decimals: { - value: 2 + value: 2, + serializable: true }, /** Converts the specified value to byte format. diff --git a/node_modules/montage/core/converter/converter.js b/node_modules/montage/core/converter/converter.js index a23d2702..97e1a5f2 100755 --- a/node_modules/montage/core/converter/converter.js +++ b/node_modules/montage/core/converter/converter.js @@ -111,7 +111,8 @@ var Converter = exports.Converter = Montage.create(Montage, /** @lends module:mo @default {Boolean} true */ allowPartialConversion: { - value: true + value: true, + serializable: true }, /** diff --git a/node_modules/montage/core/converter/currency-converter.js b/node_modules/montage/core/converter/currency-converter.js index bba81dc1..10f6f068 100755 --- a/node_modules/montage/core/converter/currency-converter.js +++ b/node_modules/montage/core/converter/currency-converter.js @@ -44,7 +44,8 @@ exports.CurrencyConverter = Montage.create(NumberConverter, /** @lends module:mo @default {String} '$' */ currency: { - value: '$' + value: '$', + serializable: true }, /** @@ -52,7 +53,8 @@ exports.CurrencyConverter = Montage.create(NumberConverter, /** @lends module:mo @default {Number} 2 */ decimals: { - value: 2 + value: 2, + serializable: true }, /** @@ -60,7 +62,8 @@ exports.CurrencyConverter = Montage.create(NumberConverter, /** @lends module:mo @default {Boolean} false */ useParensForNegative: { - value: false + value: false, + serializable: true }, /** diff --git a/node_modules/montage/core/converter/date-converter.js b/node_modules/montage/core/converter/date-converter.js index 7b062925..e200333b 100755 --- a/node_modules/montage/core/converter/date-converter.js +++ b/node_modules/montage/core/converter/date-converter.js @@ -2539,7 +2539,8 @@ var DateValidator = exports.DateValidator = Montage.create(Validator,/** @lends @default {Date} 'MM/dd/yyyy' */ pattern: { - value: 'MM/dd/yyyy' + value: 'MM/dd/yyyy', + serializable: true }, /** @function @@ -2576,7 +2577,8 @@ var DateConverter = exports.DateConverter = Montage.create(Converter,/** @lends @default {Boolean} true */ allowPartialConversion: { - value: false + value: false, + serializable: true }, /** @@ -2584,7 +2586,8 @@ var DateConverter = exports.DateConverter = Montage.create(Converter,/** @lends @default {Function} Montage.create(DateValidator) */ validator: { - value: Montage.create(DateValidator) + value: Montage.create(DateValidator), + serializable: true }, // valid fn values are: @@ -2593,7 +2596,8 @@ var DateConverter = exports.DateConverter = Montage.create(Converter,/** @lends @default {Date} 'MM/dd/yyyy' */ pattern: { - value: 'MM/dd/yyyy' + value: 'MM/dd/yyyy', + serializable: true }, /** @function diff --git a/node_modules/montage/core/converter/number-converter.js b/node_modules/montage/core/converter/number-converter.js index f440f9a2..3eb3af64 100755 --- a/node_modules/montage/core/converter/number-converter.js +++ b/node_modules/montage/core/converter/number-converter.js @@ -212,7 +212,8 @@ var NumberValidator = exports.NumberValidator = Montage.create(Validator, /** @l @default {Boolean} true */ allowFloat: { - value: true + value: true, + serializable: true }, /** @@ -220,7 +221,8 @@ var NumberValidator = exports.NumberValidator = Montage.create(Validator, /** @l @default {Boolean} true */ allowNegative: { - value: true + value: true, + serializable: true }, /** @@ -265,7 +267,8 @@ var NumberConverter = exports.NumberConverter = Montage.create(Converter, /** @l */ // do not allow partial conversion allowPartialConversion: { - value: false + value: false, + serializable: true }, /** @type {Function} @@ -281,14 +284,16 @@ var NumberConverter = exports.NumberConverter = Montage.create(Converter, /** @l */ // valid fn values are: shorten: { - value: null + value: null, + serializable: true }, /** @type {Property} @default {Number} 2 */ decimals: { - value: 2 + value: 2, + serializable: true }, /** @@ -296,7 +301,8 @@ var NumberConverter = exports.NumberConverter = Montage.create(Converter, /** @l @default {Number} null */ round: { - value: null + value: null, + serializable: true }, /** @@ -306,19 +312,21 @@ var NumberConverter = exports.NumberConverter = Montage.create(Converter, /** @l value: /(\d+)(\d{3})/ }, - /** + /** @type {Property} @default {Boolean} true */ allowFloat: { - value: true + value: true, + serializable: true }, - /** + /** @type {Property} @default {Boolean} true */ allowNegative: { - value: true + value: true, + serializable: true }, // credit: sugar.js - https://github.com/andrewplummer/Sugar diff --git a/node_modules/montage/core/converter/upper-case-converter.js b/node_modules/montage/core/converter/upper-case-converter.js index 33ec0055..f421fd6d 100755 --- a/node_modules/montage/core/converter/upper-case-converter.js +++ b/node_modules/montage/core/converter/upper-case-converter.js @@ -4,7 +4,7 @@ (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. */ /** - @module montage/core/converter/upper-case-converter + @module montage/core/converter/upper-case-converter @requires montage/core/core @requires montage/core/converter/converter */ diff --git a/node_modules/montage/core/core.js b/node_modules/montage/core/core.js index 80597dce..26733c94 100755 --- a/node_modules/montage/core/core.js +++ b/node_modules/montage/core/core.js @@ -21,6 +21,7 @@ var ATTRIBUTE_PROPERTIES = "AttributeProperties", PROTO = "__proto__", VALUE = "value", ENUMERABLE = "enumerable", + DISTINCT = "distinct", SERIALIZABLE = "serializable", MODIFY = "modify"; @@ -63,10 +64,6 @@ Object.defineProperty(Montage, "create", { var newObject = Object.create(typeof aPrototype === "undefined" ? this : aPrototype); - if (newObject._dependenciesForProperty) { - newObject._dependencyListeners = {}; - } - if (typeof newObject.didCreate === "function") { newObject.didCreate(); } @@ -80,7 +77,7 @@ Object.defineProperty(Montage, "create", { } }); -var extendedPropertyAttributes = [SERIALIZABLE, MODIFY]; +var extendedPropertyAttributes = [SERIALIZABLE]; // Extended property attributes, the property name format is "_" + attributeName + "AttributeProperties" /** @@ -112,13 +109,21 @@ extendedPropertyAttributes.forEach(function(name) { Object.defineProperty(Montage, "defineProperty", { value: function(obj, prop, descriptor) { - var dependencies = descriptor.dependencies; + + var dependencies = descriptor.dependencies, + isValueDescriptor = (VALUE in descriptor); + + if (DISTINCT in descriptor && !isValueDescriptor) { + throw ("Cannot use distinct attribute on non-value property '" + prop + "'"); + } + + //reset defaults appropriately for framework. if (PROTO in descriptor) { - descriptor.__proto__ = (VALUE in descriptor ? (typeof descriptor.value === "function" ? _defaultFunctionValueProperty : _defaultObjectValueProperty) : _defaultAccessorProperty); + descriptor.__proto__ = (isValueDescriptor ? (typeof descriptor.value === "function" ? _defaultFunctionValueProperty : _defaultObjectValueProperty) : _defaultAccessorProperty); } else { var defaults; - if (VALUE in descriptor) { + if (isValueDescriptor) { if (typeof descriptor.value === "function") { defaults = _defaultFunctionValueProperty; } else { @@ -153,10 +158,6 @@ Object.defineProperty(Montage, "defineProperty", { getAttributeProperties(obj, SERIALIZABLE)[prop] = descriptor.serializable; } - if (MODIFY in descriptor) { - getAttributeProperties(obj, MODIFY)[prop] = descriptor.modify; - } - //this is added to enable value properties with [] or Objects that are new for every instance if (descriptor.distinct === true && typeof descriptor.value === "object") { (function(prop,internalProperty, value, obj) { @@ -587,6 +588,11 @@ Object.defineProperty(Object.prototype, "uuid", { } }); +Montage.defineProperty(Montage, "identifier", { + value: null, + serializable: true +}); + /** Returns true if two objects are equal, otherwise returns false. @function module:montage/core/core.Montage#equals @@ -599,6 +605,8 @@ Object.defineProperty(Montage, "equals", { } }); + + /* * If it exists this method calls the method named with the identifier prefix. * Example: If the name parameter is "shouldDoSomething" and the caller's identifier is "bob", then diff --git a/node_modules/montage/core/deserializer.js b/node_modules/montage/core/deserializer.js index 2b3ed64e..3c0aa049 100755 --- a/node_modules/montage/core/deserializer.js +++ b/node_modules/montage/core/deserializer.js @@ -246,7 +246,7 @@ var Deserializer = Montage.create(Montage, /** @lends module:montage/core/deseri object = stack[ix-1], desc = stack[ix]; - this._deserializeProperties(object, desc.properties); + this._deserializeProperties(object, desc.properties, true); } }, @@ -331,9 +331,22 @@ var Deserializer = Montage.create(Montage, /** @lends module:montage/core/deseri @param {Object} object The target of the properties. @param {Array} properties The property names to be deserialized. */ - deserializePropertiesForObject: {value: function(object, properties) { - for (var key in properties) { - object[key] = properties[key]; + deserializePropertiesForObject: {value: function(object, properties, checkSerializableAttribute) { + if (checkSerializableAttribute) { + for (var key in properties) { + if (!Montage.getPropertyAttribute(object, key, "serializable")) { + if (Object.getPropertyDescriptor(object, key)) { + console.warn("Unserializable property \"" + key + "\" found in the serialization of " + (object._montage_metadata ? object._montage_metadata.objectName : object) + " (" + (this._origin || window.location) + ")"); + } else { + console.warn("Nonexistent (and therefore unserializable) property \"" + key + "\" found in the serialization of " + (object._montage_metadata ? object._montage_metadata.objectName : object) + " (" + (this._origin || window.location) + ")"); + } + }; + object[key] = properties[key]; + } + } else { + for (var key in properties) { + object[key] = properties[key]; + } } }}, @@ -760,7 +773,7 @@ var Deserializer = Montage.create(Montage, /** @lends module:montage/core/deseri desc._units = {}; self._customDeserialization(object, desc); } else { - self._deserializeProperties(object, desc.properties); + self._deserializeProperties(object, desc.properties, true); } } @@ -784,7 +797,7 @@ var Deserializer = Montage.create(Montage, /** @lends module:montage/core/deseri } else if ("@" in value) { type = "reference"; value = value["@"]; - } else if (typeof value["->"] === "object") { + } else if ("->" in value) { type = "function"; value = value["->"]; } else if ("." in value && Object.keys(value).length === 1) { @@ -1089,13 +1102,13 @@ var Deserializer = Montage.create(Montage, /** @lends module:montage/core/deseri /** @private */ - _deserializeProperties: {value: function(object, properties) { + _deserializeProperties: {value: function(object, properties, checkSerializableAttribute) { if (object.deserializeProperties) { this._pushContextObject(properties); object.deserializeProperties(this); this._popContextObject(); } else { - this.deserializePropertiesForObject(object, properties); + this.deserializePropertiesForObject(object, properties, checkSerializableAttribute); } }}, diff --git a/node_modules/montage/core/event/action-event-listener.js b/node_modules/montage/core/event/action-event-listener.js index 4fc75f64..c8981f1d 100755 --- a/node_modules/montage/core/event/action-event-listener.js +++ b/node_modules/montage/core/event/action-event-listener.js @@ -21,7 +21,8 @@ var ActionEventListener = exports.ActionEventListener = Montage.create(Montage, @default {Event handler} null */ handler: { - value: null + value: null, + serializable: true }, /** @@ -30,7 +31,8 @@ var ActionEventListener = exports.ActionEventListener = Montage.create(Montage, @default {Event handler} null */ action: { - value: null + value: null, + serializable: true }, /** diff --git a/node_modules/montage/core/event/binding.js b/node_modules/montage/core/event/binding.js index 2e226372..c31ee7f9 100755 --- a/node_modules/montage/core/event/binding.js +++ b/node_modules/montage/core/event/binding.js @@ -82,91 +82,57 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje this.deferredValueTarget = ""; } }, - handleChange: {value: function(event) { - var targetPropertyPath = this.targetPropertyPath, - target = this.target, - localNewValue = event.plus, - localPrevValue = event.minus, - localTarget = event.target, - boundObjectValue, - sourceObjectValue, - dotIndex, - nextPathComponent, - atSignIndex, - baseType, - bindingDescriptor, - bindingOrigin = this.bindingOrigin, - leftOriginated, - changeOriginPropertyPath = null, - exploredPath, - valueChanged; - - if (target !== bindingOrigin) { - //the left and the right are different objects; easy enough - leftOriginated = event.target === bindingOrigin; - } else { - //otherwise, they're the same object; time to try and figure out which "side" the event came from - // TODO this is a very weak check that relies on the bindingOrigin using a property and not a full propertyPath - leftOriginated = event.propertyName === this.bindingPropertyPath; - } - - if (leftOriginated) { - // This change event targeted the left side of the binding; try to push to the right side + handleChange:{ + value:function (notification) { + + var bindingOriginTriggeredChange, + // Left + bindingOrigin = this.bindingOrigin, + bindingOriginPropertyPath = this.bindingPropertyPath, + bindingOriginValue = bindingOrigin.getProperty(bindingOriginPropertyPath), + // Right + boundObject = this.target, + boundObjectPropertyPath = this.targetPropertyPath, + boundObjectValue; + + // Determine if binding triggered by change on bindingOrigin + if (boundObject !== bindingOrigin) { + // the origin and bound object are different objects; easy enough + bindingOriginTriggeredChange = notification.currentTarget === bindingOrigin; + } else { + // otherwise, if the objects are the same the propertyPaths must differ + bindingOriginTriggeredChange = notification.currentPropertyPath === bindingOriginPropertyPath; + } - if (!bindingOrigin.setProperty.changeEvent) { + if (bindingOriginTriggeredChange) { + // This change notification targeted the left side of the binding; try to push to the right side - sourceObjectValue = localNewValue; + // If this notification was triggered by the right-to-left value push; don't bother setting + // the value on the left side, that's where all this value changing started + // (The original right-to-left push installs this changeEvent key on the setProperty function) + if (bindingOrigin.setProperty.changeEvent) { + return; + } + // TODO should not assume revert is available if (this.bindingDescriptor.converter) { - sourceObjectValue = this.bindingDescriptor.converter.revert(sourceObjectValue); + bindingOriginValue = this.bindingDescriptor.converter.revert(bindingOriginValue); } - // If this event was triggered by the right-to-left- value push; don't bother setting - // the value on the left side, that's where all this value changing started - // (The original right-to-left push installs this changeEvent key on the setProperty function) if (this.bindingOriginValueDeferred === true || bindingOrigin._bindingsDisabled) { - this.deferredValue = sourceObjectValue; + this.deferredValue = bindingOriginValue; this.deferredValueTarget = "target"; } else { this.bindingOriginChangeTriggered = true; // Set the value on the RIGHT side now - this.target.setProperty(this.targetPropertyPath, sourceObjectValue); + boundObject.setProperty(boundObjectPropertyPath, bindingOriginValue); this.bindingOriginChangeTriggered = false; } - } - - } 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 - // the event target, which just changed, could be any of the objects along the path, but from here on we want to - // treat the event as "change@fullTargetPropertyPath" so adjust the event we have to reflect that - if (this.target && targetPropertyPath) { - event.target = target; - event.propertyPath = targetPropertyPath; - - event.plus = target.getProperty(targetPropertyPath); - - // If the newValue and the storedPreviousValue are the same, this was a mutation on that object - // we want to show the prevValue that came along from the event lest we point - // somebody a reference to the same array as the prevValue and newValue - if (!Array.isArray(this.previousTargetPropertyPathValue) && event.plus !== this.previousTargetPropertyPathValue) { - event.minus = this.previousTargetPropertyPathValue; - } - - } else { - // TODO I'm not sure when this would happen.. - event.target = this; - } - - // 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 = this.targetPropertyPath; - //For bindings, start the right-to-left value push - if (event.target === this.target && this.bindingPropertyPath && bindingOrigin) { - //console.log("@ % @ % @ % @ % @ % Binding Worked!!"); + } else if (!this.bindingOriginChangeTriggered) { - boundObjectValue = event.plus; + // Start the right-to-left value push + boundObjectValue = boundObject.getProperty(boundObjectPropertyPath); if (this.bindingDescriptor.boundValueMutator) { boundObjectValue = this.bindingDescriptor.boundValueMutator(boundObjectValue); @@ -174,75 +140,23 @@ var PropertyChangeBindingListener = exports.PropertyChangeBindingListener = Obje boundObjectValue = this.bindingDescriptor.converter.convert(boundObjectValue); } - // 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 (boundObjectValue !== bindingOriginValue) { 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; + // Make the original notification available to the setter + bindingOrigin.setProperty.changeEvent = notification; // Set the value on the LEFT side now - this.bindingOrigin.setProperty(this.bindingPropertyPath, boundObjectValue); - this.bindingOrigin.setProperty.changeEvent = null; + bindingOrigin.setProperty(bindingOriginPropertyPath, boundObjectValue); + bindingOrigin.setProperty.changeEvent = null; } } - } - // Otherwise, there was probably a listener for a change at this path that was not a part of some binding - // so distribute the event to the original listener - // TODO this is not as full featured as the EventManager event distribution so it may differ, which is bad - else if (this.originalListener) { - if (this.originalListenerIsFunction) { - this.originalListener.call(this.target, event); - } else { - this.originalListener.handleEvent(event); - } - } - - // Update the stored value of the propertyPath - this.previousTargetPropertyPathValue = event.plus; - - // TODO I'm not exactly sure why this happens here, or really why it does in general - event.minus = localPrevValue; - event.plus = localNewValue; - event.target = localTarget; - if (localPrevValue) { - - // Determine where along the property path the change originated from so we know how to build the path - // to stop observing on things that were removed - //TODO extract this into a "obj.getPathOfObjectAlongPropertyPath" method or something with proper tests - exploredPath = ""; - this.target.getProperty(this.targetPropertyPath, null, null, function(value, currentPathComponent, result) { - - if (changeOriginPropertyPath) { - return; - } - - exploredPath += "." + currentPathComponent; - - if (result === event.target) { - changeOriginPropertyPath = exploredPath.replace(/^\./, ""); - } - }); + // Update the stored value of the propertyPath + this.previousTargetPropertyPathValue = boundObjectValue; } } - - targetPropertyPath = null; - target = null; - localNewValue = null; - localPrevValue = null; - localTarget = null; - dotIndex = null; - nextPathComponent = null; - atSignIndex = null; - baseType = null; - bindingDescriptor = null; - bindingOrigin = null; - } } }); @@ -334,7 +248,7 @@ var BindingDescriptor = exports.BindingDescriptor = Montage.create(Montage, /** var serialization = {}; serializer.addObjectReference(this.boundObject); - serialization[this.oneway ? "<-" : "<<->"] = "@" + serializer.getObjectLabel(this.boundObject) + "." + this.boundObjectPropertyPath; + serialization[this.oneway ? "<-" : "<->"] = "@" + serializer.getObjectLabel(this.boundObject) + "." + this.boundObjectPropertyPath; serialization.deferred = this.deferred; serialization.converter = this.converter; @@ -356,26 +270,26 @@ Deserializer.defineDeserializationUnit("bindings", function(object, bindings, de dotIndex; if (!("boundObject" in binding)) { - var targetPath = binding["<-"] || binding["->"] || binding["<->>"] || binding["<<->"]; + var targetPath = binding["<-"] || binding["<->"] || binding["<<->"]; - if (targetPath[0] !== "@") { - logger.error("Invalid binding syntax '" + targetPath + "', should be in the form of '@label.path'."); - throw "Invalid binding syntax '" + targetPath + "'"; + if ("<<->" in binding) { + console.warn("WARNING: <<-> in bindings is deprectated, use <-> only, please update now.") } - if ("->" in binding || "<->>" in binding) { - binding.boundObject = object; - binding.boundObjectPropertyPath = sourcePath; - dotIndex = targetPath.indexOf("."); - object = deserializer.getObjectByLabel(targetPath.slice(1, dotIndex)); - sourcePath = targetPath.slice(dotIndex+1); + if (targetPath) { + if (targetPath[0] !== "@") { + logger.error("Invalid binding syntax '" + targetPath + "', should be in the form of '@label.path'."); + throw "Invalid binding syntax '" + targetPath + "'"; + } } else { - dotIndex = targetPath.indexOf("."); - binding.boundObject = deserializer.getObjectByLabel(targetPath.slice(1, dotIndex)); - binding.boundObjectPropertyPath = targetPath.slice(dotIndex+1); + logger.error("Invalid binding syntax '" + JSON.stringify(binding) + "'."); + throw "Invalid binding syntax '" + JSON.stringify(binding) + "'"; } - if ("<-" in binding || "->" in binding) { + dotIndex = targetPath.indexOf("."); + binding.boundObject = deserializer.getObjectByLabel(targetPath.slice(1, dotIndex)); + binding.boundObjectPropertyPath = targetPath.slice(dotIndex+1); + if ("<-" in binding) { binding.oneway = true; } } diff --git a/node_modules/montage/core/extras/object.js b/node_modules/montage/core/extras/object.js index 0281240f..d20b57fa 100644 --- a/node_modules/montage/core/extras/object.js +++ b/node_modules/montage/core/extras/object.js @@ -113,42 +113,27 @@ Object.defineProperty(Object.prototype, "setProperty", { // TODO clean up some of the duplicated code here if (lastObjectAtPath && Array.isArray(lastObjectAtPath)) { - if (lastObjectAtPath !== value) { - // if the value does not match the object described by this propertyPath; set it as the new value - - 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; + + 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 { - // Otherwise, they are the same object, a mutation event probably happened - - // If the object at the property we're "setting" is itself an array, see if there was an event passed along - // as part of a change a