Index: third_party/polymer/v0_8/components-chromium/polymer/src/standard/notify-path-extracted.js |
diff --git a/third_party/polymer/v0_8/components-chromium/polymer/src/standard/notify-path-extracted.js b/third_party/polymer/v0_8/components-chromium/polymer/src/standard/notify-path-extracted.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2dc890ccfc571124dae945225bf2df9caab5ea2e |
--- /dev/null |
+++ b/third_party/polymer/v0_8/components-chromium/polymer/src/standard/notify-path-extracted.js |
@@ -0,0 +1,240 @@ |
+ |
+ |
+ /** |
+ * Changes to an object sub-field (aka "path") via a binding |
+ * (e.g. `<x-foo value="{{item.subfield}}"`) will notify other elements bound to |
+ * the same object automatically. |
+ * |
+ * When modifying a sub-field of an object imperatively |
+ * (e.g. `this.item.subfield = 42`), in order to have the new value propagated |
+ * to other elements, a special `setPathValue(path, value)` API is provided. |
+ * `setPathValue` sets the object field at the path specified, and then notifies the |
+ * binding system so that other elements bound to the same path will update. |
+ * |
+ * Example: |
+ * |
+ * Polymer({ |
+ * |
+ * is: 'x-date', |
+ * |
+ * properties: { |
+ * date: { |
+ * type: Object, |
+ * notify: true |
+ * } |
+ * }, |
+ * |
+ * attached: function() { |
+ * this.date = {}; |
+ * setInterval(function() { |
+ * var d = new Date(); |
+ * // Required to notify elements bound to date of changes to sub-fields |
+ * // this.date.seconds = d.getSeconds(); <-- Will not notify |
+ * this.setPathValue('date.seconds', d.getSeconds()); |
+ * this.setPathValue('date.minutes', d.getMinutes()); |
+ * this.setPathValue('date.hours', d.getHours() % 12); |
+ * }.bind(this), 1000); |
+ * } |
+ * |
+ * }); |
+ * |
+ * Allows bindings to `date` sub-fields to update on changes: |
+ * |
+ * <x-date date="{{date}}"></x-date> |
+ * |
+ * Hour: <span>{{date.hours}}</span> |
+ * Min: <span>{{date.minutes}}</span> |
+ * Sec: <span>{{date.seconds}}</span> |
+ * |
+ * @class data feature: path notification |
+ */ |
+ |
+ Polymer.Base._addFeature({ |
+ /** |
+ Notify that a path has changed. For example: |
+ |
+ this.item.user.name = 'Bob'; |
+ this.notifyPath('item.user.name', this.item.user.name); |
+ |
+ Returns true if notification actually took place, based on |
+ a dirty check of whether the new value was already known |
+ */ |
+ notifyPath: function(path, value, fromAbove) { |
+ var old = this._propertySet(path, value); |
+ // manual dirty checking for now... |
+ if (old !== value) { |
+ // console.group((this.localName || this.dataHost.id + '-' + this.dataHost.dataHost.index) + '#' + (this.id || this.index) + ' ' + path, value); |
+ // Take path effects at this level for exact path matches, |
+ // and notify down for any bindings to a subset of this path |
+ this._pathEffector(path, value); |
+ // Send event to notify the path change upwards |
+ // Optimization: don't notify up if we know the notification |
+ // is coming from above already (avoid wasted event dispatch) |
+ if (!fromAbove) { |
+ // TODO(sorvell): should only notify if notify: true? |
+ this._notifyPath(path, value); |
+ } |
+ // console.groupEnd((this.localName || this.dataHost.id + '-' + this.dataHost.dataHost.index) + '#' + (this.id || this.index) + ' ' + path, value); |
+ } |
+ }, |
+ |
+ /** |
+ Convienence method for setting a value to a path and calling |
+ notify path |
+ */ |
+ setPathValue: function(path, value) { |
+ var parts = path.split('.'); |
+ if (parts.length > 1) { |
+ var last = parts.pop(); |
+ var prop = this; |
+ while (parts.length) { |
+ prop = prop[parts.shift()]; |
+ if (!prop) { |
+ return; |
+ } |
+ } |
+ // TODO(kschaaf): want dirty-check here? |
+ // if (prop[last] !== value) { |
+ prop[last] = value; |
+ this.notifyPath(path, value); |
+ // } |
+ } else { |
+ this[path] = value; |
+ } |
+ }, |
+ |
+ getPathValue: function(path, root) { |
+ var parts = path.split('.'); |
+ var last = parts.pop(); |
+ var prop = root || this; |
+ while (parts.length) { |
+ prop = prop[parts.shift()]; |
+ if (!prop) { |
+ return; |
+ } |
+ } |
+ return prop[last]; |
+ }, |
+ |
+ // TODO(kschaaf): This machine can be optimized to memoize compiled path |
+ // effectors as new paths are notified for performance, since it involves |
+ // a fair amount of runtime lookup |
+ _pathEffector: function(path, value) { |
+ // get root property |
+ var model = this._modelForPath(path); |
+ // search property effects of the root property for 'annotation' effects |
+ var fx$ = this._propertyEffects[model]; |
+ if (fx$) { |
+ fx$.forEach(function(fx) { |
+ var fxFn = this[fx.kind + 'PathEffect']; |
+ if (fxFn) { |
+ fxFn.call(this, path, value, fx.effect); |
+ } |
+ }, this); |
+ } |
+ // notify runtime-bound paths |
+ if (this._boundPaths) { |
+ this._notifyBoundPaths(path, value); |
+ } |
+ }, |
+ |
+ annotationPathEffect: function(path, value, effect) { |
+ if (effect.value === path || effect.value.indexOf(path + '.') === 0) { |
+ // TODO(sorvell): ideally the effect function is on this prototype |
+ // so we don't have to call it like this. |
+ Polymer.Bind.annotationEffect.call(this, path, value, effect); |
+ } else if ((path.indexOf(effect.value + '.') === 0) && !effect.negate) { |
+ // locate the bound node |
+ var node = this._nodes[effect.index]; |
+ if (node && node.notifyPath) { |
+ var p = this._fixPath(effect.name , effect.value, path); |
+ node.notifyPath(p, value, true); |
+ } |
+ } |
+ }, |
+ |
+ complexObserverPathEffect: function(path, value, effect) { |
+ if (this._pathMatchesEffect(path, effect)) { |
+ Polymer.Bind.complexObserverEffect.call(this, path, value, effect); |
+ } |
+ }, |
+ |
+ computePathEffect: function(path, value, effect) { |
+ if (this._pathMatchesEffect(path, effect)) { |
+ Polymer.Bind.computeEffect.call(this, path, value, effect); |
+ } |
+ }, |
+ |
+ annotatedComputationPathEffect: function(path, value, effect) { |
+ if (this._pathMatchesEffect(path, effect)) { |
+ Polymer.Bind.annotatedComputationEffect.call(this, path, value, effect); |
+ } |
+ }, |
+ |
+ _pathMatchesEffect: function(path, effect) { |
+ var effectArg = effect.arg.name; |
+ return (effectArg == path) || |
+ (effectArg.indexOf(path + '.') === 0) || |
+ (effect.arg.wildcard && path.indexOf(effectArg) === 0); |
+ }, |
+ |
+ bindPaths: function(to, from) { |
+ this._boundPaths = this._boundPaths || {}; |
+ if (from) { |
+ this._boundPaths[to] = from; |
+ // this.setPathValue(to, this.getPathValue(from)); |
+ } else { |
+ this.unbindPath(to); |
+ // this.setPathValue(to, from); |
+ } |
+ }, |
+ |
+ unbindPaths: function(path) { |
+ if (this._boundPaths) { |
+ delete this._boundPaths[path]; |
+ } |
+ }, |
+ |
+ _notifyBoundPaths: function(path, value) { |
+ var from, to; |
+ for (var a in this._boundPaths) { |
+ var b = this._boundPaths[a]; |
+ if (path.indexOf(a + '.') == 0) { |
+ from = a; |
+ to = b; |
+ break; |
+ } |
+ if (path.indexOf(b + '.') == 0) { |
+ from = b; |
+ to = a; |
+ break; |
+ } |
+ } |
+ if (from && to) { |
+ var p = this._fixPath(to, from, path); |
+ this.notifyPath(p, value); |
+ } |
+ }, |
+ |
+ _fixPath: function(property, root, path) { |
+ return property + path.slice(root.length); |
+ }, |
+ |
+ _notifyPath: function(path, value) { |
+ var rootName = this._modelForPath(path); |
+ var dashCaseName = Polymer.CaseMap.camelToDashCase(rootName); |
+ var eventName = dashCaseName + this._EVENT_CHANGED; |
+ this.fire(eventName, { |
+ path: path, |
+ value: value |
+ }, {bubbles: false}); |
+ }, |
+ |
+ _modelForPath: function(path) { |
+ return path.split('.').shift(); |
+ }, |
+ |
+ _EVENT_CHANGED: '-changed', |
+ |
+ }); |
+ |