mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
@@ -14,11 +14,13 @@ license that can be found in the LICENSE file.
|
||||
// conventional names have implicit bindings
|
||||
var conventions = {
|
||||
PROPERTY_CHANGED_SUFFIX: "Changed",
|
||||
CUSTOM_EVENT_PREFIX: "at"
|
||||
CUSTOM_EVENT_PREFIX: "on-"
|
||||
};
|
||||
|
||||
// polyfill for DOMTokenList features: list of classes in add/remove;
|
||||
// toggle method.
|
||||
// polyfill for DOMTokenList features:
|
||||
// add/remove multiple classes
|
||||
// arity-2 toggle
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
var add = DOMTokenList.prototype.add;
|
||||
@@ -44,6 +46,12 @@ license that can be found in the LICENSE file.
|
||||
(function(){
|
||||
|
||||
// attribute bindings
|
||||
//
|
||||
// <element attributes="attr1, attr2..."
|
||||
//
|
||||
// create matching observed properties attr1, attr2, etc.
|
||||
// and attaches MutationObserver so that changes in the attributes
|
||||
// are reflected in the matching properties
|
||||
|
||||
var bindAttrs = function(inAttributes) {
|
||||
var attrs = this.boundAttributes = [];
|
||||
@@ -58,6 +66,11 @@ license that can be found in the LICENSE file.
|
||||
};
|
||||
|
||||
// event bindings
|
||||
//
|
||||
// <element events="onclick: click, onkeypress: keypress, ..."
|
||||
//
|
||||
// create listeners on instances that map named events to
|
||||
// methods on the instance
|
||||
|
||||
var bindEvents = function(inEvents) {
|
||||
if (inEvents) {
|
||||
@@ -73,9 +86,45 @@ license that can be found in the LICENSE file.
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// model bindings
|
||||
//
|
||||
// convert {{macro}} strings in markup into MDV bindings
|
||||
//
|
||||
// MDV usually does this work but requires an additional
|
||||
// nested template and functions asynchronously
|
||||
|
||||
var bindModel = function(inNodes) {
|
||||
var m = bindModel.mustache;
|
||||
Array.prototype.forEach.call(inNodes, function(n) {
|
||||
n = n.baby || n;
|
||||
if (n.nodeName == "#text") {
|
||||
if (n.textContent.search(m) >= 0) {
|
||||
//console.log('addBinding', n.textContent, n);
|
||||
n.addBinding(n.textContent);
|
||||
}
|
||||
} else if (n.attributes && n.tagName !== "TEMPLATE") {
|
||||
// attribute bindings
|
||||
Array.prototype.forEach.call(n.attributes, function(a) {
|
||||
if (a.value.search(m) >= 0) {
|
||||
//console.log('addBinding', a.name, a.value, n);
|
||||
n.addBinding(a.name, a.value);
|
||||
}
|
||||
});
|
||||
bindModel(n.childNodes);
|
||||
}
|
||||
});
|
||||
};
|
||||
bindModel.mustache = /\{\{([^{}]*)}}/g;
|
||||
|
||||
// property bindings
|
||||
|
||||
//
|
||||
// sets up observable properties
|
||||
//
|
||||
// an observable property with <name> will automatically call
|
||||
// <name>Changed method when the property value changes
|
||||
// (if <name>Changed exists)
|
||||
|
||||
var propertyChanged = function(inName, inOld) {
|
||||
var fn = inName + conventions.PROPERTY_CHANGED_SUFFIX;
|
||||
if (this[fn]) {
|
||||
@@ -108,7 +157,7 @@ license that can be found in the LICENSE file.
|
||||
// set default value in a property already bound via attrs
|
||||
setPropertySilently.call(this, inName, value);
|
||||
} else {
|
||||
console.log('binding', inName, this)
|
||||
//console.log('binding', inName, this)
|
||||
var value = inValue;
|
||||
var sideEffect = sideEffectFactory(inName);
|
||||
Object.defineProperty(this, inName, {
|
||||
@@ -134,6 +183,11 @@ license that can be found in the LICENSE file.
|
||||
}
|
||||
};
|
||||
|
||||
// node bindings
|
||||
//
|
||||
// local nodes that have ID and store references to them in
|
||||
// this.$ hash
|
||||
|
||||
var deref = function(inNode) {
|
||||
return inNode && (inNode.baby || inNode);
|
||||
};
|
||||
@@ -148,7 +202,73 @@ license that can be found in the LICENSE file.
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
// data bindings
|
||||
//
|
||||
// <name>: ['<path dependency>', '<path dependency>'...]
|
||||
//
|
||||
// bind MDV macros of the form {{<name>}} to formatting methods
|
||||
// in inNode using MDV delegates
|
||||
//
|
||||
|
||||
var bindDelegates = function(inDelegates) {
|
||||
if (inDelegates) {
|
||||
var node = this;
|
||||
this.modelDelegate = function(inBinding) {
|
||||
if (inDelegates[inBinding]) {
|
||||
return [
|
||||
inDelegates[inBinding],
|
||||
function(/*inValues*/) {
|
||||
return delegateBinding(node, inBinding + "Out", arguments);
|
||||
},
|
||||
function(/*inValues*/) {
|
||||
return delegateBinding(node, inBinding + "In", arguments);
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var delegateBinding = function(inNode, inDelegate, inValues) {
|
||||
var fn = inNode[inDelegate];
|
||||
var value = fn ? fn.apply(inNode, inValues) : undefined;
|
||||
if (value === undefined) {
|
||||
value = inValues[0];
|
||||
}
|
||||
//console.log(inDelegate, inValues, value);
|
||||
return value;
|
||||
};
|
||||
|
||||
var deref = function(inNode) {
|
||||
return inNode && (inNode.baby || inNode);
|
||||
};
|
||||
|
||||
var establishNodeReferences = function(inRoot) {
|
||||
this.$ = this.$ || {};
|
||||
// search the LOCAL tree
|
||||
if (inRoot) {
|
||||
var nodes = ShadowDOM.localQueryAll(inRoot, "[id]");
|
||||
Array.prototype.forEach.call(nodes, function(n) {
|
||||
this.$[n.id] = deref(n);
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
var $$ = function(inSlctr) {
|
||||
var s = this.shadow;
|
||||
while(s){
|
||||
var n = s.querySelector(inSlctr);
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
s = s.olderSubtree;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
*/
|
||||
|
||||
// attribute mutations
|
||||
|
||||
var deserializeValue = function(inValue) {
|
||||
@@ -165,6 +285,10 @@ license that can be found in the LICENSE file.
|
||||
return isNaN(n) ? inValue : n;
|
||||
};
|
||||
|
||||
// propagate initial bound-attribute values into properties
|
||||
// as a batch, invoking side-effects only after all
|
||||
// properties are initialized
|
||||
|
||||
var takeAttributes = function() {
|
||||
var changed = [];
|
||||
try {
|
||||
@@ -188,8 +312,7 @@ license that can be found in the LICENSE file.
|
||||
};
|
||||
|
||||
var attributeChanged = function(inName) {
|
||||
var value = this.getAttribute(inName);
|
||||
this[inName] = deserializeValue(value);
|
||||
this[inName] = deserializeValue(this.getAttribute(inName));
|
||||
};
|
||||
|
||||
var handleMutations = function(inMxns) {
|
||||
@@ -212,20 +335,43 @@ license that can be found in the LICENSE file.
|
||||
|
||||
// lifecycle
|
||||
|
||||
var automate = function(inAttributes, inPublished) {
|
||||
bindAttrs.call(this, inAttributes.attributes);
|
||||
bindEvents.call(this, inAttributes.handlers);
|
||||
bindProperties.call(this, inPublished);
|
||||
};
|
||||
|
||||
// per-root
|
||||
var initialize = function(inRoot, inUber) {
|
||||
establishNodeReferences.call(this, inRoot);
|
||||
if (inUber.shadowRootCreated) {
|
||||
inUber.shadowRootCreated.call(this, inRoot);
|
||||
}
|
||||
bindAllCustomEvents(inRoot);
|
||||
if (inRoot) {
|
||||
bindModel(inRoot.childNodes);
|
||||
}
|
||||
};
|
||||
|
||||
// per-instance
|
||||
var automate = function(inAttributes, inPublished, inDelegates) {
|
||||
bindAttrs.call(this, inAttributes.attributes);
|
||||
bindEvents.call(this, inAttributes.handlers);
|
||||
bindProperties.call(this, inPublished);
|
||||
bindDelegates.call(this, inDelegates);
|
||||
};
|
||||
|
||||
|
||||
// fireEvent impl
|
||||
|
||||
var fireEvent = function(inType, inDetail) {
|
||||
this.asyncMethod("dispatchEvent", [
|
||||
new CustomEvent(inType, {bubbles: true, detail: inDetail})
|
||||
]);
|
||||
};
|
||||
|
||||
// asyncMethod impl
|
||||
|
||||
var asyncMethod = function(inMethod, inArgs) {
|
||||
window.setTimeout(function() {
|
||||
this[inMethod].apply(this, inArgs);
|
||||
}.bind(this), 0);
|
||||
};
|
||||
|
||||
// decorate HTMLElementElement with toolkit API
|
||||
|
||||
HTMLElementElement.prototype.component = function(inUber) {
|
||||
@@ -235,8 +381,9 @@ license that can be found in the LICENSE file.
|
||||
initialize.call(this, inRoot, inUber);
|
||||
},
|
||||
created: function() {
|
||||
//console.log("created", this);
|
||||
this.controller = this;
|
||||
automate.call(this, attributes, inUber.published);
|
||||
automate.call(this, attributes, inUber.published, inUber.delegates);
|
||||
takeAttributes.call(this);
|
||||
if (inUber.created) {
|
||||
inUber.created.call(this);
|
||||
@@ -250,11 +397,14 @@ license that can be found in the LICENSE file.
|
||||
// probably better to insert another link in the prototype chain
|
||||
p.utils = utils;
|
||||
p.setPropertySilently = setPropertySilently;
|
||||
p.fireEvent = fireEvent;
|
||||
p.asyncMethod = asyncMethod;
|
||||
//p.$$ = $$;
|
||||
// install our prototype
|
||||
this.generatedConstructor.prototype = p;
|
||||
};
|
||||
|
||||
// utility methods
|
||||
// utility methods (utils)
|
||||
|
||||
// job
|
||||
|
||||
@@ -277,12 +427,12 @@ license that can be found in the LICENSE file.
|
||||
|
||||
// target finding
|
||||
|
||||
var findDistributedTarget = function(inTarget, inItems) {
|
||||
// find ancestor of target (including himself) that
|
||||
// is in our item list, if any
|
||||
var findDistributedTarget = function(inTarget, inNodes) {
|
||||
// find first ancestor of target (including itself) that
|
||||
// is in inNodes, if any
|
||||
var n = inTarget;
|
||||
while (n && n != this) {
|
||||
var i = inItems.indexOf(n);
|
||||
var i = inNodes.indexOf(n);
|
||||
if (i >= 0) {
|
||||
return i;
|
||||
}
|
||||
@@ -290,13 +440,28 @@ license that can be found in the LICENSE file.
|
||||
}
|
||||
};
|
||||
|
||||
// string interpolation
|
||||
var macroize = function(inText, inMap, inPattern) {
|
||||
var result = inText, pattern = inPattern || macroize.pattern;
|
||||
var fn = function(macro, name) {
|
||||
var v = inMap[name];
|
||||
return (v === undefined || v === null) ? macro : v;
|
||||
};
|
||||
result = result.replace(pattern, fn);
|
||||
return result;
|
||||
};
|
||||
// Matches macros of the form {{name}}.
|
||||
macroize.pattern = /\{{([^{}]*)\}}/g;
|
||||
|
||||
// collect utils
|
||||
|
||||
var utils = {
|
||||
job: job,
|
||||
macroize: macroize,
|
||||
findDistributedTarget: findDistributedTarget
|
||||
};
|
||||
|
||||
/*
|
||||
// code below provides a shim for declarative event handlers
|
||||
// (aka 'x', as in onclick="x('click')")
|
||||
// it's only really for evaluating syntax, and not
|
||||
@@ -350,7 +515,8 @@ license that can be found in the LICENSE file.
|
||||
owner[inHandler](event);
|
||||
}
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
// newer experimental event handler
|
||||
|
||||
var findController = function(inNode) {
|
||||
@@ -373,6 +539,10 @@ license that can be found in the LICENSE file.
|
||||
};
|
||||
|
||||
// automagic event mapping
|
||||
//
|
||||
// locate attributes of the form <prefix><eventName>="<methodName>" and
|
||||
// map <eventName> events to method <methodName> to inNode's controller
|
||||
//
|
||||
|
||||
var bindCustomEvent = function(inNode, inEventName, inHandler) {
|
||||
var h = inNode.__athandlers = inNode.__athandlers || {};
|
||||
@@ -386,11 +556,12 @@ license that can be found in the LICENSE file.
|
||||
};
|
||||
|
||||
var bindCustomEvents = function(inNode) {
|
||||
var prefix = conventions.CUSTOM_EVENT_PREFIX;
|
||||
var a$ = inNode.attributes;
|
||||
if (a$) {
|
||||
for (var i=0, a; a=a$[i]; i++) {
|
||||
if (a.name.slice(0, 2) == conventions.CUSTOM_EVENT_PREFIX) {
|
||||
bindCustomEvent(inNode, a.name.slice(2), a.value);
|
||||
if (a.name.slice(0, prefix.length) == prefix) {
|
||||
bindCustomEvent(inNode, a.name.slice(prefix.length), a.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,12 +579,9 @@ license that can be found in the LICENSE file.
|
||||
}
|
||||
};
|
||||
|
||||
var __bindAllCustomEvents = function(inNode) {
|
||||
_bindAllCustomEvents(inNode);
|
||||
};
|
||||
|
||||
var eventsObserver = function(inNode) {
|
||||
new WebKitMutationObserver(__bindAllCustomEvents.bind(this, inNode))
|
||||
// TODO(sjmiles): optimize so we only bind new nodes
|
||||
new WebKitMutationObserver(_bindAllCustomEvents.bind(this, inNode))
|
||||
.observe(inNode, {
|
||||
childList: true,
|
||||
subTree: true
|
||||
@@ -423,6 +591,7 @@ license that can be found in the LICENSE file.
|
||||
var bindAllCustomEvents = function(inNode) {
|
||||
if (inNode) {
|
||||
_bindAllCustomEvents(inNode);
|
||||
// we need this for custom events in iterated templates
|
||||
eventsObserver(inNode);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user