Commit fd3f4d0b authored by Addy Osmani's avatar Addy Osmani

Merge pull request #648 from tastejs/polymer-update

Updating to latest official Polymer TodoMVC implementation
parents 2efa9037 300b76cd
......@@ -20,14 +20,14 @@ body {
font-smoothing: antialiased;
}
body > section {
position: relative;
margin: 130px 0 40px 0;
body > header {
padding-top: 22px;
margin-bottom: -5px;
}
h1 {
position: absolute;
top: -120px;
/* position: absolute;
top: -120px;*/
width: 100%;
font-size: 70px;
font-weight: bold;
......@@ -185,6 +185,12 @@ hr {
transition-duration: 500ms;
}
/* IE doesn't support the hidden attribute */
[hidden] {
display: none;
}
@media (min-width: 899px) {
/**body*/.learn-bar {
width: auto;
......@@ -193,7 +199,7 @@ hr {
/**body*/.learn-bar > .learn {
left: 8px;
}
/**body*/.learn-bar > section {
/**body*/.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
......
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
var thisFile = 'polymer.js';
var scopeName = 'Polymer';
var modules = [
'src/platform.min.js',
'src/lang.js',
'src/oop.js',
'src/register.js',
'src/base.js',
'src/trackObservers.js',
'src/bindProperties.js',
'src/bindMDV.js',
'src/attrs.js',
'src/marshal.js',
'src/events.js',
'src/observeProperties.js',
'src/styling.js',
'src/shimStyling.js',
'src/path.js',
'src/job.js',
'src/boot.js'
];
// export
window[scopeName] = {
entryPointName: thisFile,
modules: modules
};
// bootstrap
var script = document.querySelector('script[src*="' + thisFile + '"]');
var src = script.attributes.src.value;
var basePath = src.slice(0, src.indexOf(thisFile));
console.log(src);
modules.forEach(function(m) {
document.write('<script src="' + basePath + m + '"></script>');
});
})();
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// imports
var bindPattern = Polymer.bindPattern;
// constants
var published$ = '__published';
var attributes$ = 'attributes';
var attrProps$ = 'publish';
//var attrProps$ = 'attributeDefaults';
function publishAttributes(element, prototype) {
publishAttributesAttributes(element, prototype);
publishInstanceAttributes(element, prototype);
}
function publishAttributesAttributes(inElement, inPrototype) {
var published = {};
// merge attribute names from 'attributes' attribute
var attributes = inElement.getAttribute(attributes$);
if (attributes) {
// attributes='a b c' or attributes='a,b,c'
var names = attributes.split(attributes.indexOf(',') >= 0 ? ',' : ' ');
// record each name for publishing
names.forEach(function(p) {
p = p.trim();
if (p) {
published[p] = null;
}
});
}
// our suffix prototype chain (inPrototype is 'own')
var inherited = inElement.options.prototype;
// install 'attributes' as properties on the prototype, but don't
// override
Object.keys(published).forEach(function(p) {
if (!(p in inPrototype) && !(p in inherited)) {
inPrototype[p] = published[p];
}
});
// acquire properties published imperatively
var imperative = inPrototype[attrProps$];
if (imperative) {
// install imperative properties, overriding defaults
Object.keys(imperative).forEach(function(p) {
inPrototype[p] = imperative[p];
});
// combine declaratively and imperatively published properties
published = Platform.mixin(published, imperative);
}
// combine with inherited published properties
inPrototype[published$] = Platform.mixin(
{},
inherited[published$],
published
);
}
function publishInstanceAttributes(element, prototype) {
// our suffix prototype chain (prototype is 'own')
var inherited = element.options.prototype;
var attributes = element.attributes;
var a$ = prototype.instanceAttributes =
Object.create(inherited.instanceAttributes || null);
for (var i=0, l=attributes.length, a; (i<l) && (a=attributes[i]); i++) {
if (!publishInstanceAttributes.blackList[a.name]) {
if (a.name.slice(0, 3) !== 'on-') {
a$[a.name] = a.value;
}
}
}
}
publishInstanceAttributes.blackList = {name: 1, 'extends': 1, constructor: 1};
publishInstanceAttributes.blackList[attributes$] = 1;
function installInstanceAttributes() {
var a$ = this.instanceAttributes;
Object.keys(a$).forEach(function(name) {
this.setAttribute(name, a$[name]);
}, this);
}
function takeAttributes() {
// for each attribute
forEach(this.attributes, function(a) {
// try to match this attribute to a property (attributes are
// all lower-case, so this is case-insensitive search)
var name = propertyForAttribute.call(this, a.name);
if (name) {
// filter out 'mustached' values, these are to be
// replaced with bound-data and are not yet values
// themselves
if (a.value.search(bindPattern) >= 0) {
return;
}
// get original value
var defaultValue = this[name];
// deserialize Boolean or Number values from attribute
var value = deserializeValue(a.value, defaultValue);
// only act if the value has changed
if (value !== defaultValue) {
// install new value (has side-effects)
this[name] = value;
}
}
}, this);
}
// return the published property matching name, or undefined
function propertyForAttribute(name) {
// matchable properties must be published
var properties = Object.keys(this[published$]);
// search for a matchable property
return properties[properties.map(lowerCase).indexOf(name.toLowerCase())];
}
var lowerCase = String.prototype.toLowerCase.call.bind(
String.prototype.toLowerCase);
var typeHandlers = {
'string': function(value) {
return value;
},
'date': function(value) {
return new Date(Date.parse(value) || Date.now());
},
'boolean': function(value) {
if (value === '') {
return true;
}
return value === 'false' ? false : !!value;
},
'number': function(value) {
var floatVal = parseFloat(value);
return (String(floatVal) === value) ? floatVal : value;
},
'object': function(value, defaultValue) {
if (!defaultValue) {
return value;
}
try {
// If the string is an object, we can parse is with the JSON library.
// include convenience replace for single-quotes. If the author omits
// quotes altogether, parse will fail.
return JSON.parse(value.replace(/'/g, '"'));
} catch(e) {
// The object isn't valid JSON, return the raw value
return value;
}
}
};
function deserializeValue(value, defaultValue) {
// attempt to infer type from default value
var inferredType = typeof defaultValue;
if (defaultValue instanceof Date) {
inferredType = 'date';
}
return typeHandlers[inferredType](value, defaultValue);
}
// exports
Polymer.takeAttributes = takeAttributes;
Polymer.publishAttributes = publishAttributes;
Polymer.propertyForAttribute = propertyForAttribute;
Polymer.installInstanceAttributes = installInstanceAttributes;
})();
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/**
* @module Polymer
*/
/**
* Base methods for Polymer elements.
* @class Base
*/
(function(scope) {
// imports
var log = window.logFlags || {};
var base = {
/**
* When called in a method, invoke the method's super.
* @method super
* @param {Array} Array of arguments.
*/
super: Polymer.$super,
/**
* True if this object is a Polymer element.
* @property isPolymerElement
* @type boolean
*/
isPolymerElement: true,
/**
* MDV bind.
* @method bind
*/
bind: function() {
Polymer.bind.apply(this, arguments);
},
/**
* MDV unbind.
* @method unbind
*/
unbind: function() {
Polymer.unbind.apply(this, arguments);
},
/**
* MDV unbindAll.
* @method unbindAll
*/
unbindAll: function() {
Polymer.unbindAll.apply(this, arguments);
},
/**
* Ensures MDV bindings persist.
*
* Typically, it's not necessary to call this method. Polymer
* automatically manages bindings when elements are inserted
* and removed from the document.
*
* However, if an element is created and not inserted into the document,
* cancelUnbindAll should be called to ensure bindings remain active.
* Otherwise bindings will be removed so that the element
* may be garbage collected, freeing the memory it uses. Please note that
* if cancelUnbindAll is called and the element is not inserted
* into the document, then unbindAll or asyncUnbindAll must be called
* to dispose of the element.
*
* @method cancelUnbindAll
* @param {Boolean} [preventCascade] If true, cancelUnbindAll will not
* cascade to shadowRoot children. In the case described above,
* and in general in application code, this should not be set to true.
*/
cancelUnbindAll: function(preventCascade) {
Polymer.cancelUnbindAll.apply(this, arguments);
},
/**
* Schedules MDV bindings to be removed asynchronously.
*
* Typically, it's not necessary to call this method. Polymer
* automatically manages bindings when elements are inserted
* and removed from the document.
*
* However, if an element is created and not inserted into the document,
* cancelUnbindAll should be called to ensure bindings remain active.
* Otherwise bindings will be removed so that the element
* may be garbage collected, freeing the memory it uses. Please note that
* if cancelUnbindAll is called and the element is not inserted
* into the document, then unbindAll or asyncUnbindAll must be called
* to dispose of the element.
*
* @method asyncUnbindAll
*/
asyncUnbindAll: function() {
Polymer.asyncUnbindAll.apply(this, arguments);
},
/**
* Schedules an async job with timeout and returns a handle.
* @method job
* @param {Polymer.Job} [job] A job handle if re-registering.
* @param {Function} callback Function to invoke.
* @param {number} [wait] Timeout in milliseconds.
* @return {Polymer.Job} A job handle which can be used to
* re-register, cancel or complete a job.
*/
job: function() {
return Polymer.job.apply(this, arguments);
},
/**
* Invokes a function asynchronously. The context of the callback
* function is bound to 'this' automatically.
* @method asyncMethod
* @param {Function} method
* @param {Object|Array} args
* @param {number} timeout
*/
asyncMethod: function(inMethod, inArgs, inTimeout) {
// when polyfilling Object.observe, ensure changes
// propagate before executing the async method
Platform.flush();
var args = (inArgs && inArgs.length) ? inArgs : [inArgs];
var fn = function() {
(this[inMethod] || inMethod).apply(this, args);
}.bind(this);
return inTimeout ? window.setTimeout(fn, inTimeout) :
requestAnimationFrame(fn);
},
/**
* Invoke a method.
* @method dispatch
* @param {string} methodName A method name.
* @param {Array} arguments
*/
dispatch: function(inMethodName, inArguments) {
if (this[inMethodName]) {
this[inMethodName].apply(this, inArguments);
}
},
/**
* Fire an event.
* @method fire
* @param {string} type An event name.
* @param detail
* @param {Node} toNode Target node.
*/
fire: function(inType, inDetail, inToNode) {
var node = inToNode || this;
log.events && console.log('[%s]: sending [%s]', node.localName, inType);
node.dispatchEvent(
new CustomEvent(inType, {bubbles: true, detail: inDetail}));
return inDetail;
},
/**
* Fire an event asynchronously.
* @method asyncFire
* @param {string} type An event name.
* @param detail
* @param {Node} toNode Target node.
*/
asyncFire: function(/*inType, inDetail*/) {
this.asyncMethod("fire", arguments);
},
/**
* Remove class from old, add class to anew, if they exist
* @param classFollows
* @param anew A node.
* @param old A node
* @param className
*/
classFollows: function(anew, old, className) {
if (old) {
old.classList.remove(className);
}
if (anew) {
anew.classList.add(className);
}
}
};
// TODO(sjmiles): backward-compat for deprecated syntax
base.send = base.fire;
base.asend = base.asyncFire;
// exports
scope.base = base;
})(window.Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// imports
var log = window.logFlags || {};
// use the ExperssionSyntax
var expressionSyntax = new ExpressionSyntax;
// bind tracking
var bindings = new SideTable();
function registerBinding(element, name, path) {
var b$ = bindings.get(element);
if (!b$) {
bindings.set(element, b$ = {});
}
b$[name.toLowerCase()] = path;
}
function unregisterBinding(element, name) {
var b$ = bindings.get(element);
if (b$) {
delete b$[name.toLowerCase()];
}
}
function overrideBinding(ctor) {
var proto = ctor.prototype;
var originalBind = proto.bind;
var originalUnbind = proto.unbind;
proto.bind = function(name, model, path) {
originalBind.apply(this, arguments);
// note: must do this last because mdv may unbind before binding
registerBinding(this, name, path);
}
proto.unbind = function(name) {
originalUnbind.apply(this, arguments);
unregisterBinding(this, name);
}
};
[Node, Element, Text, HTMLInputElement].forEach(overrideBinding);
var emptyBindings = {};
function getBindings(element) {
return element && bindings.get(element) || emptyBindings;
}
function getBinding(element, name) {
return getBindings(element)[name.toLowerCase()];
}
// model bindings
function bind(name, model, path) {
var property = Polymer.propertyForAttribute.call(this, name);
if (property) {
registerBinding(this, property, path);
Polymer.registerObserver(this, 'binding', property,
Polymer.bindProperties(this, property, model, path)
);
} else {
HTMLElement.prototype.bind.apply(this, arguments);
}
}
function unbind(name) {
if (!Polymer.unregisterObserver(this, 'binding', name)) {
HTMLElement.prototype.unbind.apply(this, arguments);
}
}
function unbindAll() {
if (!isElementUnbound(this)) {
Polymer.unregisterObserversOfType(this, 'property');
HTMLElement.prototype.unbindAll.apply(this, arguments);
// unbind shadowRoot, whee
unbindNodeTree(this.webkitShadowRoot, true);
markElementUnbound(this);
}
}
function unbindNodeTree(node, olderShadows) {
forNodeTree(node, olderShadows, function(n) {
if (n.unbindAll) {
n.unbindAll();
}
});
}
function forNodeTree(node, olderShadows, callback) {
if (!node) {
return;
}
callback(node);
if (olderShadows && node.olderShadowRoot) {
forNodeTree(node.olderShadowRoot, olderShadows, callback);
}
for (var child = node.firstChild; child; child = child.nextSibling) {
forNodeTree(child, olderShadows, callback);
}
}
// binding state tracking
var unboundTable = new SideTable();
function markElementUnbound(element) {
unboundTable.set(element, true);
}
function isElementUnbound(element) {
return unboundTable.get(element);
}
// asynchronous binding management
var unbindAllJobTable = new SideTable();
function asyncUnbindAll() {
if (!isElementUnbound(this)) {
log.bind && console.log('asyncUnbindAll', this.localName);
unbindAllJobTable.set(this, this.job(unbindAllJobTable.get(this),
this.unbindAll));
}
}
function cancelUnbindAll(preventCascade) {
if (isElementUnbound(this)) {
log.bind && console.warn(this.localName,
'is unbound, cannot cancel unbindAll');
return;
}
log.bind && console.log('cancelUnbindAll', this.localName);
var unbindJob = unbindAllJobTable.get(this);
if (unbindJob) {
unbindJob.stop();
unbindAllJobTable.set(this, null);
}
// cancel unbinding our shadow tree iff we're not in the process of
// cascading our tree (as we do, for example, when the element is inserted).
if (!preventCascade) {
forNodeTree(this.webkitShadowRoot, true, function(n) {
if (n.cancelUnbindAll) {
n.cancelUnbindAll();
}
});
}
}
// bind arbitrary html to a model
function parseAndBindHTML(html, model) {
var template = document.createElement('template');
template.innerHTML = html;
return template.createInstance(model, expressionSyntax);
}
var mustachePattern = /\{\{([^{}]*)}}/;
// exports
Polymer.bind = bind;
Polymer.unbind = unbind;
Polymer.unbindAll = unbindAll;
Polymer.getBinding = getBinding;
Polymer.asyncUnbindAll = asyncUnbindAll;
Polymer.cancelUnbindAll = cancelUnbindAll;
Polymer.isElementUnbound = isElementUnbound;
Polymer.unbindNodeTree = unbindNodeTree;
Polymer.parseAndBindHTML = parseAndBindHTML;
Polymer.bindPattern = mustachePattern;
Polymer.expressionSyntax = expressionSyntax;
})();
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
var log = window.logFlags || {};
// bind a property in A to a path in B by converting A[property] to a
// getter/setter pair that accesses B[...path...]
function bindProperties(inA, inProperty, inB, inPath) {
log.bind && console.log("[%s]: bindProperties: [%s] to [%s].[%s]",
inB.localName || 'object', inPath, inA.localName, inProperty);
// capture A's value if B's value is null or undefined,
// otherwise use B's value
var v = PathObserver.getValueAtPath(inB, inPath);
if (v === null || v === undefined) {
PathObserver.setValueAtPath(inB, inPath, inA[inProperty]);
}
return PathObserver.defineProperty(inA, inProperty, {object: inB, path: inPath});
}
// exports
Polymer.bindProperties = bindProperties;
})();
\ No newline at end of file
/*
* Copyright 2012 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// FOUC prevention tactic
var style = document.createElement('style');
style.textContent = 'body {opacity: 0;}';
var head = document.querySelector('head');
head.insertBefore(style, head.firstChild);
window.addEventListener('WebComponentsReady', function() {
document.body.style.webkitTransition = 'opacity 0.3s';
document.body.style.opacity = 1;
});
})();
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
if (!window.Polymer) {
window.Polymer = {};
}
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// imports
var log = window.logFlags || {};
//
// automagic event mapping
//
var prefix = "on-";
var parseHostEvents = function(inAttributes, inPrototype) {
// inDefinition.eventDelegates = Platform.mixin(inDefinition.eventDelegates,
// parseEvents(inAttributes));
inPrototype.eventDelegates = parseEvents(inAttributes);
};
var parseEvents = function(inAttributes) {
var events = {};
if (inAttributes) {
for (var i=0, a; a=inAttributes[i]; i++) {
if (a.name.slice(0, prefix.length) == prefix) {
events[a.name.slice(prefix.length)] = a.value;
}
}
}
return events;
};
var accumulateEvents = function(inNode, inEvents) {
var events = inEvents || {};
accumulateNodeEvents(inNode, events);
accumulateChildEvents(inNode, events);
accumulateTemplatedEvents(inNode, events);
return events;
};
var accumulateNodeEvents = function(inNode, inEvents) {
var a$ = inNode.attributes;
if (a$) {
for (var i=0, a; (a=a$[i]); i++) {
if (a.name.slice(0, prefix.length) === prefix) {
accumulateEvent(a.name.slice(prefix.length), inEvents);
}
}
}
};
var event_translations = {
webkitanimationstart: 'webkitAnimationStart',
webkitanimationend: 'webkitAnimationEnd',
webkittransitionend: 'webkitTransitionEnd',
domfocusout: 'DOMFocusOut',
domfocusin: 'DOMFocusIn'
};
var accumulateEvent = function(inName, inEvents) {
var n = event_translations[inName] || inName;
inEvents[n] = 1;
};
var accumulateChildEvents = function(inNode, inEvents) {
var cn$ = inNode.childNodes;
for (var i=0, n; (n=cn$[i]); i++) {
// TODO(sjmiles): unify calling convention (.call or not .call)
accumulateEvents(n, inEvents);
//if (n.$protected) {
// accumulateHostEvents.call(n.$protected, inEvents);
//}
}
};
var accumulateTemplatedEvents = function(inNode, inEvents) {
if (inNode.localName == 'template') {
var content = getTemplateContent(inNode);
if (content) {
accumulateChildEvents(content, inEvents);
}
}
}
// TODO(sorvell): Currently in MDV, there is no way to get a template's
// effective content. A template can have a ref property
// that points to the template from which this one has been cloned.
// Remove this when the MDV api is improved
// (https://github.com/polymer-project/mdv/issues/15).
var getTemplateContent = function(inTemplate) {
return inTemplate.ref ? inTemplate.ref.content : inTemplate.content;
}
var accumulateHostEvents = function(inEvents) {
var events = inEvents || {};
// specifically search the __proto__ (as opposed to getPrototypeOf)
// __proto__ is simulated on platforms which don't support it naturally
// TODO(sjmiles): we walk the prototype tree to operate on the union of
// eventDelegates maps; it might be better to merge maps when extending
var p = this.__proto__;
while (p && p !== HTMLElement.prototype) {
if (p.hasOwnProperty('eventDelegates')) {
for (var n in p.eventDelegates) {
accumulateEvent(n, events);
}
}
p = p.__proto__;
}
return events;
};
function bindAccumulatedEvents(inNode, inEvents, inListener) {
var fn = inListener.bind(this);
for (var n in inEvents) {
log.events && console.log('[%s] bindAccumulatedEvents: addEventListener("%s", listen)', inNode.localName || 'root', n);
inNode.addEventListener(n, fn);
}
};
// host events should be listened for on a host element
function bindAccumulatedHostEvents(inEvents) {
bindAccumulatedEvents.call(this, this, inEvents, listenHost);
}
// local events should be listened for on a shadowRoot
function bindAccumulatedLocalEvents(inNode, inEvents) {
bindAccumulatedEvents.call(this, inNode, inEvents, listenLocal);
}
// experimental delegating declarative event handler
// TODO(sjmiles):
// we wanted to simply look for nearest ancestor
// with a 'controller' property to be WLOG
// but we need to honor ShadowDOM, so we had to
// customize this search
var findController = function(inNode) {
// find the shadow root that contains inNode
var n = inNode;
while (n.parentNode && n.localName !== 'shadow-root') {
n = n.parentNode;
}
return n.host;
};
var dispatch = function(inNode, inHandlerName, inArguments) {
if (inNode) {
log.events && console.group('[%s] dispatch [%s]', inNode.localName, inHandlerName);
inNode.dispatch(inHandlerName, inArguments);
log.events && console.groupEnd();
}
};
function listenLocal(inEvent) {
if (inEvent.cancelBubble) {
return;
}
inEvent.on = prefix + inEvent.type;
log.events && console.group("[%s]: listenLocal [%s]", this.localName,
inEvent.on);
if (!inEvent.path || window.ShadowDOMPolyfill) {
listenLocalNoEventPath(inEvent);
} else {
var c = null;
Array.prototype.some.call(inEvent.path, function(t) {
if (t === this) {
return true;
}
c = c === this ? c : findController(t);
if (c) {
if (handleEvent.call(c, t, inEvent)) {
return true;
}
}
}, this);
}
log.events && console.groupEnd();
}
// TODO(sorvell): remove when ShadowDOM polyfill supports event path.
// Note that findController will not return the expected
// controller when when the event target is a distributed node.
// This because we cannot traverse from a composed node to a node
// in shadowRoot.
// This will be addressed via an event path api
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066
function listenLocalNoEventPath(inEvent) {
log.events && console.log('event.path() not supported for', inEvent.type);
var t = inEvent.target, c = null;
while (t && t != this) {
c = c === this ? c : findController(t);
if (c) {
if (handleEvent.call(c, t, inEvent)) {
return;
}
}
t = t.parentNode;
}
}
function listenHost(inEvent) {
if (inEvent.cancelBubble) {
return;
}
log.events && console.group("[%s]: listenHost [%s]", this.localName, inEvent.type);
handleHostEvent.call(this, this, inEvent);
log.events && console.groupEnd();
}
var eventHandledTable = new SideTable('handledList');
function getHandledListForEvent(inEvent) {
var handledList = eventHandledTable.get(inEvent);
if (!handledList) {
handledList = [];
eventHandledTable.set(inEvent, handledList);
}
return handledList;
}
function handleEvent(inNode, inEvent) {
if (inNode.attributes) {
var handledList = getHandledListForEvent(inEvent);
if (handledList.indexOf(inNode) < 0) {
handledList.push(inNode);
var h = inNode.getAttribute(inEvent.on);
if (h) {
log.events && console.log('[%s] found handler name [%s]', this.localName, h);
dispatch(this, h, [inEvent, inEvent.detail, inNode]);
}
}
}
return inEvent.cancelBubble;
};
function handleHostEvent(inNode, inEvent) {
var h = findHostHandler.call(inNode, inEvent.type);
if (h) {
log.events && console.log('[%s] found host handler name [%s]', inNode.localName, h);
dispatch(inNode, h, [inEvent, inEvent.detail, inNode]);
}
return inEvent.cancelBubble;
};
// find the method name (handler) in eventDelegates mapped to inEventName
var findHostHandler = function(inEventName) {
// TODO(sjmiles): walking the tree again is inefficient; combine with code
// in accumulateHostEvents into something more sane
var p = this;
while (p) {
if (p.hasOwnProperty('eventDelegates')) {
var h = p.eventDelegates[inEventName]
|| p.eventDelegates[inEventName.toLowerCase()];
if (h) {
return h;
}
}
p = p.__proto__;
}
};
// exports
Polymer.parseHostEvents = parseHostEvents;
Polymer.accumulateEvents = accumulateEvents;
Polymer.accumulateHostEvents = accumulateHostEvents;
Polymer.bindAccumulatedHostEvents = bindAccumulatedHostEvents;
Polymer.bindAccumulatedLocalEvents = bindAccumulatedLocalEvents;
})();
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// usage
// invoke cb.call(this) in 100ms, unless the job is re-registered,
// which resets the timer
//
// this.myJob = this.job(this.myJob, cb, 100)
//
// returns a job handle which can be used to re-register a job
var Job = function(inContext) {
this.context = inContext;
};
Job.prototype = {
go: function(inCallback, inWait) {
this.callback = inCallback;
this.handle = setTimeout(function() {
this.handle = null;
inCallback.call(this.context);
}.bind(this), inWait);
},
stop: function() {
if (this.handle) {
clearTimeout(this.handle);
this.handle = null;
}
},
complete: function() {
if (this.handle) {
this.stop();
this.callback.call(this.context);
}
}
};
function job(inJob, inCallback, inWait) {
var job = inJob || new Job(this);
job.stop();
job.go(inCallback, inWait);
return job;
}
Polymer.job = job;
})();
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
// exports
window.forEach = forEach;
})();
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
//
// reference marshalling
//
// locate nodes with id and store references to them in this.$ hash
Polymer.marshalNodeReferences = function(inRoot) {
// establish $ instance variable
var $ = this.$ = this.$ || {};
// populate $ from nodes with ID from the LOCAL tree
if (inRoot) {
var nodes = inRoot.querySelectorAll("[id]");
forEach(nodes, function(n) {
$[n.id] = n;
});
}
};
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
//
// automagic property observation side effects
// this implementation uses MDV polyfill
//
// imports
var log = window.logFlags || {};
var OBSERVE_SUFFIX = 'Changed';
function observeProperties() {
for (var p in this) {
observeProperty.call(this, p);
}
}
function observeProperty(inName) {
if (isObservable.call(this, inName)) {
log.observe && console.log('[' + this.localName + '] watching [' + inName + ']');
var observer = new PathObserver(this, inName, function(inNew, inOld) {
log.data && console.log('[%s#%s] watch: [%s] now [%s] was [%s]', this.localName, this.node.id || '', inName, this[inName], inOld);
propertyChanged.call(this, inName, inOld);
}.bind(this));
Polymer.registerObserver(this, 'property', inName, observer);
}
}
function isObservable(inName) {
return (inName[0] != '_')
&& !(inName in Object.prototype)
&& Boolean(this[inName + OBSERVE_SUFFIX]);
}
function propertyChanged(inName, inOldValue) {
//log.data && console.log('[%s#%s] propertyChanged: [%s] now [%s] was [%s]', this.node.localName, this.node.id || '', inName, this[inName], inOldValue);
var fn = inName + OBSERVE_SUFFIX;
if (this[fn]) {
this[fn](inOldValue);
}
}
// exports
Polymer.observeProperties = observeProperties;
})();
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// super
// TODO(sjmiles):
// $super must be installed on an instance or prototype chain
// as `super`, and invoked via `this`, e.g.
// `this.super();`
// will not work if function objects are not unique, for example,
// when using mixins.
// The memoization strategy assumes each function exists on only one
// prototype chain i.e. we use the function object for memoizing)
// perhaps we can bookkeep on the prototype itself instead
function $super(inArgs) {
// since we are thunking a method call, performance is important here:
// memoize all lookups, once memoized the fast path calls no other
// functions
//
// find the caller (cannot be `strict` because of 'caller')
var caller = $super.caller;
// memoization for 'name of method'
var nom = caller.nom;
if (!nom) {
nom = nameInThis.call(this, caller);
}
if (!nom) {
console.warn('called super() on a method not installed declaratively (has no .nom property)');
}
// super prototype is either cached or we have to find it
// by searching __proto__ (at the 'top')
if (!('_super' in caller)) {
memoizeSuper(caller, nom, Object.getPrototypeOf(this));
}
// memoized next implementation prototype
var _super = caller._super;
if (!_super) {
// if _super is falsey, there is no super implementation
//console.warn('called $super(' + nom + ') where there is no super implementation');
} else {
// our super function
var fn = _super[nom];
// memoize information so 'fn' can call 'super'
if (!('_super' in fn)) {
memoizeSuper(fn, nom, _super);
}
// invoke the inherited method
// if 'fn' is not function valued, this will throw
return fn.apply(this, inArgs || []);
}
};
function nextSuper(inProto, inName, inCaller) {
// look for an inherited prototype that implements inName
var proto = inProto;
while (proto &&
(!proto.hasOwnProperty(inName) || proto[inName] == inCaller)) {
proto = Object.getPrototypeOf(proto);
}
return proto;
};
function memoizeSuper(inMethod, inName, inProto) {
// find and cache next prototype containing inName
// we need the prototype so we can another lookup
// from here
inMethod._super = nextSuper(inProto, inName, inMethod);
if (inMethod._super) {
// _super is a prototype, the actual method is _super[inName]
// tag super method with it's name for further lookups
inMethod._super[inName]._nom = inName;
}
};
function nameInThis(inValue) {
console.group('nameInThis');
var p = this;
while (p && p !== HTMLElement.prototype) {
var n$ = Object.getOwnPropertyNames(p);
for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
console.log(n);
var d = Object.getOwnPropertyDescriptor(p, n);
if (d.value == inValue) {
return n;
}
}
p = Object.getPrototypeOf(p);
}
console.groupEnd('nameInThis');
}
// exports
// `super` is a reserved word
scope.$super = $super;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
function addResolvePath(inPrototype, inElement) {
var root = calcElementPath(inElement);
inPrototype.resolvePath = function(inPath) {
return root + inPath;
}
}
function urlToPath(inUrl) {
if (inUrl) {
var parts = inUrl.split('/');
parts.pop();
parts.push('');
return parts.join('/');
} else {
return '';
}
}
function calcElementPath(inElement) {
return urlToPath(HTMLImports.getDocumentUrl(inElement.ownerDocument));
}
// exports
Polymer.addResolvePath = addResolvePath;
})();
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
(function(global) {
'use strict';
function Scope() {}
var bindPattern = /([\w\.\$]*)[\s]+as[\s]+([\w]*)/;
var repeatPattern = /([\w]*)[\s]+in[\s]+([\w\.\$]*)/;
function createBindRepeatBinding(model, path, name, node) {
var scopeName, scopePath;
var match = path.match(repeatPattern);
if (match) {
scopeName = match[1];
scopePath = match[2];
} else {
match = path.match(bindPattern);
if (match) {
scopeName = match[2];
scopePath = match[1];
} else {
return;
}
}
var binding = new CompoundBinding(function(values) {
return values['value'];
});
binding.bind('value', model, scopePath);
templateScopeTable.set(node, { model: model, scope: scopeName });
return binding;
}
function createStringIfTruthyBinding(model, className, path) {
var binding = new CompoundBinding(function(values) {
return values['value'] ? className : '';
});
binding.bind('value', model, path);
return binding;
}
var templateScopeTable = new SideTable;
HTMLTemplateElement.syntax['Polymer'] = {
getBinding: function(model, path, name, node) {
if (node.nodeType === Node.ELEMENT_NODE &&
(name === 'bind' || name === 'repeat') &&
node.tagName === 'TEMPLATE') {
return createBindRepeatBinding(model, path, name, node);
}
// {{ string: path }}
var match = path.match(/([\w]+):[\W]*([\w\.\$]*)/);
if (match)
return createStringIfTruthyBinding(model, match[1], match[2]);
},
getInstanceModel: function(template, model) {
var scopeInfo = templateScopeTable.get(template);
if (!scopeInfo)
return model;
var scope;
if (scopeInfo.model) { // instanceof Scope) {
scope = Object.create(scopeInfo.model);
} else {
scope = new Scope();
}
scope[scopeInfo.scope] = model;
return scope;
}
};
})(this);
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
// api
function register(inElement, inPrototype) {
// in the main document, parser runs script in <element> tags in the wrong
// context, filter that out here
if (inElement == window) {
return;
}
// catch common mistake of omitting 'this' in call to register
if (!inElement || !(inElement instanceof HTMLElement)) {
throw "First argument to Polymer.register must be an HTMLElement";
}
// TODO(sjmiles): it's not obvious at this point whether inElement
// will chain to another polymer element, so we just copy base boilerplate
// anyway
// this can result in multiple copies of boilerplate methods on a custom
// element chain, which is inefficient and has ramifications for 'super'
// also, we don't yet support intermediate prototypes in calls to
// HTMLElementElement.prototype.register, so we have to use mixin
var prototype = Platform.mixin({}, scope.base, inPrototype);
// capture defining element
prototype.elementElement = inElement;
// TODO(sorvell): install a helper method this.resolvePath to aid in
// setting resource paths. e.g.
// this.$.image.src = this.resolvePath('images/foo.png')
// Potentially remove when spec bug is addressed.
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407
scope.addResolvePath(prototype, inElement);
// install instance method that closes over 'inElement'
prototype.installTemplate = function() {
this.super();
staticInstallTemplate.call(this, inElement);
};
// hint the supercall mechanism
// TODO(sjmiles): make prototype extension api that does this
prototype.installTemplate.nom = 'installTemplate';
// install callbacks
prototype.readyCallback = readyCallback;
prototype.insertedCallback = insertedCallback;
prototype.removedCallback = removedCallback;
prototype.attributeChangedCallback = attributeChangedCallback;
// hint super call engine by tagging methods with names
hintSuper(prototype);
// parse declared on-* delegates into imperative form
scope.parseHostEvents(inElement.attributes, prototype);
// parse attribute-attributes
scope.publishAttributes(inElement, prototype);
// install external stylesheets as if they are inline
scope.installSheets(inElement);
scope.shimStyling(inElement);
// invoke element.register
inElement.register({prototype: prototype});
// logging
logFlags.comps &&
console.log("Polymer: element registered" + inElement.options.name);
};
function readyCallback() {
// invoke 'installTemplate' closure
this.installTemplate();
// invoke boilerplate 'instanceReady'
instanceReady.call(this);
};
function staticInstallTemplate(inElement) {
var template = inElement.querySelector('template');
if (template) {
// make a shadow root
var root = this.webkitCreateShadowRoot();
// TODO(sjmiles): must be settable ex post facto
root.applyAuthorStyles = this.applyAuthorStyles;
// TODO(sjmiles): override createShadowRoot to do this automatically
CustomElements.watchShadow(this);
// TODO(sorvell): host not set per spec; we set it for convenience
// so we can traverse from root to host.
root.host = this;
// parse and apply MDV bindings
// do this before being inserted to avoid {{}} in attribute values
// e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
root.appendChild(template.createInstance(this, Polymer.expressionSyntax));
rootCreated.call(this, root);
return root;
}
};
function rootCreated(inRoot) {
// to resolve this node synchronously we must process CustomElements
// in the subtree immediately
CustomElements.takeRecords();
// parse and apply MDV bindings
// locate nodes with id and store references to them in this.$ hash
scope.marshalNodeReferences.call(this, inRoot);
// add local events of interest...
var rootEvents = scope.accumulateEvents(inRoot);
scope.bindAccumulatedLocalEvents.call(this, inRoot, rootEvents);
// set up gestures
PointerGestures.register(inRoot);
PointerEventsPolyfill.setTouchAction(inRoot,
this.getAttribute('touch-action'));
};
function instanceReady(inElement) {
// install property observation side effects
// do this first so we can observe changes during initialization
scope.observeProperties.call(this);
// install boilerplate attributes
scope.installInstanceAttributes.call(this);
// process input attributes
scope.takeAttributes.call(this);
// add host-events...
var hostEvents = scope.accumulateHostEvents.call(this);
scope.bindAccumulatedHostEvents.call(this, hostEvents);
// asynchronously unbindAll... will be cancelled if inserted
this.asyncUnbindAll();
// invoke user 'ready'
if (this.ready) {
this.ready();
}
};
function insertedCallback() {
this.cancelUnbindAll(true);
// invoke user 'inserted'
if (this.inserted) {
this.inserted();
}
}
function removedCallback() {
this.asyncUnbindAll();
// invoke user 'removed'
if (this.removed) {
this.removed();
}
}
function attributeChangedCallback() {
if (this.attributeChanged) {
this.attributeChanged.apply(this, arguments);
}
}
function hintSuper(prototype) {
Object.getOwnPropertyNames(prototype).forEach(function(n) {
var d = Object.getOwnPropertyDescriptor(prototype, n);
if (typeof d.value == 'function') {
d.value.nom = n;
}
});
}
// user utility
function findDistributedTarget(inTarget, inNodes) {
// find first ancestor of target (including itself) that
// is in inNodes, if any
var n = inTarget;
while (n && n != this) {
var i = Array.prototype.indexOf.call(inNodes, n);
if (i >= 0) {
return i;
}
n = n.parentNode;
}
}
// exports
scope.register = register;
scope.findDistributedTarget = findDistributedTarget;
scope.instanceReady = instanceReady;
})(Polymer);
/*
* Copyright 2012 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/*
This is a limited shim for shadowDOM css styling.
https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
The intention here is to support only the styling features which can be
relatively simply implemented. The goal is to allow users to avoid the
most obvious pitfalls and do so without compromising performance significantly.
For shadowDOM styling that's not covered here, a set of best practices
can be provided that should allow users to accomplish more complex styling.
The following is a list of specific shadowDOM styling features and a brief
discussion of the approach used to shim.
Shimmed features:
* @host: ShadowDOM allows styling of the shadowRoot's host element using the
@host rule. To shim this feature, the @host styles are reformatted and
prefixed with a given scope name and promoted to a document level stylesheet.
For example, given a scope name of .foo, a rule like this:
@host {
* {
background: red;
}
}
becomes:
.foo {
background: red;
}
* encapsultion: Styles defined within shadowDOM, apply only to
dom inside the shadowDOM. Polymer uses one of two techniques to imlement
this feature.
By default, rules are prefixed with the host element tag name
as a descendant selector. This ensures styling does not leak out of the 'top'
of the element's shadowDOM. For example,
div {
font-weight: bold;
}
becomes:
x-foo div {
font-weight: bold;
}
becomes:
Alternatively, if Polymer.strictPolyfillStyling is set to true then
selectors are scoped by adding an attribute selector suffix to each
simple selector that contains the host element tag name. Each element
in the element's shadowDOM template is also given the scope attribute.
Thus, these rules match only elements that have the scope attribute.
For example, given a scope name of x-foo, a rule like this:
div {
font-weight: bold;
}
becomes:
div[x-foo] {
font-weight: bold;
}
Note that elements that are dynamically added to a scope must have the scope
selector added to them manually.
* ::pseudo: These rules are converted to rules that take advantage of the
pseudo attribute. For example, a shadowRoot like this inside an x-foo
<div pseudo="x-special">Special</div>
with a rule like this:
x-foo::x-special { ... }
becomes:
x-foo [pseudo=x-special] { ... }
Unaddressed shadowDOM styling features:
* upper/lower bound encapsulation: Styles which are defined outside a
shadowRoot should not cross the shadowDOM boundary and should not apply
inside a shadowRoot.
This styling behavior is not emulated. Some possible ways to do this that
were rejected due to complexity and/or performance concerns include: (1) reset
every possible property for every possible selector for a given scope name;
(2) re-implement css in javascript.
As an alternative, users should make sure to use selectors
specific to the scope in which they are working.
* ::distributed: This behavior is not emulated. It's often not necessary
to style the contents of a specific insertion point and instead, descendants
of the host element can be styled selectively. Users can also create an
extra node around an insertion point and style that node's contents
via descendent selectors. For example, with a shadowRoot like this:
<style>
content::-webkit-distributed(div) {
background: red;
}
</style>
<content></content>
could become:
<style>
/ *@polyfill .content-container div * /
content::-webkit-distributed(div) {
background: red;
}
</style>
<div class="content-container">
<content></content>
</div>
Note the use of @polyfill in the comment above a shadowDOM specific style
declaration. This is a directive to the styling shim to use the selector
in comments in lieu of the next selector when running under polyfill.
*/
(function(scope) {
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
var concat = Array.prototype.concat.call.bind(Array.prototype.concat);
var slice = Array.prototype.slice.call.bind(Array.prototype.slice);
var stylizer = {
hostRuleRe: /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
selectorRe: /([^{]*)({[\s\S]*?})/gim,
hostElementRe: /(.*)((?:\*)|(?:\:scope))(.*)/,
hostFixableRe: /^[.\[:]/,
cssCommentRe: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
cssPolyfillCommentRe: /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,
cssPseudoRe: /::(x-[^\s{,(]*)/gim,
selectorReSuffix: '([>\\s~+\[.,{:][\\s\\S]*)?$',
hostRe: /@host/gim,
cache: {},
shimStyling: function(element) {
if (window.ShadowDOMPolyfill && element) {
// use caching to make working with styles nodes easier and to facilitate
// lookup of extendee
var name = element.options.name;
stylizer.cacheDefinition(element);
stylizer.shimPolyfillDirectives(element.styles, name);
// find styles and apply shimming...
if (Polymer.strictPolyfillStyling) {
stylizer.applyScopeToContent(element.templateContent, name);
}
stylizer.applyShimming(stylizer.stylesForElement(element), name);
}
},
// Shim styles to be placed inside a shadowRoot.
// 1. shim @host rules and inherited @host rules
// 2. shim scoping: apply .scoped when available or pseudo-scoping when not
// (e.g. a selector 'div' becomes 'x-foo div')
shimShadowDOMStyling: function(styles, name) {
if (window.ShadowDOMPolyfill) {
stylizer.shimPolyfillDirectives(styles, name);
stylizer.applyShimming(styles, name);
}
},
applyShimming: function(styles, name) {
var cssText = this.shimAtHost(styles, name);
cssText += this.shimScoping(styles, name);
this.addCssToDocument(cssText);
},
cacheDefinition: function(element) {
var name = element.options.name;
var template = element.querySelector('template');
var content = template && templateContent(template);
var styles = content && content.querySelectorAll('style');
element.styles = styles ? slice(styles) : [];
element.templateContent = content;
stylizer.cache[name] = element;
},
applyScopeToContent: function(root, name) {
if (root) {
forEach(root.querySelectorAll('*'), function(node) {
node.setAttribute(name, '');
});
forEach(root.querySelectorAll('template'), function(template) {
this.applyScopeToContent(templateContent(template), name);
}, this);
}
},
stylesForElement: function(element) {
var styles = element.styles;
var shadow = element.templateContent &&
element.templateContent.querySelector('shadow');
if (shadow || (element.templateContent === null)) {
var extendee = this.findExtendee(element.options.name);
if (extendee) {
var extendeeStyles = this.stylesForElement(extendee);
styles = concat(slice(extendeeStyles), slice(styles));
}
}
return styles;
},
findExtendee: function(name) {
var element = this.cache[name];
return element && this.cache[element.options.extends];
},
/*
* Process styles to convert native ShadowDOM rules that will trip
* up the css parser; we rely on decorating the stylesheet with comments.
*
* For example, we convert this rule:
*
* (comment start) @polyfill @host g-menu-item (comment end)
* shadow::-webkit-distributed(g-menu-item) {
*
* to this:
*
* scopeName g-menu-item {
*
**/
shimPolyfillDirectives: function(styles, name) {
if (window.ShadowDOMPolyfill) {
if (styles) {
forEach(styles, function(s) {
s.textContent = this.convertPolyfillDirectives(s.textContent, name);
}, this);
}
}
},
// form: @host { .foo { declarations } }
// becomes: scopeName.foo { declarations }
shimAtHost: function(styles, name) {
if (styles) {
return this.convertAtHostStyles(styles, name);
}
},
/* Ensure styles are scoped. Pseudo-scoping takes a rule like:
*
* .foo {... }
*
* and converts this to
*
* scopeName .foo { ... }
*/
shimScoping: function(styles, name) {
if (styles) {
return this.convertScopedStyles(styles, name);
}
},
convertPolyfillDirectives: function(cssText, name) {
var r = '', l = 0, matches, selector;
while (matches=this.cssPolyfillCommentRe.exec(cssText)) {
r += cssText.substring(l, matches.index);
// remove end comment delimiter (*/)
selector = matches[1].slice(0, -2).replace(this.hostRe, name);
r += this.scopeSelector(selector, name) + '{';
l = this.cssPolyfillCommentRe.lastIndex;
}
r += cssText.substring(l, cssText.length);
return r;
},
// consider styles that do not include component name in the selector to be
// unscoped and in need of promotion;
// for convenience, also consider keyframe rules this way.
findAtHostRules: function(cssRules, matcher) {
return Array.prototype.filter.call(cssRules,
this.isHostRule.bind(this, matcher));
},
isHostRule: function(matcher, cssRule) {
return (cssRule.selectorText && cssRule.selectorText.match(matcher)) ||
(cssRule.cssRules && this.findAtHostRules(cssRule.cssRules, matcher).length) ||
(cssRule.type == CSSRule.WEBKIT_KEYFRAMES_RULE);
},
convertAtHostStyles: function(styles, name) {
var cssText = this.stylesToCssText(styles);
var r = '', l=0, matches;
while (matches=this.hostRuleRe.exec(cssText)) {
r += cssText.substring(l, matches.index);
r += this.scopeHostCss(matches[1], name);
l = this.hostRuleRe.lastIndex;
}
r += cssText.substring(l, cssText.length);
var selectorRe = new RegExp('^' + name + this.selectorReSuffix, 'm');
var cssText = this.rulesToCss(this.findAtHostRules(this.cssToRules(r),
selectorRe));
return cssText;
},
scopeHostCss: function(cssText, name) {
var r = '', matches;
while (matches = this.selectorRe.exec(cssText)) {
r += this.scopeHostSelector(matches[1], name) +' ' + matches[2] + '\n\t';
}
return r;
},
// supports scopig by name and [is=name] syntax
scopeHostSelector: function(selector, name) {
var r = [], parts = selector.split(','), is = '[is=' + name + ']';
parts.forEach(function(p) {
p = p.trim();
// selector: *|:scope -> name
if (p.match(this.hostElementRe)) {
p = p.replace(this.hostElementRe, name + '$1$3, ' + is + '$1$3');
// selector: .foo -> name.foo, [bar] -> name[bar]
} else if (p.match(this.hostFixableRe)) {
p = name + p + ', ' + is + p;
}
r.push(p);
}, this);
return r.join(', ');
},
convertScopedStyles: function(styles, name) {
forEach(styles, function(s) {
if (s.parentNode) {
s.parentNode.removeChild(s);
}
});
var cssText = this.stylesToCssText(styles).replace(this.hostRuleRe, '');
cssText = this.convertPseudos(cssText);
var rules = this.cssToRules(cssText);
cssText = this.scopeRules(rules, name);
return cssText;
},
convertPseudos: function(cssText) {
return cssText.replace(this.cssPseudoRe, ' [pseudo=$1]');
},
// change a selector like 'div' to 'name div'
scopeRules: function(cssRules, name) {
var cssText = '';
forEach(cssRules, function(rule) {
if (rule.selectorText && (rule.style && rule.style.cssText)) {
cssText += this.scopeSelector(rule.selectorText, name,
Polymer.strictPolyfillStyling) + ' {\n\t';
cssText += this.propertiesFromRule(rule) + '\n}\n\n';
} else if (rule.media) {
cssText += '@media ' + rule.media.mediaText + ' {\n';
cssText += this.scopeRules(rule.cssRules, name);
cssText += '\n}\n\n';
} else if (rule.cssText) {
cssText += rule.cssText + '\n\n';
}
}, this);
return cssText;
},
propertiesFromRule: function(rule) {
var properties = rule.style.cssText;
// TODO(sorvell): Chrome cssom incorrectly removes quotes from the content
// property. (https://code.google.com/p/chromium/issues/detail?id=247231)
if (rule.style.content && !rule.style.content.match(/['"]+/)) {
properties = 'content: \'' + rule.style.content + '\';\n' +
rule.style.cssText.replace(/content:[^;]*;/g, '');
}
return properties;
},
selectorNeedsScoping: function(selector, name) {
var matchScope = '(' + name + '|\\[is=' + name + '\\])';
var selectorRe = new RegExp('^' + matchScope + this.selectorReSuffix, 'm');
return !selector.match(selectorRe);
},
scopeSelector: function(selector, name, strict) {
var r = [], parts = selector.split(',');
parts.forEach(function(p) {
p = p.trim();
if (this.selectorNeedsScoping(p, name)) {
p = strict ? this.applyStrictSelectorScope(p, name) :
this.applySimpleSelectorScope(p, name);
}
r.push(p);
}, this);
return r.join(', ');
},
// scope via name and [is=name]
applySimpleSelectorScope: function(selector, name) {
return name + ' ' + selector + ', ' + '[is=' + name + '] ' + selector;
},
// return a selector with [name] suffix on each simple selector
// e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
applyStrictSelectorScope: function(selector, name) {
var splits = [' ', '>', '+', '~'],
scoped = selector,
attrName = '[' + name + ']';
splits.forEach(function(sep) {
var parts = scoped.split(sep);
scoped = parts.map(function(p) {
var t = p.trim();
if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
}
return p;
}).join(sep);
});
return scoped;
},
stylesToCssText: function(styles, preserveComments) {
var cssText = '';
forEach(styles, function(s) {
cssText += s.textContent + '\n\n';
});
// strip comments for easier processing
if (!preserveComments) {
cssText = this.stripCssComments(cssText);
}
return cssText;
},
stripCssComments: function(cssText) {
return cssText.replace(this.cssCommentRe, '');
},
cssToRules: function(cssText) {
var style = document.createElement('style');
style.textContent = cssText;
document.head.appendChild(style);
var rules = style.sheet.cssRules;
style.parentNode.removeChild(style);
return rules;
},
rulesToCss: function(cssRules) {
for (var i=0, css=[]; i < cssRules.length; i++) {
css.push(cssRules[i].cssText);
}
return css.join('\n\n');
},
addCssToDocument: function(cssText) {
if (cssText) {
this.getSheet().appendChild(document.createTextNode(cssText));
}
},
// support for creating @host rules
getSheet: function() {
if (!this.sheet) {
this.sheet = document.createElement("style");
this.sheet.setAttribute('polymer-polyfill', '');
}
return this.sheet;
},
addSheetToDocument: function() {
this.addCssToDocument('style { display: none !important; }\n');
var head = document.querySelector('head');
head.insertBefore(this.getSheet(), head.childNodes[0]);
}
};
// add polyfill stylesheet to document
if (window.ShadowDOMPolyfill) {
stylizer.addSheetToDocument();
}
// exports
Polymer.shimStyling = stylizer.shimStyling;
Polymer.shimShadowDOMStyling = stylizer.shimShadowDOMStyling;
Polymer.shimPolyfillDirectives = stylizer.shimPolyfillDirectives.bind(stylizer);
Polymer.strictPolyfillStyling = false;
})(window);
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// imports
var log = window.logFlags || {};
var doc = wrap(document);
/**
* Install external stylesheets loaded in <element> elements into the
* element's template.
* @param elementElement The <element> element to style.
*/
function installSheets(elementElement) {
installLocalSheets(elementElement);
installGlobalStyles(elementElement);
}
/**
* Takes external stylesheets loaded in an <element> element and moves
* their content into a <style> element inside the <element>'s template.
* The sheet is then removed from the <element>. This is done only so
* that if the element is loaded in the main document, the sheet does
* not become active.
* Note, ignores sheets with the attribute 'polymer-scope'.
* @param elementElement The <element> element to style.
*/
function installLocalSheets(elementElement) {
var sheets = findInElement(elementElement, SHEET_SELECTOR, function(s) {
return !s.hasAttribute(SCOPE_ATTR);
});
var content = elementTemplateContent(elementElement);
if (content) {
// in case we're in document, remove from element
var cssText = '';
sheets.forEach(function(sheet) {
sheet.parentNode.removeChild(sheet);
cssText += cssTextFromSheet(sheet) + '\n';
});
if (cssText) {
content.insertBefore(createStyleElement(cssText), content.firstChild);
}
}
}
/**
* Promotes external stylesheets and <style> elements with the attribute
* polymer-scope='global' into global scope.
* This is particularly useful for defining @keyframe rules which
* currently do not function in scoped or shadow style elements.
* (See wkb.ug/72462)
* @param elementElement The <element> element to style.
*/
// TODO(sorvell): remove when wkb.ug/72462 is addressed.
function installGlobalStyles(elementElement) {
applyStyleToScope(styleFromElement(elementElement, STYLE_GLOBAL_SCOPE),
doc.head);
}
/**
* Installs external stylesheets and <style> elements with the attribute
* polymer-scope='controller' into the scope of element. This is intended
* to be a called during custom element construction. Note, this incurs a per
* instance cost and should be used sparingly.
* The need for this type of styling should go away when the shadowDOM spec
* addresses these issues:
*
* https://www.w3.org/Bugs/Public/show_bug.cgi?id=21391
* https://www.w3.org/Bugs/Public/show_bug.cgi?id=21390
* https://www.w3.org/Bugs/Public/show_bug.cgi?id=21389
*
* @param element The custom element instance into whose controller (parent)
* scope styles will be installed.
* @param elementElement The <element> containing controller styles.
*/
// TODO(sorvell): remove when spec issues are addressed
function installControllerStyles(element, elementElement) {
if (!elementElement.controllerStyle) {
elementElement.controllerStyle = styleFromElement(elementElement,
STYLE_CONTROLLER_SCOPE);
}
var styleElement = elementElement.controllerStyle;
var scope = findStyleController(element);
// apply controller styles only if they are not yet applied
if (scope && !scopeHasElementStyle(scope, element,
STYLE_CONTROLLER_SCOPE)) {
Polymer.shimPolyfillDirectives([styleElement], element.localName);
applyStyleToScope(styleElement, scope);
}
}
function scopeHasElementStyle(scope, element, descriptor) {
return scope.querySelector('style[' + STYLE_SCOPE_ATTRIBUTE + '=' +
element.localName + '-' + descriptor + ']');
}
function cssTextFromElement(elementElement, descriptor) {
var cssText = '';
// handle stylesheets
var selector = '[' + SCOPE_ATTR + '=' + descriptor + ']';
var matcher = function(s) {
return matchesSelector(s, selector);
};
var sheets = findInElement(elementElement, SHEET_SELECTOR, matcher);
sheets.forEach(function(sheet) {
// in case we're in document, remove from element
sheet.parentNode.removeChild(sheet);
cssText += cssTextFromSheet(sheet) + '\n\n';
});
// handle style elements
var styles = findInElement(elementElement, STYLE_SELECTOR, matcher);
styles.forEach(function(style) {
// in case we're in document, remove from element
style.parentNode.removeChild(style);
cssText += style.textContent + '\n\n';
});
return cssText;
}
function styleFromElement(elementElement, descriptor) {
var cssText = cssTextFromElement(elementElement, descriptor);
if (cssText) {
var style = createStyleElement(cssText);
style.setAttribute(STYLE_SCOPE_ATTRIBUTE, elementElement.options.name +
'-' + descriptor);
return style;
}
}
function findInElement(elementElement, selector, matcher) {
var nodes = arrayFromNodeList(elementElement
.querySelectorAll(selector));
var content = elementTemplateContent(elementElement);
if (content) {
var templateNodes = arrayFromNodeList(content
.querySelectorAll(selector));
nodes = nodes.concat(templateNodes);
}
return nodes.filter(matcher);
}
function findStyleController(node) {
// find the shadow root that contains inNode
var n = node;
while (n.parentNode) {
n = n.parentNode;
}
return n == doc ? doc.head : n;
};
function createStyleElement(cssText) {
var style = document.createElement('style');
style.textContent = cssText;
return style;
}
function cssTextFromSheet(sheet) {
return (sheet && sheet.__resource) || '';
}
function applyStyleToScope(style, scope) {
if (style) {
var clone = style.cloneNode(true);
// TODO(sorvell): necessary for IE
// see https://connect.microsoft.com/IE/feedback/details/790212/
// cloning-a-style-element-and-adding-to-document-produces
// -unexpected-result#details
clone.textContent = style.textContent;
scope.appendChild(clone);
}
}
var eltProto = HTMLElement.prototype;
var matches = eltProto.matches || eltProto.matchesSelector ||
eltProto.webkitMatchesSelector || eltProto.mozMatchesSelector;
function matchesSelector(node, inSelector) {
if (matches) {
return matches.call(node, inSelector);
}
}
function elementTemplateContent(elementElement) {
var template = elementElement.querySelector('template');
return template && templateContent(template);
}
var STYLE_SELECTOR = 'style';
var SHEET_SELECTOR = '[rel=stylesheet]';
var STYLE_SCOPE_ATTRIBUTE = 'element';
var STYLE_GLOBAL_SCOPE = 'global';
var STYLE_CONTROLLER_SCOPE = 'controller';
var SCOPE_ATTR = 'polymer-scope';
function arrayFromNodeList(nodeList) {
return Array.prototype.slice.call(nodeList || [], 0);
}
// exports
Polymer.installSheets = installSheets;
Polymer.installControllerStyles = installControllerStyles;
})();
\ No newline at end of file
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function() {
// observer tracking
var trackingTable = new SideTable();
function registerObserver(element, type, name, observer) {
var o$ = getObserversOfType(element, type, true);
o$[name.toLowerCase()] = observer;
}
function unregisterObserver(element, type, name) {
var $o = getObserversOfType(element, type), lcName = name.toLowerCase();
if ($o && $o[lcName]) {
//console.log('remove observer: %s, %s', type, name);
$o[lcName].close();
$o[lcName] = null;
return true;
}
}
function unregisterObserversOfType(element, type) {
var $o = getObserversOfType(element, type);
if ($o) {
Object.keys($o).forEach(function(key) {
unregisterObserver(element, type, key);
});
}
}
function getObserversOfType(element, type, force) {
var b$ = trackingTable.get(element);
if (force) {
if (!b$) {
trackingTable.set(element, b$ = {});
}
if (!b$[type]) {
b$[type] = {};
}
}
return b$ && b$[type];
}
// exports
Polymer.registerObserver = registerObserver;
Polymer.unregisterObserver = unregisterObserver;
Polymer.unregisterObserversOfType = unregisterObserversOfType;
})();
<polymer-element name="td-input" extends="input" on-keyup="keyupAction" on-keypress="keypressAction">
<script>
(function() {
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer('td-input', {
keypressAction: function (e, detail, sender) {
// Listen for enter on keypress but esc on keyup, because
// IE doesn't fire keyup for enter.
if (e.keyCode === ENTER_KEY) {
this.fire('td-input-commit');
}
},
keyupAction: function (e, detail, sender) {
if (e.keyCode === ESC_KEY) {
this.fire('td-input-cancel');
}
}
});
})();
</script>
</polymer-element>
......@@ -124,8 +124,7 @@ label {
}
.destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
text-shadow: 0 0 1px #000, 0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3);
......@@ -148,6 +147,11 @@ label {
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle {
background: none;
/*
ShadowDOM Polyfill work around for webkit/blink bug
https://code.google.com/p/chromium/issues/detail?id=251510
*/
background-color: transparent;
}
.toggle {
......
<element name="td-item" extends="li" attributes="item editing" on-blur="commitAction">
<link rel="stylesheet" href="td-item.css">
<link rel="import" href="td-input.html">
<polymer-element name="td-item" extends="li" attributes="item editing" on-blur="commitAction">
<template>
<div class="view {{completed: item.completed; editing: editing}}" hidden?="{{editing}}">
<link rel="stylesheet" href="td-item.css">
<div class="view {{completed: item.completed; editing: editing}}" hidden?="{{editing}}" on-dblclick="editAction">
<input type="checkbox" class="toggle" checked="{{item.completed}}" on-click="itemChangeAction">
<label on-dblclick="editAction">{{item.title}}</label>
<label>{{item.title}}</label>
<button class="destroy" on-click="destroyAction"></button>
</div>
<input id="edit" class="edit" value="{{title}}" hidden?="{{!editing}}" on-keyup="keyAction">
<input is="td-input" id="edit" class="edit" value="{{title}}" hidden?="{{!editing}}" on-td-input-commit="commitAction" on-td-input-cancel="cancelAction">
</template>
<script>
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer.register(this, {
editing: false,
keyAction: function (e) {
switch (e.keyCode) {
case ESC_KEY:
this.cancelAction();
break;
case ENTER_KEY:
this.commitAction();
break;
}
},
editAction: function () {
this.editing = true;
this.title = this.item.title;
// schedule focus for the end of microtask, when the input will be visible
Platform.flush();
this.asyncMethod(function () {
this.$.edit.focus();
});
},
commitAction: function () {
if (this.editing) {
this.editing = false;
this.item.title = this.title.trim();
if (this.item.title === '') {
this.destroyAction();
(function () {
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer('td-item', {
editing: false,
editAction: function () {
this.editing = true;
// FIXME: Custom elements extended from <input> don't have
// <input> binding behavior.
// https://github.com/Polymer/polymer/issues/186
this.$.edit.value = this.title = this.item.title;
// schedule focus for the end of microtask, when the input will be visible
Platform.flush();
this.asyncMethod(function () {
this.$.edit.focus();
});
},
commitAction: function () {
// FIXME: Custom elements extended from <input> don't have
// <input> binding behavior.
// https://github.com/Polymer/polymer/issues/186
this.title = this.$.edit.value;
if (this.editing) {
this.editing = false;
this.item.title = this.title.trim();
if (this.item.title === '') {
this.destroyAction();
}
this.fire('td-item-changed');
}
},
cancelAction: function() {
this.editing = false;
},
itemChangeAction: function() {
this.fire('td-item-changed');
},
destroyAction: function() {
this.fire('td-destroy-item', this.item);
}
},
cancelAction: function () {
this.editing = false;
},
itemChangeAction: function () {
this.fire('td-item-changed');
},
destroyAction: function () {
this.fire('td-destroy-item', this.item);
}
});
});
})();
</script>
</element>
</polymer-element>
<element name="td-model" attributes="filter items">
<polymer-element name="td-model" attributes="filter items storageId">
<script>
Polymer.register(this, {
Polymer('td-model', {
filtered: null,
completedCount: 0,
activeCount: 0,
......@@ -14,11 +14,21 @@
this.filterItems();
},
itemsChanged: function () {
this.completedCount = this.items.filter(this.filters.completed).length;
this.completedCount =
this.items.filter(this.filters.completed).length;
this.activeCount = this.items.length - this.completedCount;
this.allCompleted = this.completedCount && !this.activeCount;
this.filterItems();
this.fire('td-model-changed');
if (this.storage) {
this.storage.value = this.items;
this.storage.save();
}
},
storageIdChanged: function () {
this.storage = document.querySelector('#' + this.storageId);
if (this.storage) {
this.items = this.storage.value;
}
},
filterItems: function () {
var fn = this.filters[this.filter];
......@@ -27,10 +37,11 @@
newItem: function (title) {
title = String(title).trim();
if (title) {
this.items.push({
var item = {
title: title,
completed: false
});
};
this.items.push(item);
this.itemsChanged();
}
},
......@@ -60,4 +71,4 @@
}
});
</script>
</element>
</polymer-element>
/* base.css overrides */
@host {
* {
@host {
* {
display: block;
position: relative;
}
......@@ -17,7 +17,6 @@ button {
font-family: inherit;
color: inherit;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
......@@ -27,7 +26,12 @@ input::-webkit-input-placeholder {
font-style: italic;
}
input:-moz-placeholder {
input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
input:-ms-input-placeholder, #new-todo:-ms-input-placeholder {
font-style: italic;
color: #a9a9a9;
}
......@@ -35,7 +39,6 @@ input:-moz-placeholder {
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
/*margin: 130px 0 40px 0;*/
margin: 0 0 40px 0;
border: 1px solid #ccc;
position: relative;
......@@ -218,7 +221,7 @@ label[for='toggle-all'] {
text-decoration: none;
}
#filters li.selected a {
#filters li.polymer-selected a {
font-weight: bold;
}
......@@ -242,6 +245,11 @@ label[for='toggle-all'] {
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all {
background: none;
/*
ShadowDOM Polyfill work around for webkit/blink bug
https://code.google.com/p/chromium/issues/detail?id=251510
*/
background-color: transparent;
}
#toggle-all {
......
<link rel="import" href="../lib-elements/polymer-selector.html">
<link rel="import" href="../lib-elements/flatiron-director.html">
<link rel="import" href="../lib-elements/polymer-localstorage.html">
<link rel="import" href="td-input.html">
<link rel="import" href="td-item.html">
<link rel="import" href="td-model.html">
<element name="td-todos" attributes="route">
<link rel="stylesheet" href="td-todos.css">
<polymer-element name="td-todos" attributes="route modelId">
<template>
<link rel="stylesheet" href="td-todos.css">
<flatiron-director route="{{route}}"></flatiron-director>
<polymer-localstorage id="storage" name="todos-polymer" value="{{items}}"></polymer-localstorage>
<td-model id="model" items="{{items}}" filter="{{route}}" on-td-model-changed="modelChangedAction"></td-model>
<section id="todoapp">
<header id="header">
<input id="new-todo" placeholder="What needs to be done?" autofocus on-keyup="keyAction">
<input is="td-input" id="new-todo" placeholder="What needs to be done?" autofocus on-td-input-commit="addTodoAction" on-td-input-cancel="cancelAddTodoAction">
</header>
<section id="main" hidden?="{{$.model.items.length == 0}}">
<input id="toggle-all" type="checkbox" on-change="toggleAllCompletedAction" checked="{{$.model.allCompleted}}">
<section id="main" hidden?="{{model.items.length == 0}}">
<input id="toggle-all" type="checkbox" on-change="toggleAllCompletedAction" checked="{{model.allCompleted}}">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" on-td-item-changed="itemChangedAction" on-td-destroy-item="destroyItemAction">
<template repeat="{{$.model.filtered}}">
<template repeat="{{model.filtered}}">
<li is="td-item" item="{{}}"></li>
</template>
</ul>
</section>
<footer id="footer" hidden?="{{$.model.items.length == 0}}">
<span id="todo-count"><strong>{{$.model.activeCount}}</strong> {{$.model.activeCount == 1 ? 'item' : 'items'}} left</span>
<footer id="footer" hidden?="{{model.items.length == 0}}">
<span id="todo-count"><strong>{{model.activeCount}}</strong> {{model.activeCount == 1 ? 'item' : 'items'}} left</span>
<polymer-selector id="filters" selected="{{route || 'all'}}">
<li name="all">
<a href="../#/">All</a>
......@@ -36,42 +33,49 @@
<a href="../#/completed">Completed</a>
</li>
</polymer-selector>
<button hidden?="{{$.model.completedCount == 0}}" id="clear-completed" on-click="clearCompletedAction">Clear completed ({{$.model.completedCount}})</button>
<button hidden?="{{model.completedCount == 0}}" id="clear-completed" on-click="clearCompletedAction">Clear completed ({{model.completedCount}})</button>
</footer>
</section>
</template>
<script>
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer.register(this, {
keyAction: function (e, detail, sender) {
switch (e.keyCode) {
case ENTER_KEY:
this.$.model.newItem(sender.value);
// when polyfilling Object.observe, make sure we update immediately
Platform.flush();
case ESC_KEY:
sender.value = '';
break;
(function () {
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer('td-todos', {
modelIdChanged: function () {
this.model = document.querySelector('#' + this.modelId);
},
routeChanged: function () {
if (this.model) {
this.model.filter = this.route;
}
},
addTodoAction: function () {
this.model.newItem(this.$['new-todo'].value);
// when polyfilling Object.observe, make sure we update immediately
Platform.flush();
this.$['new-todo'].value = '';
},
cancelAddTodoAction: function () {
this.$['new-todo'].value = '';
},
itemChangedAction: function () {
if (this.model) {
this.model.itemsChanged();
}
},
destroyItemAction: function (e, detail) {
this.model.destroyItem(detail);
},
toggleAllCompletedAction: function (e, detail, sender) {
this.model.setItemsCompleted(sender.checked);
},
clearCompletedAction: function () {
this.model.clearItems();
}
},
modelChangedAction: function () {
this.$.storage.save();
},
itemChangedAction: function () {
this.$.model.itemsChanged();
},
destroyItemAction: function (e, detail) {
this.$.model.destroyItem(detail);
},
toggleAllCompletedAction: function (e, detail, sender) {
this.$.model.setItemsCompleted(sender.checked);
},
clearCompletedAction: function () {
this.$.model.clearItems();
}
});
});
})();
</script>
</element>
</polymer-element>
......@@ -5,22 +5,25 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Polymer • TodoMVC</title>
<link rel="stylesheet" href="app/app.css">
<link rel="import" href="lib-elements/polymer-localstorage.html">
<link rel="import" href="elements/td-model.html">
<link rel="import" href="elements/td-todos.html">
</head>
<body>
<section>
<header>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
</header>
<td-todos></td-todos>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://www.polymer-project.org">The Polymer Authors</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<polymer-localstorage id="storage" name="todos-polymer"></polymer-localstorage>
<td-model id="model" storageId="storage"></td-model>
<td-todos modelId="model"></td-todos>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://www.polymer-project.org">The Polymer Authors</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/polymer/polymer.js"></script>
<link rel="import" href="elements/td-todos.html">
<script src="bower_components/polymer/polymer.min.js"></script>
</body>
</html>
<script src="../bower_components/director/build/director.min.js"></script>
<element name="flatiron-director" attributes="route">
<polymer-element name="flatiron-director" attributes="route">
<script>
var private_router;
Polymer.register(this, {
ready: function () {
this.router.on(/(\w*)/, function (route) {
this.route = route;
}.bind(this));
this.asyncMethod(function () {
var initialRoute = this.router.getRoute(0);
this.route = initialRoute || '';
});
},
get router() {
if (!private_router) {
private_router = new Router();
private_router.init();
(function() {
var private_router;
Polymer('flatiron-director', {
ready: function() {
this.router.on(/(\w*)/, function(route) {
this.route = route;
}.bind(this));
this.asyncMethod(function() {
var initialRoute = this.router.getRoute(0);
this.route = initialRoute || '';
// flush to here to render the initial route synchronously.
Platform.flush();
});
},
get router() {
if (!private_router) {
private_router = new Router();
private_router.init();
}
return private_router;
},
routeChanged: function() {
this.fire('route', this.route);
}
return private_router;
},
routeChanged: function () {
this.fire('route', this.route);
}
});
});
})();
</script>
</element>
</polymer-element>
......@@ -7,38 +7,47 @@ license that can be found in the LICENSE file.
/**
* @module Polymer Elements
*/
/**
* polymer-localstorage provides access to localStorage.
*
* Example:
*
* <polymer-localstorage name="my-app-storage" value="{{value}}"></polymer-localstorage>
*
* @class polymer-localstorage
*/
-->
<element name="polymer-localstorage" attributes="name value isJson">
<polymer-element name="polymer-localstorage" attributes="name value useRaw">
<template>
<style>
@host {
* {
display: none !important;
display: none;
}
}
</style>
</template>
<script>
Polymer.register(this, {
isJson: true,
ready: function () {
Polymer('polymer-localstorage', {
useRaw: false,
ready: function() {
this.load();
},
valueChanged: function () {
valueChanged: function() {
this.save();
},
load: function () {
load: function() {
var s = window.localStorage.getItem(this.name);
if (s && this.isJson) {
if (s && !this.useRaw) {
this.value = JSON.parse(s);
} else {
this.value = s;
}
},
save: function () {
var item = this.isJson ? JSON.stringify(this.value) : this.value;
window.localStorage.setItem(this.name, item);
save: function() {
window.localStorage.setItem(this.name,
this.useRaw ? this.value : JSON.stringify(this.value));
}
});
</script>
</element>
</polymer-element>
......@@ -8,7 +8,7 @@ license that can be found in the LICENSE file.
* @module Polymer Elements
*/
-->
<element name="polymer-selection" attributes="multi">
<polymer-element name="polymer-selection" attributes="multi">
<template>
<style>
@host {
......@@ -19,47 +19,46 @@ license that can be found in the LICENSE file.
</style>
</template>
<script>
Polymer.register(this, {
Polymer('polymer-selection', {
multi: false,
ready: function () {
ready: function() {
this.clear();
},
clear: function () {
clear: function() {
this.selection = [];
},
getSelection: function () {
getSelection: function() {
return this.multi ? this.selection : this.selection[0];
},
isSelected: function (inItem) {
return this.selection.indexOf(inItem) >= 0;
isSelected: function(item) {
return this.selection.indexOf(item) >= 0;
},
setItemSelected: function (inItem, inIsSelected) {
var i;
if (inItem) {
if (inIsSelected) {
this.selection.push(inItem);
setItemSelected: function(item, isSelected) {
if (item) {
if (isSelected) {
this.selection.push(item);
} else {
i = this.selection.indexOf(inItem);
var i = this.selection.indexOf(item);
if (i >= 0) {
this.selection.splice(i, 1);
}
}
// TODO(sjmiles): consider replacing with summary
// notifications (asynchronous job)
this.asend("select", { isSelected: inIsSelected, item: inItem });
this.asyncFire("polymer-select", {isSelected: isSelected, item: item});
}
},
select: function (inItem) {
select: function(item) {
if (this.multi) {
this.toggle(inItem);
} else if (this.getSelection() !== inItem) {
this.toggle(item);
} else if (this.getSelection() !== item) {
this.setItemSelected(this.getSelection(), false);
this.setItemSelected(inItem, true);
this.setItemSelected(item, true);
}
},
toggle: function (inItem) {
this.setItemSelected(inItem, !this.isSelected(inItem));
toggle: function(item) {
this.setItemSelected(item, !this.isSelected(item));
}
});
</script>
</element>
</polymer-element>
......@@ -10,31 +10,31 @@ license that can be found in the LICENSE file.
/**
* polymer-selector is used to display a list of elements that can be selected.
* The attribute "selected" indicates which element is being selected.
* Tapping on the element to change selection would fire "activate"
* Tapping on the element to change selection would fire "polymer-activate"
* event.
*
* Example:
*
* <polymer-selector selected="0" on-activate="activateHandler">
* <div>Item 1</div>
* <div>Item 2</div>
* <div>Item 3</div>
* </polymer-selector>
* <polymer-selector selected="0" on-polymer-activate="activateAction">
* <div>Item 1</div>
* <div>Item 2</div>
* <div>Item 3</div>
* </polymer-selector>
*
* polymer-selector is not styled. So one needs to use "selected" CSS class
* polymer-selector is not styled. So one needs to use "selected" CSS class
* to style the selected element.
*
* <style>
* .item.selected {
* background: #eee;
* }
* </style>
* ...
* <polymer-selector>
* <div class="item">Item 1</div>
* <div class="item">Item 2</div>
* <div class="item">Item 3</div>
* </polymer-selector>
* <style>
* .item.polymer-selected {
* background: #eee;
* }
* </style>
* ...
* <polymer-selector>
* <div class="item">Item 1</div>
* <div class="item">Item 2</div>
* <div class="item">Item 3</div>
* </polymer-selector>
*
* @class polymer-selector
*/
......@@ -42,18 +42,20 @@ license that can be found in the LICENSE file.
* Fired when an element is selected via tap.
*
* @event activate
* @param {Object} inDetail
* @param {Object} inDetail.item the selected element
* @param {Object} detail
* @param {Object} detail.item the selected element
*/
-->
<link rel="import" href="polymer-selection.html">
<element name="polymer-selector" on-tap="activateHandler" attributes="selected selectedClass selectedModel multi valueattr notap">
<polymer-element name="polymer-selector" on-tap="activateHandler" touch-action="none"
attributes="selected multi valueattr selectedClass setectedProperty selectedModel notap">
<template>
<polymer-selection id="selection" multi="{{multi}}" on-select="selectionSelect"></polymer-selection>
<polymer-selection id="selection" multi="{{multi}}" on-polymer-select="selectionSelect"></polymer-selection>
<content id="items" select="*"></content>
</template>
<script>
Polymer.register(this, {
Polymer('polymer-selector', {
/**
* Gets or sets the selected element. Default is to use the index
* of the currently selected element.
......@@ -63,11 +65,11 @@ license that can be found in the LICENSE file.
*
* Example:
*
* <polymer-selector valueattr="label" selected="foo">
* <div label="foo"></div>
* <div label="bar"></div>
* <div label="zot"></div>
* </polymer-selector>
* <polymer-selector valueattr="label" selected="foo">
* <div label="foo"></div>
* <div label="bar"></div>
* <div label="zot"></div>
* </polymer-selector>
*
* @attribute selected
* @type string
......@@ -95,9 +97,18 @@ license that can be found in the LICENSE file.
*
* @attribute selectedClass
* @type string
* @default 'selected'
* @default 'polymer-selected'
*/
selectedClass: 'selected',
selectedClass: 'polymer-selected',
/**
* Specifies the property to be used to set on the selected element
* to indicate its active state.
*
* @attribute selectedProperty
* @type string
* @default 'active'
*/
setectedProperty: 'active',
/**
* Returns the model associated with the selected element.
*
......@@ -106,67 +117,82 @@ license that can be found in the LICENSE file.
* @default null
*/
selectedModel: null,
ready: function () {
this.setAttribute('touch-action', 'none');
},
get items() {
return this.$.items.getDistributedNodes();
var nodes = this.$.items.getDistributedNodes();
return Array.prototype.filter.call(nodes, function(n) {
return n && n.localName !== 'template';
});
},
get selection() {
return this.$.selection.getSelection();
},
selectedChanged: function () {
selectedChanged: function() {
this.valueToSelection(this.selected);
},
valueToSelection: function (inValue) {
var item = this.items[this.valueToIndex(inValue)];
var template;
if (item) {
this.$.selection.select(item);
template = item.templateInstance;
this.selectedModel = template ? template.model : undefined;
valueToSelection: function(value) {
var item = this.items[this.valueToIndex(value)];
this.selectedItem = item;
this.$.selection.select(item);
this.updateSelectedModel();
},
updateSelectedModel: function() {
if (this.selectedItem) {
var t = this.selectedItem.templateInstance;
this.selectedModel = t ? t.model : undefined;
} else {
this.selectedModel = null;
}
},
valueToIndex: function (inValue) {
// find an item with value == inValue and return it's index
var i, items, c;
for (i = 0, items = this.items, c; (c = items[i]); i++) {
if (this.valueForNode(c) == inValue) {
valueToIndex: function(value) {
// find an item with value == value and return it's index
for (var i=0, items=this.items, c; (c=items[i]); i++) {
if (this.valueForNode(c) == value) {
return i;
}
}
// if no item found, the value itself is probably the index
return inValue;
return value;
},
valueForNode: function (inNode) {
return inNode[this.valueattr] || inNode.getAttribute(this.valueattr);
valueForNode: function(node) {
return node[this.valueattr] || node.getAttribute(this.valueattr);
},
// events fired from <polymer-selection> object
selectionSelect: function (inEvent, inInfo) {
if (inInfo.item && this.selectedClass) {
inInfo.item.classList.toggle(this.selectedClass, inInfo.isSelected);
selectionSelect: function(e, detail) {
if (detail.item) {
if (this.selectedClass) {
detail.item.classList.toggle(this.selectedClass, detail.isSelected);
}
if (this.setectedProperty) {
detail.item[this.setectedProperty] = detail.isSelected;
}
}
},
// event fired from host
activateHandler: function (inEvent) {
if (this.notap) {
return;
activateHandler: function(e) {
if (!this.notap) {
var i = this.findDistributedTarget(e.target, this.items);
if (i >= 0) {
var selected = this.valueForNode(this.items[i]) || i;
if (this.multi) {
this.valueToSelection(selected);
} else {
this.selected = selected;
}
this.asyncFire('polymer-activate', {item: this.items[i]});
}
}
var items = this.items;
var i = Polymer.findDistributedTarget(inEvent.target, items);
var selected;
if (i >= 0) {
selected = this.valueForNode(items[i]) || i;
if (this.multi) {
this.valueToSelection(selected);
} else {
this.selected = selected;
},
findDistributedTarget: function(target, nodes) {
// find first ancestor of target (including itself) that
// is in inNodes, if any
while (target && target != this) {
var i = Array.prototype.indexOf.call(nodes, target);
if (i >= 0) {
return i;
}
this.asend('activate', { item: items[i] });
target = target.parentNode;
}
}
});
</script>
</element>
</polymer-element>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment