Commit 23591c48 authored by Eric Bidelman's avatar Eric Bidelman

Polymer cleanup

parent a500cab9
node_modules
bower_components/iron-localstorage/.bower.json bower_components/iron-localstorage/.bower.json
bower_components/iron-localstorage/.gitignore bower_components/iron-localstorage/.gitignore
bower_components/iron-localstorage/README.md bower_components/iron-localstorage/README.md
...@@ -19,7 +21,7 @@ bower_components/flatiron-director ...@@ -19,7 +21,7 @@ bower_components/flatiron-director
!bower_components/flatiron-director/flatiron-director.html !bower_components/flatiron-director/flatiron-director.html
bower_components/webcomponentsjs bower_components/webcomponentsjs
!bower_components/webcomponentsjs/webcomponents.js !bower_components/webcomponentsjs/webcomponents-lite.min.js
bower_components/polymer/.bower.json bower_components/polymer/.bower.json
bower_components/polymer/LICENSE.txt bower_components/polymer/LICENSE.txt
......
{ {
"name": "todomvc-polymer", "name": "todomvc-polymer",
"version": "0.0.1", "version": "0.0.2",
"dependencies": { "dependencies": {
"todomvc-common": "^1.0.1", "todomvc-common": "^1.0.1",
"todomvc-app-css": "^1.0.0", "todomvc-app-css": "^1.0.0",
"polymer": "Polymer/polymer#1.1.0", "polymer": "Polymer/polymer#^1.1.4",
"iron-selector": "polymerelements/iron-selector", "iron-selector": "PolymerElements/iron-selector#^1.0.5",
"flatiron-director": "PolymerLabs/flatiron-director#1.0.0", "flatiron-director": "PolymerLabs/flatiron-director#^1.0.0",
"iron-localstorage": "polymerelements/iron-localstorage" "iron-localstorage": "PolymerElements/iron-localstorage#^1.0.4"
} }
} }
...@@ -103,9 +103,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -103,9 +103,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var i = this.selectedValues.indexOf(value); var i = this.selectedValues.indexOf(value);
var unselected = i < 0; var unselected = i < 0;
if (unselected) { if (unselected) {
this.selectedValues.push(value); this.push('selectedValues',value);
} else { } else {
this.selectedValues.splice(i, 1); this.splice('selectedValues',i,1);
} }
this._selection.setItemSelected(this._valueToItem(value), unselected); this._selection.setItemSelected(this._valueToItem(value), unselected);
} }
......
...@@ -15,14 +15,39 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -15,14 +15,39 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** @polymerBehavior */ /** @polymerBehavior */
Polymer.IronSelectableBehavior = { Polymer.IronSelectableBehavior = {
/**
* Fired when iron-selector is activated (selected or deselected).
* It is fired before the selected items are changed.
* Cancel the event to abort selection.
*
* @event iron-activate
*/
/**
* Fired when an item is selected
*
* @event iron-select
*/
/**
* Fired when an item is deselected
*
* @event iron-deselect
*/
/**
* Fired when the list of selectable items changes (e.g., items are
* added or removed). The detail of the event is a list of mutation
* records that describe what changed.
*
* @event iron-items-changed
*/
properties: { properties: {
/** /**
* If you want to use the attribute value of an element for `selected` instead of the index, * If you want to use the attribute value of an element for `selected` instead of the index,
* set this to the name of the attribute. * set this to the name of the attribute.
*
* @attribute attrForSelected
* @type {string}
*/ */
attrForSelected: { attrForSelected: {
type: String, type: String,
...@@ -31,9 +56,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -31,9 +56,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* Gets or sets the selected element. The default is to use the index of the item. * Gets or sets the selected element. The default is to use the index of the item.
*
* @attribute selected
* @type {string}
*/ */
selected: { selected: {
type: String, type: String,
...@@ -42,9 +64,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -42,9 +64,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* Returns the currently selected item. * Returns the currently selected item.
*
* @attribute selectedItem
* @type {Object}
*/ */
selectedItem: { selectedItem: {
type: Object, type: Object,
...@@ -56,10 +75,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -56,10 +75,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
* The event that fires from items when they are selected. Selectable * The event that fires from items when they are selected. Selectable
* will listen for this event from items and update the selection state. * will listen for this event from items and update the selection state.
* Set to empty string to listen to no events. * Set to empty string to listen to no events.
*
* @attribute activateEvent
* @type {string}
* @default 'tap'
*/ */
activateEvent: { activateEvent: {
type: String, type: String,
...@@ -68,19 +83,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -68,19 +83,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* This is a CSS selector sting. If this is set, only items that matches the CSS selector * This is a CSS selector string. If this is set, only items that match the CSS selector
* are selectable. * are selectable.
*
* @attribute selectable
* @type {string}
*/ */
selectable: String, selectable: String,
/** /**
* The class to set on elements when selected. * The class to set on elements when selected.
*
* @attribute selectedClass
* @type {string}
*/ */
selectedClass: { selectedClass: {
type: String, type: String,
...@@ -89,25 +98,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -89,25 +98,33 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* The attribute to set on elements when selected. * The attribute to set on elements when selected.
*
* @attribute selectedAttribute
* @type {string}
*/ */
selectedAttribute: { selectedAttribute: {
type: String, type: String,
value: null value: null
} },
/**
* The set of excluded elements where the key is the `localName`
* of the element that will be ignored from the item list.
*
* @type {object}
* @default {template: 1}
*/
excludedLocalNames: {
type: Object,
value: function() {
return {
'template': 1
};
}
}
}, },
observers: [ observers: [
'_updateSelected(attrForSelected, selected)' '_updateSelected(attrForSelected, selected)'
], ],
excludedLocalNames: {
'template': 1
},
created: function() { created: function() {
this._bindFilterItem = this._filterItem.bind(this); this._bindFilterItem = this._filterItem.bind(this);
this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); this._selection = new Polymer.IronSelection(this._applySelection.bind(this));
...@@ -116,6 +133,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -116,6 +133,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
attached: function() { attached: function() {
this._observer = this._observeItems(this); this._observer = this._observeItems(this);
this._contentObserver = this._observeContent(this); this._contentObserver = this._observeContent(this);
if (!this.selectedItem && this.selected) {
this._updateSelected(this.attrForSelected,this.selected)
}
}, },
detached: function() { detached: function() {
...@@ -186,9 +206,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -186,9 +206,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
_removeListener: function(eventName) { _removeListener: function(eventName) {
// There is no unlisten yet... this.unlisten(this, eventName, '_activateHandler');
// https://github.com/Polymer/polymer/issues/1639
//this.removeEventListener(eventName, this._bindActivateHandler);
}, },
_activateEventChanged: function(eventName, old) { _activateEventChanged: function(eventName, old) {
...@@ -264,7 +282,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -264,7 +282,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// observe items change under the given node. // observe items change under the given node.
_observeItems: function(node) { _observeItems: function(node) {
var observer = new MutationObserver(function() { // TODO(cdata): Update this when we get distributed children changed.
var observer = new MutationObserver(function(mutations) {
// Let other interested parties know about the change so that
// we don't have to recreate mutation observers everywher.
this.fire('iron-items-changed', mutations, {
bubbles: false,
cancelable: false
});
if (this.selected != null) { if (this.selected != null) {
this._updateSelected(); this._updateSelected();
} }
...@@ -277,11 +303,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -277,11 +303,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
_activateHandler: function(e) { _activateHandler: function(e) {
// TODO: remove this when https://github.com/Polymer/polymer/issues/1639 is fixed so we
// can just remove the old event listener.
if (e.type !== this.activateEvent) {
return;
}
var t = e.target; var t = e.target;
var items = this.items; var items = this.items;
while (t && t != this) { while (t && t != this) {
......
...@@ -32,7 +32,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ...@@ -32,7 +32,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
* the selected item or undefined if there is no selection. * the selected item or undefined if there is no selection.
*/ */
get: function() { get: function() {
return this.multi ? this.selection : this.selection[0]; return this.multi ? this.selection.slice() : this.selection[0];
}, },
/** /**
......
...@@ -20,7 +20,7 @@ addEventListener('DOMContentLoaded', resolve); ...@@ -20,7 +20,7 @@ addEventListener('DOMContentLoaded', resolve);
} }
} }
}()); }());
Polymer = { window.Polymer = {
Settings: function () { Settings: function () {
var user = window.Polymer || {}; var user = window.Polymer || {};
location.search.slice(1).split('&').forEach(function (o) { location.search.slice(1).split('&').forEach(function (o) {
...@@ -48,15 +48,21 @@ useNativeCustomElements: useNativeCustomElements ...@@ -48,15 +48,21 @@ useNativeCustomElements: useNativeCustomElements
(function () { (function () {
var userPolymer = window.Polymer; var userPolymer = window.Polymer;
window.Polymer = function (prototype) { window.Polymer = function (prototype) {
var ctor = desugar(prototype); if (typeof prototype === 'function') {
prototype = ctor.prototype; prototype = prototype.prototype;
}
if (!prototype) {
prototype = {};
}
var factory = desugar(prototype);
prototype = factory.prototype;
var options = { prototype: prototype }; var options = { prototype: prototype };
if (prototype.extends) { if (prototype.extends) {
options.extends = prototype.extends; options.extends = prototype.extends;
} }
Polymer.telemetry._registrate(prototype); Polymer.telemetry._registrate(prototype);
document.registerElement(prototype.is, options); document.registerElement(prototype.is, options);
return ctor; return factory;
}; };
var desugar = function (prototype) { var desugar = function (prototype) {
var base = Polymer.Base; var base = Polymer.Base;
...@@ -133,6 +139,8 @@ _addFeature: function (feature) { ...@@ -133,6 +139,8 @@ _addFeature: function (feature) {
this.extend(this, feature); this.extend(this, feature);
}, },
registerCallback: function () { registerCallback: function () {
this._desugarBehaviors();
this._doBehavior('beforeRegister');
this._registerFeatures(); this._registerFeatures();
this._doBehavior('registered'); this._doBehavior('registered');
}, },
...@@ -217,6 +225,9 @@ Polymer.telemetry.instanceCount = 0; ...@@ -217,6 +225,9 @@ Polymer.telemetry.instanceCount = 0;
(function () { (function () {
var modules = {}; var modules = {};
var lcModules = {}; var lcModules = {};
var findModule = function (id) {
return modules[id] || lcModules[id.toLowerCase()];
};
var DomModule = function () { var DomModule = function () {
return document.createElement('dom-module'); return document.createElement('dom-module');
}; };
...@@ -235,16 +246,18 @@ lcModules[id.toLowerCase()] = this; ...@@ -235,16 +246,18 @@ lcModules[id.toLowerCase()] = this;
} }
}, },
import: function (id, selector) { import: function (id, selector) {
var m = modules[id] || lcModules[id.toLowerCase()]; if (id) {
var m = findModule(id);
if (!m) { if (!m) {
forceDocumentUpgrade(); forceDocumentUpgrade();
m = modules[id]; m = findModule(id);
} }
if (m && selector) { if (m && selector) {
m = m.querySelector(selector); m = m.querySelector(selector);
} }
return m; return m;
} }
}
}); });
var cePolyfill = window.CustomElements && !CustomElements.useNative; var cePolyfill = window.CustomElements && !CustomElements.useNative;
document.registerElement('dom-module', DomModule); document.registerElement('dom-module', DomModule);
...@@ -252,8 +265,7 @@ function forceDocumentUpgrade() { ...@@ -252,8 +265,7 @@ function forceDocumentUpgrade() {
if (cePolyfill) { if (cePolyfill) {
var script = document._currentScript || document.currentScript; var script = document._currentScript || document.currentScript;
var doc = script && script.ownerDocument; var doc = script && script.ownerDocument;
if (doc && !doc.__customElementsForceUpgraded) { if (doc) {
doc.__customElementsForceUpgraded = true;
CustomElements.upgradeAll(doc); CustomElements.upgradeAll(doc);
} }
} }
...@@ -275,11 +287,17 @@ this.is = this.is.toLowerCase(); ...@@ -275,11 +287,17 @@ this.is = this.is.toLowerCase();
}); });
Polymer.Base._addFeature({ Polymer.Base._addFeature({
behaviors: [], behaviors: [],
_prepBehaviors: function () { _desugarBehaviors: function () {
if (this.behaviors.length) { if (this.behaviors.length) {
this.behaviors = this._flattenBehaviorsList(this.behaviors); this.behaviors = this._desugarSomeBehaviors(this.behaviors);
} }
this._prepAllBehaviors(this.behaviors); },
_desugarSomeBehaviors: function (behaviors) {
behaviors = this._flattenBehaviorsList(behaviors);
for (var i = behaviors.length - 1; i >= 0; i--) {
this._mixinBehavior(behaviors[i]);
}
return behaviors;
}, },
_flattenBehaviorsList: function (behaviors) { _flattenBehaviorsList: function (behaviors) {
var flat = []; var flat = [];
...@@ -294,15 +312,6 @@ this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for miss ...@@ -294,15 +312,6 @@ this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for miss
}, this); }, this);
return flat; return flat;
}, },
_prepAllBehaviors: function (behaviors) {
for (var i = behaviors.length - 1; i >= 0; i--) {
this._mixinBehavior(behaviors[i]);
}
for (var i = 0, l = behaviors.length; i < l; i++) {
this._prepBehavior(behaviors[i]);
}
this._prepBehavior(this);
},
_mixinBehavior: function (b) { _mixinBehavior: function (b) {
Object.getOwnPropertyNames(b).forEach(function (n) { Object.getOwnPropertyNames(b).forEach(function (n) {
switch (n) { switch (n) {
...@@ -326,6 +335,15 @@ break; ...@@ -326,6 +335,15 @@ break;
} }
}, this); }, this);
}, },
_prepBehaviors: function () {
this._prepFlattenedBehaviors(this.behaviors);
},
_prepFlattenedBehaviors: function (behaviors) {
for (var i = 0, l = behaviors.length; i < l; i++) {
this._prepBehavior(behaviors[i]);
}
this._prepBehavior(this);
},
_doBehavior: function (name, args) { _doBehavior: function (name, args) {
this.behaviors.forEach(function (b) { this.behaviors.forEach(function (b) {
this._invokeBehavior(b, name, args); this._invokeBehavior(b, name, args);
...@@ -558,7 +576,7 @@ debouncer.stop(); ...@@ -558,7 +576,7 @@ debouncer.stop();
} }
} }
}); });
Polymer.version = '1.1.0'; Polymer.version = '1.1.4';
Polymer.Base._addFeature({ Polymer.Base._addFeature({
_registerFeatures: function () { _registerFeatures: function () {
this._prepIs(); this._prepIs();
......
...@@ -457,47 +457,43 @@ Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent)); ...@@ -457,47 +457,43 @@ Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent));
} }
}, },
appendChild: function (node) { appendChild: function (node) {
var handled; return this._addNode(node);
this._ensureContentLogicalInfo(node);
this._removeNodeFromHost(node, true);
if (this._nodeIsInLogicalTree(this.node)) {
this._addLogicalInfo(node, this.node);
this._addNodeToHost(node);
handled = this._maybeDistribute(node, this.node);
} else {
this._addNodeToHost(node);
}
if (!handled && !this._tryRemoveUndistributedNode(node)) {
var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node);
nativeAppendChild.call(container, node);
}
return node;
}, },
insertBefore: function (node, ref_node) { insertBefore: function (node, ref_node) {
if (!ref_node) { return this._addNode(node, ref_node);
return this.appendChild(node); },
} _addNode: function (node, ref_node) {
var handled;
this._ensureContentLogicalInfo(node);
this._removeNodeFromHost(node, true); this._removeNodeFromHost(node, true);
if (this._nodeIsInLogicalTree(this.node)) { var addedInsertionPoint;
var root = this.getOwnerRoot();
if (root) {
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
}
if (this._nodeHasLogicalChildren(this.node)) {
if (ref_node) {
var children = this.childNodes; var children = this.childNodes;
var index = children.indexOf(ref_node); var index = children.indexOf(ref_node);
if (index < 0) { if (index < 0) {
throw Error('The ref_node to be inserted before is not a child ' + 'of this node'); throw Error('The ref_node to be inserted before is not a child ' + 'of this node');
} }
}
this._addLogicalInfo(node, this.node, index); this._addLogicalInfo(node, this.node, index);
this._addNodeToHost(node);
handled = this._maybeDistribute(node, this.node);
} else {
this._addNodeToHost(node);
} }
if (!handled && !this._tryRemoveUndistributedNode(node)) { this._addNodeToHost(node);
if (!this._maybeDistribute(node, this.node) && !this._tryRemoveUndistributedNode(node)) {
if (ref_node) {
ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node; ref_node = ref_node.localName === CONTENT ? this._firstComposedNode(ref_node) : ref_node;
}
var container = this.node._isShadyRoot ? this.node.host : this.node; var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node, ref_node); addToComposedParent(container, node, ref_node);
if (ref_node) {
nativeInsertBefore.call(container, node, ref_node); nativeInsertBefore.call(container, node, ref_node);
} else {
nativeAppendChild.call(container, node);
}
}
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
} }
return node; return node;
}, },
...@@ -505,14 +501,8 @@ removeChild: function (node) { ...@@ -505,14 +501,8 @@ removeChild: function (node) {
if (factory(node).parentNode !== this.node) { if (factory(node).parentNode !== this.node) {
console.warn('The node to be removed is not a child of this node', node); console.warn('The node to be removed is not a child of this node', node);
} }
var handled;
if (this._nodeIsInLogicalTree(this.node)) {
this._removeNodeFromHost(node); this._removeNodeFromHost(node);
handled = this._maybeDistribute(node, this.node); if (!this._maybeDistribute(node, this.node)) {
} else {
this._removeNodeFromHost(node);
}
if (!handled) {
var container = this.node._isShadyRoot ? this.node.host : this.node; var container = this.node._isShadyRoot ? this.node.host : this.node;
if (container === node.parentNode) { if (container === node.parentNode) {
removeFromComposedParent(container, node); removeFromComposedParent(container, node);
...@@ -560,7 +550,6 @@ if (hasContent) { ...@@ -560,7 +550,6 @@ if (hasContent) {
var root = this._ownerShadyRootForNode(parent); var root = this._ownerShadyRootForNode(parent);
if (root) { if (root) {
var host = root.host; var host = root.host;
this._updateInsertionPoints(host);
this._lazyDistribute(host); this._lazyDistribute(host);
} }
} }
...@@ -570,10 +559,30 @@ this._lazyDistribute(parent); ...@@ -570,10 +559,30 @@ this._lazyDistribute(parent);
} }
return parentNeedsDist || hasContent && !wrappedContent; return parentNeedsDist || hasContent && !wrappedContent;
}, },
_maybeAddInsertionPoint: function (node, parent) {
var added;
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !node.__noContent) {
var c$ = factory(node).querySelectorAll(CONTENT);
for (var i = 0, n, np, na; i < c$.length && (n = c$[i]); i++) {
np = factory(n).parentNode;
if (np === node) {
np = parent;
}
na = this._maybeAddInsertionPoint(n, np);
added = added || na;
}
} else if (node.localName === CONTENT) {
saveLightChildrenIfNeeded(parent);
saveLightChildrenIfNeeded(node);
added = true;
}
return added;
},
_tryRemoveUndistributedNode: function (node) { _tryRemoveUndistributedNode: function (node) {
if (this.node.shadyRoot) { if (this.node.shadyRoot) {
if (node._composedParent) { var parent = getComposedParent(node);
nativeRemoveChild.call(node._composedParent, node); if (parent) {
nativeRemoveChild.call(parent, node);
} }
return true; return true;
} }
...@@ -586,20 +595,8 @@ saveLightChildrenIfNeeded(c); ...@@ -586,20 +595,8 @@ saveLightChildrenIfNeeded(c);
saveLightChildrenIfNeeded(factory(c).parentNode); saveLightChildrenIfNeeded(factory(c).parentNode);
} }
}, },
_nodeIsInLogicalTree: function (node) { _nodeHasLogicalChildren: function (node) {
return Boolean(node._lightParent !== undefined || node._isShadyRoot || node.shadyRoot); return Boolean(node._lightChildren !== undefined);
},
_ensureContentLogicalInfo: function (node) {
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
saveLightChildrenIfNeeded(this.node);
var c$ = Array.prototype.slice.call(node.childNodes);
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) {
this._ensureContentLogicalInfo(n);
}
} else if (node.localName === CONTENT) {
saveLightChildrenIfNeeded(this.node);
saveLightChildrenIfNeeded(node);
}
}, },
_parentNeedsDistribution: function (parent) { _parentNeedsDistribution: function (parent) {
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot); return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
...@@ -609,6 +606,7 @@ var hostNeedsDist; ...@@ -609,6 +606,7 @@ var hostNeedsDist;
var root; var root;
var parent = node._lightParent; var parent = node._lightParent;
if (parent) { if (parent) {
factory(node)._distributeParent();
root = this._ownerShadyRootForNode(node); root = this._ownerShadyRootForNode(node);
if (root) { if (root) {
root.host._elementRemove(node); root.host._elementRemove(node);
...@@ -621,7 +619,7 @@ if (root && hostNeedsDist) { ...@@ -621,7 +619,7 @@ if (root && hostNeedsDist) {
this._updateInsertionPoints(root.host); this._updateInsertionPoints(root.host);
this._lazyDistribute(root.host); this._lazyDistribute(root.host);
} else if (ensureComposedRemoval) { } else if (ensureComposedRemoval) {
removeFromComposedParent(parent || node.parentNode, node); removeFromComposedParent(getComposedParent(node), node);
} }
}, },
_removeDistributedChildren: function (root, container) { _removeDistributedChildren: function (root, container) {
...@@ -845,7 +843,7 @@ configurable: true ...@@ -845,7 +843,7 @@ configurable: true
}, },
parentNode: { parentNode: {
get: function () { get: function () {
return this.node._lightParent || (this.node.__patched ? this.node._composedParent : this.node.parentNode); return this.node._lightParent || getComposedParent(this.node);
}, },
configurable: true configurable: true
}, },
...@@ -967,6 +965,18 @@ DomApi.prototype._getComposedInnerHTML = function () { ...@@ -967,6 +965,18 @@ DomApi.prototype._getComposedInnerHTML = function () {
return getInnerHTML(this.node, true); return getInnerHTML(this.node, true);
}; };
} else { } else {
var forwardMethods = [
'cloneNode',
'appendChild',
'insertBefore',
'removeChild',
'replaceChild'
];
forwardMethods.forEach(function (name) {
DomApi.prototype[name] = function () {
return this.node[name].apply(this.node, arguments);
};
});
DomApi.prototype.querySelectorAll = function (selector) { DomApi.prototype.querySelectorAll = function (selector) {
return Array.prototype.slice.call(this.node.querySelectorAll(selector)); return Array.prototype.slice.call(this.node.querySelectorAll(selector));
}; };
...@@ -979,9 +989,6 @@ return n; ...@@ -979,9 +989,6 @@ return n;
n = n.parentNode; n = n.parentNode;
} }
}; };
DomApi.prototype.cloneNode = function (deep) {
return this.node.cloneNode(deep);
};
DomApi.prototype.importNode = function (externalNode, deep) { DomApi.prototype.importNode = function (externalNode, deep) {
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument; var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
return doc.importNode(externalNode, deep); return doc.importNode(externalNode, deep);
...@@ -1028,7 +1035,7 @@ return this.node.innerHTML = value; ...@@ -1028,7 +1035,7 @@ return this.node.innerHTML = value;
configurable: true configurable: true
} }
}); });
var forwards = [ var forwardProperties = [
'parentNode', 'parentNode',
'firstChild', 'firstChild',
'lastChild', 'lastChild',
...@@ -1039,7 +1046,7 @@ var forwards = [ ...@@ -1039,7 +1046,7 @@ var forwards = [
'nextElementSibling', 'nextElementSibling',
'previousElementSibling' 'previousElementSibling'
]; ];
forwards.forEach(function (name) { forwardProperties.forEach(function (name) {
Object.defineProperty(DomApi.prototype, name, { Object.defineProperty(DomApi.prototype, name, {
get: function () { get: function () {
return this.node[name]; return this.node[name];
...@@ -1123,6 +1130,9 @@ node._composedChildren = null; ...@@ -1123,6 +1130,9 @@ node._composedChildren = null;
addNodeToComposedChildren(node, parent, children, i); addNodeToComposedChildren(node, parent, children, i);
} }
} }
function getComposedParent(node) {
return node.__patched ? node._composedParent : node.parentNode;
}
function addNodeToComposedChildren(node, parent, children, i) { function addNodeToComposedChildren(node, parent, children, i) {
node._composedParent = parent; node._composedParent = parent;
children.splice(i >= 0 ? i : children.length, 0, node); children.splice(i >= 0 ? i : children.length, 0, node);
...@@ -1147,12 +1157,13 @@ node._lightChildren = c$; ...@@ -1147,12 +1157,13 @@ node._lightChildren = c$;
} }
} }
function hasInsertionPoint(root) { function hasInsertionPoint(root) {
return Boolean(root._insertionPoints.length); return Boolean(root && root._insertionPoints.length);
} }
var p = Element.prototype; var p = Element.prototype;
var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector; var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
return { return {
getLightChildren: getLightChildren, getLightChildren: getLightChildren,
getComposedParent: getComposedParent,
getComposedChildren: getComposedChildren, getComposedChildren: getComposedChildren,
removeFromComposedParent: removeFromComposedParent, removeFromComposedParent: removeFromComposedParent,
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded, saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
...@@ -1341,7 +1352,9 @@ var composed = getComposedChildren(container); ...@@ -1341,7 +1352,9 @@ var composed = getComposedChildren(container);
var splices = Polymer.ArraySplice.calculateSplices(children, composed); var splices = Polymer.ArraySplice.calculateSplices(children, composed);
for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) { for (var i = 0, d = 0, s; i < splices.length && (s = splices[i]); i++) {
for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) { for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
if (getComposedParent(n) === container) {
remove(n); remove(n);
}
composed.splice(s.index + d, 1); composed.splice(s.index + d, 1);
} }
d -= s.addedCount; d -= s.addedCount;
...@@ -1354,6 +1367,7 @@ insertBefore(container, n, next); ...@@ -1354,6 +1367,7 @@ insertBefore(container, n, next);
composed.splice(j, 0, n); composed.splice(j, 0, n);
} }
} }
ensureComposedParent(container, children);
}, },
_matchesContentSelect: function (node, contentElement) { _matchesContentSelect: function (node, contentElement) {
var select = contentElement.getAttribute('select'); var select = contentElement.getAttribute('select');
...@@ -1383,6 +1397,7 @@ var getLightChildren = Polymer.DomApi.getLightChildren; ...@@ -1383,6 +1397,7 @@ var getLightChildren = Polymer.DomApi.getLightChildren;
var matchesSelector = Polymer.DomApi.matchesSelector; var matchesSelector = Polymer.DomApi.matchesSelector;
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint; var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
var getComposedChildren = Polymer.DomApi.getComposedChildren; var getComposedChildren = Polymer.DomApi.getComposedChildren;
var getComposedParent = Polymer.DomApi.getComposedParent;
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent; var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
function distributeNodeInto(child, insertionPoint) { function distributeNodeInto(child, insertionPoint) {
insertionPoint._distributedNodes.push(child); insertionPoint._distributedNodes.push(child);
...@@ -1436,8 +1451,10 @@ node._composedParent = null; ...@@ -1436,8 +1451,10 @@ node._composedParent = null;
nativeRemoveChild.call(parentNode, node); nativeRemoveChild.call(parentNode, node);
} }
} }
function getComposedParent(node) { function ensureComposedParent(parent, children) {
return node.__patched ? node._composedParent : node.parentNode; for (var i = 0, n; i < children.length; i++) {
children[i]._composedParent = parent;
}
} }
function getTopDistributingHost(host) { function getTopDistributingHost(host) {
while (host && hostNeedsRedistribution(host)) { while (host && hostNeedsRedistribution(host)) {
......
...@@ -236,7 +236,11 @@ if (!this._template) { ...@@ -236,7 +236,11 @@ if (!this._template) {
this._notes = []; this._notes = [];
} else { } else {
Polymer.Annotations.prepElement = this._prepElement.bind(this); Polymer.Annotations.prepElement = this._prepElement.bind(this);
if (this._template._content && this._template._content._notes) {
this._notes = this._template._content._notes;
} else {
this._notes = Polymer.Annotations.parseAnnotations(this._template); this._notes = Polymer.Annotations.parseAnnotations(this._template);
}
this._processAnnotations(this._notes); this._processAnnotations(this._notes);
Polymer.Annotations.prepElement = null; Polymer.Annotations.prepElement = null;
} }
...@@ -683,12 +687,12 @@ gd = gobj[dep]; ...@@ -683,12 +687,12 @@ gd = gobj[dep];
if (gd && gd[name]) { if (gd && gd[name]) {
gd[name] = (gd[name] || 1) - 1; gd[name] = (gd[name] || 1) - 1;
gd._count = (gd._count || 1) - 1; gd._count = (gd._count || 1) - 1;
}
if (gd._count === 0) { if (gd._count === 0) {
node.removeEventListener(dep, this.handleNative); node.removeEventListener(dep, this.handleNative);
} }
} }
} }
}
node.removeEventListener(evType, handler); node.removeEventListener(evType, handler);
}, },
register: function (recog) { register: function (recog) {
...@@ -1656,6 +1660,9 @@ name: arg, ...@@ -1656,6 +1660,9 @@ name: arg,
model: this._modelForPath(arg) model: this._modelForPath(arg)
}; };
var fc = arg[0]; var fc = arg[0];
if (fc === '-') {
fc = arg[1];
}
if (fc >= '0' && fc <= '9') { if (fc >= '0' && fc <= '9') {
fc = '#'; fc = '#';
} }
...@@ -1810,6 +1817,7 @@ var h$ = this._handlers; ...@@ -1810,6 +1817,7 @@ var h$ = this._handlers;
for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) { for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) {
h[0].call(this, h[1], h[2]); h[0].call(this, h[1], h[2]);
} }
this._handlers = [];
} }
}); });
(function () { (function () {
...@@ -1933,7 +1941,7 @@ this._boundPaths = this._boundPaths || {}; ...@@ -1933,7 +1941,7 @@ this._boundPaths = this._boundPaths || {};
if (from) { if (from) {
this._boundPaths[to] = from; this._boundPaths[to] = from;
} else { } else {
this.unbindPath(to); this.unlinkPaths(to);
} }
}, },
unlinkPaths: function (path) { unlinkPaths: function (path) {
...@@ -1942,23 +1950,13 @@ delete this._boundPaths[path]; ...@@ -1942,23 +1950,13 @@ delete this._boundPaths[path];
} }
}, },
_notifyBoundPaths: function (path, value) { _notifyBoundPaths: function (path, value) {
var from, to;
for (var a in this._boundPaths) { for (var a in this._boundPaths) {
var b = this._boundPaths[a]; var b = this._boundPaths[a];
if (path.indexOf(a + '.') == 0) { if (path.indexOf(a + '.') == 0) {
from = a; this.notifyPath(this._fixPath(b, a, path), value);
to = b; } else if (path.indexOf(b + '.') == 0) {
break; this.notifyPath(this._fixPath(a, b, path), value);
} }
if (path.indexOf(b + '.') == 0) {
from = b;
to = a;
break;
}
}
if (from && to) {
var p = this._fixPath(to, from, path);
this.notifyPath(p, value);
} }
}, },
_fixPath: function (property, root, path) { _fixPath: function (property, root, path) {
...@@ -2186,7 +2184,7 @@ MIXIN_RULE: 1000 ...@@ -2186,7 +2184,7 @@ MIXIN_RULE: 1000
OPEN_BRACE: '{', OPEN_BRACE: '{',
CLOSE_BRACE: '}', CLOSE_BRACE: '}',
_rx: { _rx: {
comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,
port: /@import[^;]*;/gim, port: /@import[^;]*;/gim,
customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim, customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim,
mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim, mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim,
...@@ -2230,6 +2228,9 @@ clearStyleRules: function (style) { ...@@ -2230,6 +2228,9 @@ clearStyleRules: function (style) {
style.__cssRules = null; style.__cssRules = null;
}, },
forEachStyleRule: function (node, callback) { forEachStyleRule: function (node, callback) {
if (!node) {
return;
}
var s = node.parsedSelector; var s = node.parsedSelector;
var skipRules = false; var skipRules = false;
if (node.type === this.ruleTypes.STYLE_RULE) { if (node.type === this.ruleTypes.STYLE_RULE) {
...@@ -2258,47 +2259,44 @@ afterNode = n$[n$.length - 1]; ...@@ -2258,47 +2259,44 @@ afterNode = n$[n$.length - 1];
target.insertBefore(style, afterNode && afterNode.nextSibling || target.firstChild); target.insertBefore(style, afterNode && afterNode.nextSibling || target.firstChild);
return style; return style;
}, },
cssFromModules: function (moduleIds) { cssFromModules: function (moduleIds, warnIfNotFound) {
var modules = moduleIds.trim().split(' '); var modules = moduleIds.trim().split(' ');
var cssText = ''; var cssText = '';
for (var i = 0; i < modules.length; i++) { for (var i = 0; i < modules.length; i++) {
cssText += this.cssFromModule(modules[i]); cssText += this.cssFromModule(modules[i], warnIfNotFound);
} }
return cssText; return cssText;
}, },
cssFromModule: function (moduleId) { cssFromModule: function (moduleId, warnIfNotFound) {
var m = Polymer.DomModule.import(moduleId); var m = Polymer.DomModule.import(moduleId);
if (m && !m._cssText) { if (m && !m._cssText) {
m._cssText = this._cssFromElement(m); m._cssText = this._cssFromElement(m);
} }
if (!m && warnIfNotFound) {
console.warn('Could not find style data in module named', moduleId);
}
return m && m._cssText || ''; return m && m._cssText || '';
}, },
_cssFromElement: function (element) { _cssFromElement: function (element) {
var cssText = ''; var cssText = '';
var content = element.content || element; var content = element.content || element;
var sourceDoc = element.ownerDocument;
var e$ = Array.prototype.slice.call(content.querySelectorAll(this.MODULE_STYLES_SELECTOR)); var e$ = Array.prototype.slice.call(content.querySelectorAll(this.MODULE_STYLES_SELECTOR));
for (var i = 0, e, resolveDoc, addModule; i < e$.length; i++) { for (var i = 0, e; i < e$.length; i++) {
e = e$[i]; e = e$[i];
resolveDoc = sourceDoc;
addModule = null;
if (e.localName === 'template') { if (e.localName === 'template') {
cssText += this._cssFromElement(e); cssText += this._cssFromElement(e);
} else { } else {
if (e.localName === 'style') { if (e.localName === 'style') {
addModule = e.getAttribute(this.INCLUDE_ATTR); var include = e.getAttribute(this.INCLUDE_ATTR);
if (include) {
cssText += this.cssFromModules(include, true);
}
e = e.__appliedElement || e; e = e.__appliedElement || e;
e.parentNode.removeChild(e); e.parentNode.removeChild(e);
} else { cssText += this.resolveCss(e.textContent, element.ownerDocument);
e = e.import && e.import.body; } else if (e.import && e.import.body) {
resolveDoc = e.ownerDocument; cssText += this.resolveCss(e.import.body.textContent, e.import);
} }
if (e) {
cssText += this.resolveCss(e.textContent, resolveDoc);
}
}
if (addModule) {
cssText += this.cssFromModules(addModule);
} }
} }
return cssText; return cssText;
...@@ -3232,18 +3230,25 @@ observer.observe(e, { childList: true }); ...@@ -3232,18 +3230,25 @@ observer.observe(e, { childList: true });
_apply: function () { _apply: function () {
var e = this.__appliedElement || this; var e = this.__appliedElement || this;
if (this.include) { if (this.include) {
e.textContent += styleUtil.cssFromModules(this.include); e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent;
} }
if (e.textContent) {
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function (rule) {
styleTransformer.documentRule(rule);
});
this._applyCustomProperties(e);
}
},
_applyCustomProperties: function (element) {
this._computeStyleProperties(); this._computeStyleProperties();
var props = this._styleProperties; var props = this._styleProperties;
var self = this; var rules = styleUtil.rulesForStyle(element);
e.textContent = styleUtil.toCssText(styleUtil.rulesForStyle(e), function (rule) { element.textContent = styleUtil.toCssText(rules, function (rule) {
var css = rule.cssText = rule.parsedCssText; var css = rule.cssText = rule.parsedCssText;
if (rule.propertyInfo && rule.propertyInfo.cssText) { if (rule.propertyInfo && rule.propertyInfo.cssText) {
css = cssParse.removeCustomPropAssignment(css); css = cssParse.removeCustomPropAssignment(css);
rule.cssText = propertyUtils.valueForProperties(css, props); rule.cssText = propertyUtils.valueForProperties(css, props);
} }
styleTransformer.documentRule(rule);
}); });
} }
}); });
...@@ -3799,6 +3804,9 @@ this._instances.splice(keys.length, this._instances.length - keys.length); ...@@ -3799,6 +3804,9 @@ this._instances.splice(keys.length, this._instances.length - keys.length);
_keySort: function (a, b) { _keySort: function (a, b) {
return this.collection.getKey(a) - this.collection.getKey(b); return this.collection.getKey(a) - this.collection.getKey(b);
}, },
_numericSort: function (a, b) {
return a - b;
},
_applySplicesUserSort: function (splices) { _applySplicesUserSort: function (splices) {
var c = this.collection; var c = this.collection;
var instances = this._instances; var instances = this._instances;
...@@ -3826,7 +3834,7 @@ addedKeys.push(key); ...@@ -3826,7 +3834,7 @@ addedKeys.push(key);
} }
} }
if (removedIdxs.length) { if (removedIdxs.length) {
removedIdxs.sort(); removedIdxs.sort(this._numericSort);
for (var i = removedIdxs.length - 1; i >= 0; i--) { for (var i = removedIdxs.length - 1; i >= 0; i--) {
var idx = removedIdxs[i]; var idx = removedIdxs[i];
if (idx !== undefined) { if (idx !== undefined) {
...@@ -4009,6 +4017,10 @@ selected: { ...@@ -4009,6 +4017,10 @@ selected: {
type: Object, type: Object,
notify: true notify: true
}, },
selectedItem: {
type: Object,
notify: true
},
toggle: { toggle: {
type: Boolean, type: Boolean,
value: false value: false
...@@ -4031,6 +4043,7 @@ this._selectedColl = Polymer.Collection.get(this.selected); ...@@ -4031,6 +4043,7 @@ this._selectedColl = Polymer.Collection.get(this.selected);
this.selected = null; this.selected = null;
this._selectedColl = null; this._selectedColl = null;
} }
this.selectedItem = null;
}, },
isSelected: function (item) { isSelected: function (item) {
if (this.multi) { if (this.multi) {
...@@ -4048,7 +4061,9 @@ this.unlinkPaths('selected.' + skey); ...@@ -4048,7 +4061,9 @@ this.unlinkPaths('selected.' + skey);
} }
} else { } else {
this.selected = null; this.selected = null;
this.selectedItem = null;
this.unlinkPaths('selected'); this.unlinkPaths('selected');
this.unlinkPaths('selectedItem');
} }
}, },
select: function (item) { select: function (item) {
...@@ -4068,8 +4083,10 @@ this.linkPaths('selected.' + skey, 'items.' + key); ...@@ -4068,8 +4083,10 @@ this.linkPaths('selected.' + skey, 'items.' + key);
if (this.toggle && item == this.selected) { if (this.toggle && item == this.selected) {
this.deselect(); this.deselect();
} else { } else {
this.linkPaths('selected', 'items.' + key);
this.selected = item; this.selected = item;
this.selectedItem = item;
this.linkPaths('selected', 'items.' + key);
this.linkPaths('selectedItem', 'items.' + key);
} }
} }
} }
......
<html><head><meta charset="UTF-8"><!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--><!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--></head><body><div hidden="" by-vulcanize=""><dom-module id="iron-localstorage" assetpath="../bower_components/iron-localstorage/"></dom-module>
<dom-module id="td-model" assetpath="/">
<template>
<iron-localstorage name="todos-polymer" value="{{items}}" on-iron-localstorage-load-empty="_initializeDefaultTodos"></iron-localstorage>
</template>
</dom-module>
<dom-module id="td-item" assetpath="/">
<template>
<template is="dom-if" if="{{!editing}}">
<div on-dblclick="_editAction">
<input type="checkbox" class="toggle" checked="{{item.completed::change}}">
<label>{{item.title}}</label>
<button class="destroy" on-tap="_destroyAction"></button>
</div>
</template>
<template is="dom-if" if="{{editing}}">
<input is="td-input" id="edit" class="edit" value="{{_editingValue::input}}" on-td-input-commit="_commitAction" on-td-input-cancel="_cancelAction" on-blur="_onBlur">
</template>
</template>
</dom-module>
<dom-module id="td-todos" assetpath="/">
<template>
<style> :host { display: block; } </style>
<flatiron-director route="{{route}}" hidden=""></flatiron-director>
<section id="todoapp">
<header id="header">
<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>
<template is="dom-if" if="{{items.length}}">
<section id="main">
<input id="toggle-all" type="checkbox" checked="[[allCompleted]]" on-change="toggleAllCompletedAction">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" on-td-destroy-item="destroyItemAction">
<template id="todo-list-repeater" is="dom-repeat" items="{{items}}" filter="{{matchesFilter(route)}}" observe="completed">
<li is="td-item" item="{{item}}"></li>
</template>
</ul>
</section>
<footer id="footer">
<span id="todo-count">
<strong>{{activeCount}}</strong>
<span>{{_computeItemsLeft(activeCount)}}</span> left
</span>
<iron-selector id="filters" selected="{{route}}" attr-for-selected="route">
<li route="">
<a href="#/" class$="{{_computeLinkClass(route, '')}}">All</a>
</li>
<li route="active">
<a href="#/active" class$="{{_computeLinkClass(route, 'active')}}">Active</a>
</li>
<li route="completed">
<a href="#/completed" class$="{{_computeLinkClass(route, 'completed')}}">Completed</a>
</li>
</iron-selector>
<template is="dom-if" if="{{anyCompleted}}">
<button id="clear-completed" on-tap="clearCompletedAction" style="visibility:visible">Clear completed</button>
</template>
</footer>
</template>
</section>
</template>
</dom-module>
</div><script src="elements.build.js"></script></body></html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<link rel="import" href="td-model.html">
<link rel="import" href="td-todos.html">
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="td-input"> <script>
<script> (function() {
(function() {
'use strict'; 'use strict';
var ENTER_KEY = 13; var ENTER_KEY = 13;
var ESC_KEY = 27; var ESC_KEY = 27;
Polymer({ Polymer({
is: 'td-input', is: 'td-input',
extends: 'input', extends: 'input',
listeners: { listeners: {
'keyup': 'keyupAction', 'keyup': '_keyupAction',
'keypress': 'keypressAction' 'keypress': '_keypressAction'
}, },
keypressAction: function(e, detail, sender) {
_keypressAction: function(e, detail, sender) {
// Listen for enter on keypress but esc on keyup, because // Listen for enter on keypress but esc on keyup, because
// IE doesn't fire keyup for enter. // IE doesn't fire keyup for enter.
if (e.keyCode === ENTER_KEY) { if (e.keyCode === ENTER_KEY) {
this.fire('td-input-commit'); this.fire('td-input-commit');
} }
}, },
keyupAction: function(e, detail, sender) {
_keyupAction: function(e, detail, sender) {
if (e.keyCode === ESC_KEY) { if (e.keyCode === ESC_KEY) {
this.fire('td-input-cancel'); this.fire('td-input-cancel');
} }
} }
}); });
})(); })();
</script> </script>
</dom-module>
...@@ -4,14 +4,19 @@ ...@@ -4,14 +4,19 @@
<dom-module id="td-item"> <dom-module id="td-item">
<template> <template>
<template is="dom-if" if="{{!editing}}"> <template is="dom-if" if="{{!editing}}">
<div on-dblclick="editAction"> <div on-dblclick="_editAction">
<input type="checkbox" class="toggle" checked="{{item.completed::change}}" on-click="itemChangeAction"> <input type="checkbox" class="toggle"
checked="{{item.completed::change}}">
<label>{{item.title}}</label> <label>{{item.title}}</label>
<button class="destroy" on-click="destroyAction"></button> <button class="destroy" on-tap="_destroyAction"></button>
</div> </div>
</template> </template>
<template is="dom-if" if="{{editing}}"> <template is="dom-if" if="{{editing}}">
<input is="td-input" id="edit" class="edit" value$="{{item.title}}" on-td-input-commit="commitAction" on-td-input-cancel="cancelAction" on-blur="onBlur"> <input is="td-input" id="edit" class="edit"
value="{{_editingValue::input}}"
on-td-input-commit="_commitAction"
on-td-input-cancel="_cancelAction"
on-blur="_onBlur">
</template> </template>
</template> </template>
<script> <script>
...@@ -20,7 +25,9 @@ ...@@ -20,7 +25,9 @@
Polymer({ Polymer({
is: 'td-item', is: 'td-item',
extends: 'li', extends: 'li',
properties: { properties: {
editing: { editing: {
type: Boolean, type: Boolean,
...@@ -28,49 +35,48 @@ ...@@ -28,49 +35,48 @@
}, },
item: { item: {
type: Object, type: Object,
value: function() { value: function() { return {}; }
return {};
}
}, },
}, },
observers: ['setRootClass(item.completed, editing)'],
setRootClass: function(completed, editing) { observers: ['setHostClass(item.completed, editing)'],
this.classList[completed ? 'add' : 'remove']('completed');
this.classList[editing ? 'add' : 'remove']('editing'); setHostClass: function(completed, editing) {
// Note: TodoMVC has styling for classes. Too bad we can't use the
// editing property and reflectToAttribute.
this.toggleClass('completed', completed);
this.toggleClass('editing', editing);
}, },
onBlur: function() {
this.commitAction(); _onBlur: function() {
this._commitAction();
this.editing = false; this.editing = false;
}, },
editAction: function() {
_editAction: function() {
this.editing = true; this.editing = true;
this._editingValue = this.item.title;
// Wait one tick template to stamp.
this.async(function() { this.async(function() {
var elm = this.querySelector('#edit'); this.querySelector('#edit').focus();
// It looks like polymer is trying to be smart here and not updating the
// title attribute on the input when it is changed. To work around this, we manually have
// to set the value again when we go into edit mode.
elm.value = this.item.title;
elm.focus();
}); });
}, },
commitAction: function() {
_commitAction: function() {
if (this.editing) { if (this.editing) {
this.editing = false; this.editing = false;
this.set('item.title', this.querySelector('#edit').value.trim()); this.set('item.title', this._editingValue.trim());
if (this.item.title === '') { if (this.item.title === '') {
this.destroyAction(); this._destroyAction();
} }
this.fire('td-item-changed');
} }
}, },
cancelAction: function() {
_cancelAction: function() {
this.editing = false; this.editing = false;
}, },
itemChangeAction: function(e, details) {
this.set('item.completed', e.target.checked); _destroyAction: function() {
this.fire('td-item-changed');
},
destroyAction: function() {
this.fire('td-destroy-item', this.item); this.fire('td-destroy-item', this.item);
} }
}); });
......
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/iron-localstorage/iron-localstorage.html">
<dom-module id="td-model"> <dom-module id="td-model">
<template> <template>
<iron-localstorage name="todos-polymer" value="{{items}}" on-iron-localstorage-load-empty="initializeDefaultTodos"></iron-localstorage> <iron-localstorage name="todos-polymer" value="{{items}}"
on-iron-localstorage-load-empty="_initializeDefaultTodos"></iron-localstorage>
</template> </template>
<script> <script>
(function() { (function() {
...@@ -10,16 +12,25 @@ ...@@ -10,16 +12,25 @@
Polymer({ Polymer({
is: 'td-model', is: 'td-model',
hostAttributes: {
hidden: true
},
properties: { properties: {
items: { items: {
type: Array, type: Array,
notify: true notify: true
}, },
filter: String filter: {
type: String
}
}, },
initializeDefaultTodos: function() {
_initializeDefaultTodos: function() {
this.items = []; this.items = [];
}, },
newItem: function(title) { newItem: function(title) {
title = String(title).trim(); title = String(title).trim();
...@@ -27,50 +38,39 @@ ...@@ -27,50 +38,39 @@
return; return;
} }
this.push('items', { this.push('items', {title: title, completed: false});
title: title,
completed: false
});
}, },
getCompletedCount: function(items) {
return items === null ? 0 : items.filter(this.filters.completed).length getCompletedCount: function() {
}, return this.items ? this.items.filter(this.filters.completed).length : 0;
getActiveCount: function(items) {
return items.length - this.getCompletedCount(items);
}, },
areAllCompleted: function(items) {
return this.getCompletedCount(items) && !this.getActiveCount(items) ? true : false; getActiveCount: function() {
return this.items.length - this.getCompletedCount(this.items);
}, },
matchesFilter: function(item, filter) { matchesFilter: function(item, filter) {
var fn = this.filters[filter]; var fn = this.filters[filter];
return this.filtered = fn ? fn(item) : true; return fn ? fn(item) : true;
}, },
destroyItem: function(item) { destroyItem: function(item) {
var i = this.items.indexOf(item); var i = this.items.indexOf(item);
if (i > -1) {
i !== -1 && this.splice('items', i, 1); this.splice('items', i, 1)
}
}, },
clearCompletedItems: function (){
clearCompletedItems: function() {
this.items = this.items.filter(this.filters.active); this.items = this.items.filter(this.filters.active);
}, },
setItemsCompleted: function(completed) { setItemsCompleted: function(completed) {
// Since we are mutating elements in an array here for (var i = 0; i < this.items.length; ++i) {
// and we want everyone to know about it we must go through this.set(['items', i, 'completed'], completed);
// the polymer internal `splice` api. The fix that comes to my mind here
// would be to use a hash or set to represent this structure so that the mutations
// would happening on an object with real key value pairs instead of an object
// nested inside of an array
// Polymer array mutation docs: https://www.polymer-project.org/1.0/docs/devguide/properties.html#array-mutation
this.items.forEach(function(item, i) {
if (this.filter) {
if (this.filters[this.filter](item)) {
this.splice('items', i, 1, {title: item.title, completed: completed});
} }
} else {
this.splice('items', i, 1, {title: item.title, completed: completed});
}
}, this);
}, },
filters: { filters: {
active: function(item) { active: function(item) {
return !item.completed; return !item.completed;
......
...@@ -6,140 +6,142 @@ ...@@ -6,140 +6,142 @@
<dom-module id="td-todos"> <dom-module id="td-todos">
<template> <template>
<flatiron-director id="router" route="{{route}}"></flatiron-director> <style>
:host {
display: block;
}
</style>
<flatiron-director route="{{route}}" hidden></flatiron-director>
<section id="todoapp"> <section id="todoapp">
<header id="header"> <header id="header">
<input is="td-input" id="new-todo" placeholder="What needs to be done?" autofocus on-td-input-commit="addTodoAction" on-td-input-cancel="cancelAddTodoAction"> <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> </header>
<template is="dom-if" if="{{hasTodos(items.length)}}">
<template is="dom-if" if="{{items.length}}">
<section id="main"> <section id="main">
<input id="toggle-all" type="checkbox" on-change="toggleAllCompletedAction" checked="[[areAllCompleted]]"> <input id="toggle-all" type="checkbox" checked="[[allCompleted]]"
on-change="toggleAllCompletedAction">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" on-td-destroy-item="destroyItemAction"> <ul id="todo-list" on-td-destroy-item="destroyItemAction">
<template id="todo-list-repeater" is="dom-repeat" items="{{items}}" filter="matchesFilter" observe="completed"> <template id="todo-list-repeater" is="dom-repeat" items="{{items}}"
filter="{{matchesFilter(route)}}" observe="completed">
<li is="td-item" item="{{item}}"></li> <li is="td-item" item="{{item}}"></li>
</template> </template>
</ul> </ul>
</section> </section>
</template>
<template is="dom-if" if="{{hasTodos(items.length)}}">
<footer id="footer"> <footer id="footer">
<span id="todo-count"><strong>{{getActiveCount(items, items.*)}}</strong> <span>{{getItemWord(items, items.*)}}</span> left</span> <span id="todo-count">
<iron-selector on-tap="onTap" id="filters" selected="{{getSelectedRoute(route)}}}}" attr-for-selected="name"> <strong>{{activeCount}}</strong>
<li name="all"> <span>{{_computeItemsLeft(activeCount)}}</span> left
<a href="#/">All</a> </span>
<iron-selector id="filters" selected="{{route}}" attr-for-selected="route">
<li route="">
<a href="#/" class$="{{_computeLinkClass(route, '')}}">All</a>
</li> </li>
<li name="active"> <li route="active">
<a href="#/active">Active</a> <a href="#/active" class$="{{_computeLinkClass(route, 'active')}}">Active</a>
</li> </li>
<li name="completed"> <li route="completed">
<a href="#/completed">Completed</a> <a href="#/completed" class$="{{_computeLinkClass(route, 'completed')}}">Completed</a>
</li> </li>
</iron-selector> </iron-selector>
<template is="dom-if" if="{{anyCompleted(items, items.*)}}">
<button id="clear-completed" on-click="clearCompletedAction" style="visibility:visible">Clear completed</button> <template is="dom-if" if="{{anyCompleted}}">
<button id="clear-completed" on-tap="clearCompletedAction"
style="visibility:visible">Clear completed</button>
</template> </template>
</footer> </footer>
</template> </template>
</section> </section>
</template> </template>
<script> <script>
(function() { (function() {
'use strict'; 'use strict';
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer({ Polymer({
is: 'td-todos', is: 'td-todos',
properties: { properties: {
modelId: String,
areAllCompleted: {
type: Boolean,
computed: 'allCompleted(items.*)'
},
items: { items: {
type: Array type: Array
}, },
model: { model: {
type: Object type: Object
}, },
route: { modelId: {
type: String, type: String
observer: 'refreshFiltered'
}
},
onTap: function() {
this.setActiveFilterChildClass();
},
setActiveFilterChildClass: function() {
// iron-selector should maybe allow selecting of arbitrary subnodes?
var filters = this.querySelector('#filters');
var prev = filters.querySelector('a.selected');
prev && prev.classList.remove('selected');
this.async(function() {
filters.selectedItem.querySelector('a').classList.add('selected');
});
},
getSelectedRoute: function(route) {
return route || 'all';
}, },
anyCompleted: function(items) { route: {
return this.model.getCompletedCount(items) > 0; type: String
}, },
getActiveCount: function(items) { allCompleted: {
return this.model.getActiveCount(items); type: Boolean,
computed: '_computeAllCompleted(anyCompleted, activeCount)'
}, },
refreshFiltered: function() { activeCount: {
// WAT: So it would be nice if repeat would be able to "observe" and external instance prop type: Number,
// since it does not we have to "hack" this. computed: '_computeActiveCount(items, items.*)'
var elm = this.querySelector('#todo-list-repeater');
elm && elm._applyFullRefresh();
}, },
matchesFilter: function(item) { anyCompleted: {
return this.model.matchesFilter(item, this.route); type: Boolean,
computed: '_computeAnyCompleted(items, items.*)'
}
}, },
attached: function() { attached: function() {
document.querySelector('#router').addEventListener('director-route', this.routeChanged.bind(this)); this.model = document.querySelector('#' + this.modelId);
},
// get a reference to the "model" which is our datastore interface _computeLinkClass: function(currRoute, route) {
this.set('model', document.querySelector('#model')); return currRoute === route ? 'selected' : '';
},
// this seems like smell... however I am not sure of a better way _computeAnyCompleted: function(items) {
this.addEventListener('dom-change', function() { return this.model ? this.model.getCompletedCount() > 0 : false;
if (this.querySelector('#filters') !== null) {
this.setActiveFilterChildClass();
this.removeEventListener('dom-change');
}
});
}, },
allCompleted: function(items) {
return this.model.areAllCompleted(this.items); _computeActiveCount: function(items) {
return this.model ? this.model.getActiveCount() : 0;
}, },
getItemWord: function(items) {
return items.length === 1 ? 'item' : 'items'; _computeAllCompleted: function(anyCompleted, activeCount) {
return anyCompleted && !activeCount;
}, },
hasTodos: function(todoCount) {
return todoCount > 0; _computeItemsLeft: function(count) {
return count < 2 ? 'item' : 'items';
}, },
routeChanged: function(e) {
this.model.filter = e.detail; matchesFilter: function(route) {
return function(item, index, array) {
return this.model ? this.model.matchesFilter(item, route) : false;
}.bind(this);
}, },
addTodoAction: function() { addTodoAction: function() {
this.model.newItem(this.$['new-todo'].value); this.model.newItem(this.$['new-todo'].value);
// when polyfilling Object.observe, make sure we update immediately
this.$['new-todo'].value = ''; this.$['new-todo'].value = '';
}, },
cancelAddTodoAction: function() { cancelAddTodoAction: function() {
this.$['new-todo'].value = ''; this.$['new-todo'].value = '';
}, },
destroyItemAction: function(e, detail) { destroyItemAction: function(e, detail) {
this.model.destroyItem(detail); this.model.destroyItem(detail);
}, },
toggleAllCompletedAction: function(e) { toggleAllCompletedAction: function(e) {
this.model.setItemsCompleted(e.target.checked); this.model.setItemsCompleted(e.target.checked);
}, },
clearCompletedAction: function() { clearCompletedAction: function() {
this.model.clearCompletedItems(); this.model.clearCompletedItems();
} }
......
...@@ -3,26 +3,25 @@ ...@@ -3,26 +3,25 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Polymer • TodoMVC</title> <title>Polymer • TodoMVC</title>
<script src="bower_components/webcomponentsjs/webcomponents.js"></script> <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js" async></script>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="bower_components/todomvc-common/base.css">
<link rel="stylesheet" href="bower_components/todomvc-app-css/index.css"> <link rel="stylesheet" href="bower_components/todomvc-app-css/index.css">
<link rel="import" href="bower_components/iron-localstorage/iron-localstorage.html"> <link rel="import" href="elements/elements.build.html" async>
<link rel="import" href="elements/td-model.html"> <!-- <link rel="import" href="elements/elements.html"> -->
<link rel="import" href="elements/td-todos.html">
</head> </head>
<body> <body>
<div id="todoapp"> <div id="todoapp">
<header> <header>
<h1>todos</h1> <h1>todos</h1>
</header> </header>
<template is="dom-bind" id="root"> <template is="dom-bind">
<td-model id="model" items="{{todos}}"></td-model> <td-model id="model" items="{{todos}}"></td-model>
<td-todos items="{{todos}}"></td-todos> <td-todos model-id="model" items="{{todos}}"></td-todos>
</template> </template>
</div> </div>
<footer id="info"> <footer id="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Created by <a href="http://www.polymer-project.org">The Polymer Authors</a></p> <p>Created by <a href="https://www.polymer-project.org/1.0/">The Polymer Authors</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
<script src="bower_components/todomvc-common/base.js"></script> <script src="bower_components/todomvc-common/base.js"></script>
......
{
"name": "polymer-todomvc",
"version": "1.0.0",
"description": "Polymer TodoMVC demo",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/tastejs/todomvc"
},
"keywords": [
"polymer",
"todomvc"
],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/tastejs/todomvc/issues"
},
"homepage": "https://github.com/tastejs/todomvc",
"devDependencies": {
"polybuild": "^1.0.5"
}
}
#!/bin/sh
# vulcanize elements.html \
# --inline-script --inline-css --strip-comments | \
# crisper -h elements.build.html -j elements.build.js
polybuild --maximum-crush elements/elements.html
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