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 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>
(function () {
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer.register(this, {
Polymer('td-item', {
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;
// 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 () {
......@@ -33,23 +28,29 @@
});
},
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 () {
cancelAction: function() {
this.editing = false;
},
itemChangeAction: function () {
itemChangeAction: function() {
this.fire('td-item-changed');
},
destroyAction: function () {
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>
......@@ -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>
(function () {
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);
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();
case ESC_KEY:
sender.value = '';
break;
}
this.$['new-todo'].value = '';
},
modelChangedAction: function () {
this.$.storage.save();
cancelAddTodoAction: function () {
this.$['new-todo'].value = '';
},
itemChangedAction: function () {
this.$.model.itemsChanged();
if (this.model) {
this.model.itemsChanged();
}
},
destroyItemAction: function (e, detail) {
this.$.model.destroyItem(detail);
this.model.destroyItem(detail);
},
toggleAllCompletedAction: function (e, detail, sender) {
this.$.model.setItemsCompleted(sender.checked);
this.model.setItemsCompleted(sender.checked);
},
clearCompletedAction: function () {
this.$.model.clearItems();
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>
<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>
</section>
<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>
(function() {
var private_router;
Polymer.register(this, {
ready: function () {
this.router.on(/(\w*)/, function (route) {
Polymer('flatiron-director', {
ready: function() {
this.router.on(/(\w*)/, function(route) {
this.route = route;
}.bind(this));
this.asyncMethod(function () {
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() {
......@@ -19,9 +22,10 @@
}
return private_router;
},
routeChanged: function () {
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,12 +10,12 @@ 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">
* <polymer-selector selected="0" on-polymer-activate="activateAction">
* <div>Item 1</div>
* <div>Item 2</div>
* <div>Item 3</div>
......@@ -25,7 +25,7 @@ license that can be found in the LICENSE file.
* to style the selected element.
*
* <style>
* .item.selected {
* .item.polymer-selected {
* background: #eee;
* }
* </style>
......@@ -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.
......@@ -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) {
valueToSelection: function(value) {
var item = this.items[this.valueToIndex(value)];
this.selectedItem = item;
this.$.selection.select(item);
template = item.templateInstance;
this.selectedModel = template ? template.model : undefined;
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;
}
var items = this.items;
var i = Polymer.findDistributedTarget(inEvent.target, items);
var selected;
activateHandler: function(e) {
if (!this.notap) {
var i = this.findDistributedTarget(e.target, this.items);
if (i >= 0) {
selected = this.valueForNode(items[i]) || i;
var selected = this.valueForNode(this.items[i]) || i;
if (this.multi) {
this.valueToSelection(selected);
} else {
this.selected = selected;
}
this.asend('activate', { item: items[i] });
this.asyncFire('polymer-activate', {item: this.items[i]});
}
}
},
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;
}
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