five-bells-visualization
Version:
Tool to visualize Five Bells payments
230 lines (202 loc) • 7.14 kB
HTML
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script>
Polymer.Bind = {
// for prototypes (usually)
prepareModel: function(model) {
model._propertyEffects = {};
model._bindListeners = [];
// TODO(sjmiles): no mixin function?
var api = this._modelApi;
for (var n in api) {
model[n] = api[n];
}
},
_modelApi: {
_notifyChange: function(property) {
var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed';
// TODO(sjmiles): oops, `fire` doesn't exist at this layer
this.fire(eventName, {
value: this[property]
}, {bubbles: false});
},
// TODO(sjmiles): removing _notifyListener from here breaks accessors.html
// as a standalone lib. This is temporary, as standard/configure.html
// installs it's own version on Polymer.Base, and we need that to work
// right now.
// NOTE: exists as a hook for processing listeners
/*
_notifyListener: function(fn, e) {
// NOTE: pass e.target because e.target can get lost if this function
// is queued asynchrously
return fn.call(this, e, e.target);
},
*/
_propertySet: function(property, value, effects) {
var old = this._data[property];
if (old !== value) {
this._data[property] = value;
if (typeof value == 'object') {
this._clearPath(property);
}
if (effects) {
this._effectEffects(property, value, effects, old);
}
}
return old;
},
_effectEffects: function(property, value, effects, old) {
effects.forEach(function(fx) {
//console.log(fx);
var fn = Polymer.Bind[fx.kind + 'Effect'];
if (fn) {
fn.call(this, property, value, fx.effect, old);
}
}, this);
},
_clearPath: function(path) {
for (var prop in this._data) {
if (prop.indexOf(path + '.') === 0) {
this._data[prop] = undefined;
}
}
}
},
// a prepared model can acquire effects
ensurePropertyEffects: function(model, property) {
var fx = model._propertyEffects[property];
if (!fx) {
fx = model._propertyEffects[property] = [];
}
return fx;
},
addPropertyEffect: function(model, property, kind, effect) {
var fx = this.ensurePropertyEffects(model, property);
fx.push({
kind: kind,
effect: effect
});
},
createBindings: function(model) {
//console.group(model.is);
// map of properties to effects
var fx$ = model._propertyEffects;
if (fx$) {
// for each property with effects
for (var n in fx$) {
// array of effects
var fx = fx$[n];
// effects have priority
fx.sort(this._sortPropertyEffects);
// create accessors
this._createAccessors(model, n, fx);
}
}
//console.groupEnd();
},
_sortPropertyEffects: (function() {
// TODO(sjmiles): EFFECT_ORDER buried this way is not ideal,
// but presumably the sort method is going to be a hot path and not
// have a `this`. There is also a problematic dependency on effect.kind
// values here, which are otherwise pluggable.
var EFFECT_ORDER = {
'compute': 0,
'annotation': 1,
'computedAnnotation': 2,
'reflect': 3,
'notify': 4,
'observer': 5,
'complexObserver': 6,
'function': 7
};
return function(a, b) {
return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind];
};
})(),
// create accessors that implement effects
_createAccessors: function(model, property, effects) {
var defun = {
get: function() {
// TODO(sjmiles): elide delegation for performance, good ROI?
return this._data[property];
}
};
var setter = function(value) {
this._propertySet(property, value, effects);
};
// ReadOnly properties have a private setter only
// TODO(kschaaf): Per current Bind factoring, we shouldn't
// be interrogating the prototype here
if (model.isReadOnlyProperty && model.isReadOnlyProperty(property)) {
//model['_' + property + 'Setter'] = setter;
//model['_set_' + property] = setter;
model['_set' + this.upper(property)] = setter;
} else {
defun.set = setter;
}
Object.defineProperty(model, property, defun);
},
upper: function(name) {
return name[0].toUpperCase() + name.substring(1);
},
_addAnnotatedListener: function(model, index, property, path, event) {
var fn = this._notedListenerFactory(property, path,
this._isStructured(path), this._isEventBogus);
var eventName = event ||
(Polymer.CaseMap.camelToDashCase(property) + '-changed');
model._bindListeners.push({
index: index,
property: property,
path: path,
changedFn: fn,
event: eventName
});
},
_isStructured: function(path) {
return path.indexOf('.') > 0;
},
_isEventBogus: function(e, target) {
return e.path && e.path[0] !== target;
},
_notedListenerFactory: function(property, path, isStructured, bogusTest) {
return function(e, target) {
if (!bogusTest(e, target)) {
if (e.detail && e.detail.path) {
this.notifyPath(this._fixPath(path, property, e.detail.path),
e.detail.value);
} else {
var value = target[property];
if (!isStructured) {
this[path] = target[property];
} else {
// TODO(kschaaf): dirty check avoids null references when the object has gone away
if (this._data[path] != value) {
this.setPathValue(path, value);
}
}
}
}
};
},
// for instances
prepareInstance: function(inst) {
inst._data = Object.create(null);
},
setupBindListeners: function(inst) {
inst._bindListeners.forEach(function(info) {
// Property listeners:
// <node>.on.<property>-changed: <path]> = e.detail.value
//console.log('[_setupBindListener]: [%s][%s] listening for [%s][%s-changed]', this.localName, info.path, info.id || info.index, info.property);
var node = inst._nodes[info.index];
node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn));
});
}
};
</script>