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/.gitignore
bower_components/iron-localstorage/README.md
......@@ -19,7 +21,7 @@ bower_components/flatiron-director
!bower_components/flatiron-director/flatiron-director.html
bower_components/webcomponentsjs
!bower_components/webcomponentsjs/webcomponents.js
!bower_components/webcomponentsjs/webcomponents-lite.min.js
bower_components/polymer/.bower.json
bower_components/polymer/LICENSE.txt
......
{
"name": "todomvc-polymer",
"version": "0.0.1",
"version": "0.0.2",
"dependencies": {
"todomvc-common": "^1.0.1",
"todomvc-app-css": "^1.0.0",
"polymer": "Polymer/polymer#1.1.0",
"iron-selector": "polymerelements/iron-selector",
"flatiron-director": "PolymerLabs/flatiron-director#1.0.0",
"iron-localstorage": "polymerelements/iron-localstorage"
"polymer": "Polymer/polymer#^1.1.4",
"iron-selector": "PolymerElements/iron-selector#^1.0.5",
"flatiron-director": "PolymerLabs/flatiron-director#^1.0.0",
"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
var i = this.selectedValues.indexOf(value);
var unselected = i < 0;
if (unselected) {
this.selectedValues.push(value);
this.push('selectedValues',value);
} else {
this.selectedValues.splice(i, 1);
this.splice('selectedValues',i,1);
}
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
/** @polymerBehavior */
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: {
/**
* 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.
*
* @attribute attrForSelected
* @type {string}
*/
attrForSelected: {
type: String,
......@@ -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.
*
* @attribute selected
* @type {string}
*/
selected: {
type: String,
......@@ -42,9 +64,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/**
* Returns the currently selected item.
*
* @attribute selectedItem
* @type {Object}
*/
selectedItem: {
type: Object,
......@@ -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
* will listen for this event from items and update the selection state.
* Set to empty string to listen to no events.
*
* @attribute activateEvent
* @type {string}
* @default 'tap'
*/
activateEvent: {
type: String,
......@@ -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.
*
* @attribute selectable
* @type {string}
*/
selectable: String,
/**
* The class to set on elements when selected.
*
* @attribute selectedClass
* @type {string}
*/
selectedClass: {
type: String,
......@@ -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.
*
* @attribute selectedAttribute
* @type {string}
*/
selectedAttribute: {
type: String,
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: [
'_updateSelected(attrForSelected, selected)'
],
excludedLocalNames: {
'template': 1
},
created: function() {
this._bindFilterItem = this._filterItem.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
attached: function() {
this._observer = this._observeItems(this);
this._contentObserver = this._observeContent(this);
if (!this.selectedItem && this.selected) {
this._updateSelected(this.attrForSelected,this.selected)
}
},
detached: function() {
......@@ -186,9 +206,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
_removeListener: function(eventName) {
// There is no unlisten yet...
// https://github.com/Polymer/polymer/issues/1639
//this.removeEventListener(eventName, this._bindActivateHandler);
this.unlisten(this, eventName, '_activateHandler');
},
_activateEventChanged: function(eventName, old) {
......@@ -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.
_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) {
this._updateSelected();
}
......@@ -277,11 +303,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
_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 items = this.items;
while (t && t != this) {
......
......@@ -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.
*/
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);
}
}
}());
Polymer = {
window.Polymer = {
Settings: function () {
var user = window.Polymer || {};
location.search.slice(1).split('&').forEach(function (o) {
......@@ -48,15 +48,21 @@ useNativeCustomElements: useNativeCustomElements
(function () {
var userPolymer = window.Polymer;
window.Polymer = function (prototype) {
var ctor = desugar(prototype);
prototype = ctor.prototype;
if (typeof prototype === 'function') {
prototype = prototype.prototype;
}
if (!prototype) {
prototype = {};
}
var factory = desugar(prototype);
prototype = factory.prototype;
var options = { prototype: prototype };
if (prototype.extends) {
options.extends = prototype.extends;
}
Polymer.telemetry._registrate(prototype);
document.registerElement(prototype.is, options);
return ctor;
return factory;
};
var desugar = function (prototype) {
var base = Polymer.Base;
......@@ -133,6 +139,8 @@ _addFeature: function (feature) {
this.extend(this, feature);
},
registerCallback: function () {
this._desugarBehaviors();
this._doBehavior('beforeRegister');
this._registerFeatures();
this._doBehavior('registered');
},
......@@ -217,6 +225,9 @@ Polymer.telemetry.instanceCount = 0;
(function () {
var modules = {};
var lcModules = {};
var findModule = function (id) {
return modules[id] || lcModules[id.toLowerCase()];
};
var DomModule = function () {
return document.createElement('dom-module');
};
......@@ -235,16 +246,18 @@ lcModules[id.toLowerCase()] = this;
}
},
import: function (id, selector) {
var m = modules[id] || lcModules[id.toLowerCase()];
if (id) {
var m = findModule(id);
if (!m) {
forceDocumentUpgrade();
m = modules[id];
m = findModule(id);
}
if (m && selector) {
m = m.querySelector(selector);
}
return m;
}
}
});
var cePolyfill = window.CustomElements && !CustomElements.useNative;
document.registerElement('dom-module', DomModule);
......@@ -252,8 +265,7 @@ function forceDocumentUpgrade() {
if (cePolyfill) {
var script = document._currentScript || document.currentScript;
var doc = script && script.ownerDocument;
if (doc && !doc.__customElementsForceUpgraded) {
doc.__customElementsForceUpgraded = true;
if (doc) {
CustomElements.upgradeAll(doc);
}
}
......@@ -275,11 +287,17 @@ this.is = this.is.toLowerCase();
});
Polymer.Base._addFeature({
behaviors: [],
_prepBehaviors: function () {
_desugarBehaviors: function () {
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) {
var flat = [];
......@@ -294,15 +312,6 @@ this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for miss
}, this);
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) {
Object.getOwnPropertyNames(b).forEach(function (n) {
switch (n) {
......@@ -326,6 +335,15 @@ break;
}
}, 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) {
this.behaviors.forEach(function (b) {
this._invokeBehavior(b, name, args);
......@@ -558,7 +576,7 @@ debouncer.stop();
}
}
});
Polymer.version = '1.1.0';
Polymer.version = '1.1.4';
Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
......
......@@ -457,47 +457,43 @@ Polymer.dom.addDebouncer(host.debounce('_distribute', host._distributeContent));
}
},
appendChild: function (node) {
var handled;
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;
return this._addNode(node);
},
insertBefore: function (node, ref_node) {
if (!ref_node) {
return this.appendChild(node);
}
var handled;
this._ensureContentLogicalInfo(node);
return this._addNode(node, ref_node);
},
_addNode: function (node, ref_node) {
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 index = children.indexOf(ref_node);
if (index < 0) {
throw Error('The ref_node to be inserted before is not a child ' + 'of this node');
}
}
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;
}
var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node, ref_node);
if (ref_node) {
nativeInsertBefore.call(container, node, ref_node);
} else {
nativeAppendChild.call(container, node);
}
}
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
}
return node;
},
......@@ -505,14 +501,8 @@ removeChild: function (node) {
if (factory(node).parentNode !== this.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);
handled = this._maybeDistribute(node, this.node);
} else {
this._removeNodeFromHost(node);
}
if (!handled) {
if (!this._maybeDistribute(node, this.node)) {
var container = this.node._isShadyRoot ? this.node.host : this.node;
if (container === node.parentNode) {
removeFromComposedParent(container, node);
......@@ -560,7 +550,6 @@ if (hasContent) {
var root = this._ownerShadyRootForNode(parent);
if (root) {
var host = root.host;
this._updateInsertionPoints(host);
this._lazyDistribute(host);
}
}
......@@ -570,10 +559,30 @@ this._lazyDistribute(parent);
}
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) {
if (this.node.shadyRoot) {
if (node._composedParent) {
nativeRemoveChild.call(node._composedParent, node);
var parent = getComposedParent(node);
if (parent) {
nativeRemoveChild.call(parent, node);
}
return true;
}
......@@ -586,20 +595,8 @@ saveLightChildrenIfNeeded(c);
saveLightChildrenIfNeeded(factory(c).parentNode);
}
},
_nodeIsInLogicalTree: function (node) {
return Boolean(node._lightParent !== undefined || node._isShadyRoot || node.shadyRoot);
},
_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);
}
_nodeHasLogicalChildren: function (node) {
return Boolean(node._lightChildren !== undefined);
},
_parentNeedsDistribution: function (parent) {
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
......@@ -609,6 +606,7 @@ var hostNeedsDist;
var root;
var parent = node._lightParent;
if (parent) {
factory(node)._distributeParent();
root = this._ownerShadyRootForNode(node);
if (root) {
root.host._elementRemove(node);
......@@ -621,7 +619,7 @@ if (root && hostNeedsDist) {
this._updateInsertionPoints(root.host);
this._lazyDistribute(root.host);
} else if (ensureComposedRemoval) {
removeFromComposedParent(parent || node.parentNode, node);
removeFromComposedParent(getComposedParent(node), node);
}
},
_removeDistributedChildren: function (root, container) {
......@@ -845,7 +843,7 @@ configurable: true
},
parentNode: {
get: function () {
return this.node._lightParent || (this.node.__patched ? this.node._composedParent : this.node.parentNode);
return this.node._lightParent || getComposedParent(this.node);
},
configurable: true
},
......@@ -967,6 +965,18 @@ DomApi.prototype._getComposedInnerHTML = function () {
return getInnerHTML(this.node, true);
};
} 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) {
return Array.prototype.slice.call(this.node.querySelectorAll(selector));
};
......@@ -979,9 +989,6 @@ return n;
n = n.parentNode;
}
};
DomApi.prototype.cloneNode = function (deep) {
return this.node.cloneNode(deep);
};
DomApi.prototype.importNode = function (externalNode, deep) {
var doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
return doc.importNode(externalNode, deep);
......@@ -1028,7 +1035,7 @@ return this.node.innerHTML = value;
configurable: true
}
});
var forwards = [
var forwardProperties = [
'parentNode',
'firstChild',
'lastChild',
......@@ -1039,7 +1046,7 @@ var forwards = [
'nextElementSibling',
'previousElementSibling'
];
forwards.forEach(function (name) {
forwardProperties.forEach(function (name) {
Object.defineProperty(DomApi.prototype, name, {
get: function () {
return this.node[name];
......@@ -1123,6 +1130,9 @@ node._composedChildren = null;
addNodeToComposedChildren(node, parent, children, i);
}
}
function getComposedParent(node) {
return node.__patched ? node._composedParent : node.parentNode;
}
function addNodeToComposedChildren(node, parent, children, i) {
node._composedParent = parent;
children.splice(i >= 0 ? i : children.length, 0, node);
......@@ -1147,12 +1157,13 @@ node._lightChildren = c$;
}
}
function hasInsertionPoint(root) {
return Boolean(root._insertionPoints.length);
return Boolean(root && root._insertionPoints.length);
}
var p = Element.prototype;
var matchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
return {
getLightChildren: getLightChildren,
getComposedParent: getComposedParent,
getComposedChildren: getComposedChildren,
removeFromComposedParent: removeFromComposedParent,
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
......@@ -1341,7 +1352,9 @@ var composed = getComposedChildren(container);
var splices = Polymer.ArraySplice.calculateSplices(children, composed);
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++) {
if (getComposedParent(n) === container) {
remove(n);
}
composed.splice(s.index + d, 1);
}
d -= s.addedCount;
......@@ -1354,6 +1367,7 @@ insertBefore(container, n, next);
composed.splice(j, 0, n);
}
}
ensureComposedParent(container, children);
},
_matchesContentSelect: function (node, contentElement) {
var select = contentElement.getAttribute('select');
......@@ -1383,6 +1397,7 @@ var getLightChildren = Polymer.DomApi.getLightChildren;
var matchesSelector = Polymer.DomApi.matchesSelector;
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
var getComposedChildren = Polymer.DomApi.getComposedChildren;
var getComposedParent = Polymer.DomApi.getComposedParent;
var removeFromComposedParent = Polymer.DomApi.removeFromComposedParent;
function distributeNodeInto(child, insertionPoint) {
insertionPoint._distributedNodes.push(child);
......@@ -1436,8 +1451,10 @@ node._composedParent = null;
nativeRemoveChild.call(parentNode, node);
}
}
function getComposedParent(node) {
return node.__patched ? node._composedParent : node.parentNode;
function ensureComposedParent(parent, children) {
for (var i = 0, n; i < children.length; i++) {
children[i]._composedParent = parent;
}
}
function getTopDistributingHost(host) {
while (host && hostNeedsRedistribution(host)) {
......
......@@ -236,7 +236,11 @@ if (!this._template) {
this._notes = [];
} else {
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._processAnnotations(this._notes);
Polymer.Annotations.prepElement = null;
}
......@@ -683,12 +687,12 @@ gd = gobj[dep];
if (gd && gd[name]) {
gd[name] = (gd[name] || 1) - 1;
gd._count = (gd._count || 1) - 1;
}
if (gd._count === 0) {
node.removeEventListener(dep, this.handleNative);
}
}
}
}
node.removeEventListener(evType, handler);
},
register: function (recog) {
......@@ -1656,6 +1660,9 @@ name: arg,
model: this._modelForPath(arg)
};
var fc = arg[0];
if (fc === '-') {
fc = arg[1];
}
if (fc >= '0' && fc <= '9') {
fc = '#';
}
......@@ -1810,6 +1817,7 @@ var h$ = this._handlers;
for (var i = 0, l = h$.length, h; i < l && (h = h$[i]); i++) {
h[0].call(this, h[1], h[2]);
}
this._handlers = [];
}
});
(function () {
......@@ -1933,7 +1941,7 @@ this._boundPaths = this._boundPaths || {};
if (from) {
this._boundPaths[to] = from;
} else {
this.unbindPath(to);
this.unlinkPaths(to);
}
},
unlinkPaths: function (path) {
......@@ -1942,23 +1950,13 @@ delete this._boundPaths[path];
}
},
_notifyBoundPaths: function (path, value) {
var from, to;
for (var a in this._boundPaths) {
var b = this._boundPaths[a];
if (path.indexOf(a + '.') == 0) {
from = a;
to = b;
break;
this.notifyPath(this._fixPath(b, a, path), value);
} else if (path.indexOf(b + '.') == 0) {
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) {
......@@ -2186,7 +2184,7 @@ MIXIN_RULE: 1000
OPEN_BRACE: '{',
CLOSE_BRACE: '}',
_rx: {
comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,
port: /@import[^;]*;/gim,
customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim,
mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim,
......@@ -2230,6 +2228,9 @@ clearStyleRules: function (style) {
style.__cssRules = null;
},
forEachStyleRule: function (node, callback) {
if (!node) {
return;
}
var s = node.parsedSelector;
var skipRules = false;
if (node.type === this.ruleTypes.STYLE_RULE) {
......@@ -2258,47 +2259,44 @@ afterNode = n$[n$.length - 1];
target.insertBefore(style, afterNode && afterNode.nextSibling || target.firstChild);
return style;
},
cssFromModules: function (moduleIds) {
cssFromModules: function (moduleIds, warnIfNotFound) {
var modules = moduleIds.trim().split(' ');
var cssText = '';
for (var i = 0; i < modules.length; i++) {
cssText += this.cssFromModule(modules[i]);
cssText += this.cssFromModule(modules[i], warnIfNotFound);
}
return cssText;
},
cssFromModule: function (moduleId) {
cssFromModule: function (moduleId, warnIfNotFound) {
var m = Polymer.DomModule.import(moduleId);
if (m && !m._cssText) {
m._cssText = this._cssFromElement(m);
}
if (!m && warnIfNotFound) {
console.warn('Could not find style data in module named', moduleId);
}
return m && m._cssText || '';
},
_cssFromElement: function (element) {
var cssText = '';
var content = element.content || element;
var sourceDoc = element.ownerDocument;
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];
resolveDoc = sourceDoc;
addModule = null;
if (e.localName === 'template') {
cssText += this._cssFromElement(e);
} else {
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.parentNode.removeChild(e);
} else {
e = e.import && e.import.body;
resolveDoc = e.ownerDocument;
cssText += this.resolveCss(e.textContent, element.ownerDocument);
} else if (e.import && e.import.body) {
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;
......@@ -3232,18 +3230,25 @@ observer.observe(e, { childList: true });
_apply: function () {
var e = this.__appliedElement || this;
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();
var props = this._styleProperties;
var self = this;
e.textContent = styleUtil.toCssText(styleUtil.rulesForStyle(e), function (rule) {
var rules = styleUtil.rulesForStyle(element);
element.textContent = styleUtil.toCssText(rules, function (rule) {
var css = rule.cssText = rule.parsedCssText;
if (rule.propertyInfo && rule.propertyInfo.cssText) {
css = cssParse.removeCustomPropAssignment(css);
rule.cssText = propertyUtils.valueForProperties(css, props);
}
styleTransformer.documentRule(rule);
});
}
});
......@@ -3799,6 +3804,9 @@ this._instances.splice(keys.length, this._instances.length - keys.length);
_keySort: function (a, b) {
return this.collection.getKey(a) - this.collection.getKey(b);
},
_numericSort: function (a, b) {
return a - b;
},
_applySplicesUserSort: function (splices) {
var c = this.collection;
var instances = this._instances;
......@@ -3826,7 +3834,7 @@ addedKeys.push(key);
}
}
if (removedIdxs.length) {
removedIdxs.sort();
removedIdxs.sort(this._numericSort);
for (var i = removedIdxs.length - 1; i >= 0; i--) {
var idx = removedIdxs[i];
if (idx !== undefined) {
......@@ -4009,6 +4017,10 @@ selected: {
type: Object,
notify: true
},
selectedItem: {
type: Object,
notify: true
},
toggle: {
type: Boolean,
value: false
......@@ -4031,6 +4043,7 @@ this._selectedColl = Polymer.Collection.get(this.selected);
this.selected = null;
this._selectedColl = null;
}
this.selectedItem = null;
},
isSelected: function (item) {
if (this.multi) {
......@@ -4048,7 +4061,9 @@ this.unlinkPaths('selected.' + skey);
}
} else {
this.selected = null;
this.selectedItem = null;
this.unlinkPaths('selected');
this.unlinkPaths('selectedItem');
}
},
select: function (item) {
......@@ -4068,8 +4083,10 @@ this.linkPaths('selected.' + skey, 'items.' + key);
if (this.toggle && item == this.selected) {
this.deselect();
} else {
this.linkPaths('selected', 'items.' + key);
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">
<dom-module id="td-input">
<script>
(function() {
'use strict';
<script>
(function() {
'use strict';
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer({
is: 'td-input',
extends: 'input',
listeners: {
'keyup': 'keyupAction',
'keypress': 'keypressAction'
},
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>
</dom-module>
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer({
is: 'td-input',
extends: 'input',
listeners: {
'keyup': '_keyupAction',
'keypress': '_keypressAction'
},
_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>
......@@ -4,14 +4,19 @@
<dom-module id="td-item">
<template>
<template is="dom-if" if="{{!editing}}">
<div on-dblclick="editAction">
<input type="checkbox" class="toggle" checked="{{item.completed::change}}" on-click="itemChangeAction">
<div on-dblclick="_editAction">
<input type="checkbox" class="toggle"
checked="{{item.completed::change}}">
<label>{{item.title}}</label>
<button class="destroy" on-click="destroyAction"></button>
<button class="destroy" on-tap="_destroyAction"></button>
</div>
</template>
<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>
<script>
......@@ -20,7 +25,9 @@
Polymer({
is: 'td-item',
extends: 'li',
properties: {
editing: {
type: Boolean,
......@@ -28,49 +35,48 @@
},
item: {
type: Object,
value: function() {
return {};
}
value: function() { return {}; }
},
},
observers: ['setRootClass(item.completed, editing)'],
setRootClass: function(completed, editing) {
this.classList[completed ? 'add' : 'remove']('completed');
this.classList[editing ? 'add' : 'remove']('editing');
observers: ['setHostClass(item.completed, 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;
},
editAction: function() {
_editAction: function() {
this.editing = true;
this._editingValue = this.item.title;
// Wait one tick template to stamp.
this.async(function() {
var elm = this.querySelector('#edit');
// 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();
this.querySelector('#edit').focus();
});
},
commitAction: function() {
_commitAction: function() {
if (this.editing) {
this.editing = false;
this.set('item.title', this.querySelector('#edit').value.trim());
this.set('item.title', this._editingValue.trim());
if (this.item.title === '') {
this.destroyAction();
this._destroyAction();
}
this.fire('td-item-changed');
}
},
cancelAction: function() {
_cancelAction: function() {
this.editing = false;
},
itemChangeAction: function(e, details) {
this.set('item.completed', e.target.checked);
this.fire('td-item-changed');
},
destroyAction: function() {
_destroyAction: function() {
this.fire('td-destroy-item', this.item);
}
});
......
<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">
<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>
<script>
(function() {
......@@ -10,16 +12,25 @@
Polymer({
is: 'td-model',
hostAttributes: {
hidden: true
},
properties: {
items: {
type: Array,
notify: true
},
filter: String
filter: {
type: String
}
},
initializeDefaultTodos: function() {
_initializeDefaultTodos: function() {
this.items = [];
},
newItem: function(title) {
title = String(title).trim();
......@@ -27,50 +38,39 @@
return;
}
this.push('items', {
title: title,
completed: false
});
},
getCompletedCount: function(items) {
return items === null ? 0 : items.filter(this.filters.completed).length
this.push('items', {title: title, completed: false});
},
getActiveCount: function(items) {
return items.length - this.getCompletedCount(items);
getCompletedCount: function() {
return this.items ? this.items.filter(this.filters.completed).length : 0;
},
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) {
var fn = this.filters[filter];
return this.filtered = fn ? fn(item) : true;
return fn ? fn(item) : true;
},
destroyItem: function(item) {
var i = this.items.indexOf(item);
i !== -1 && this.splice('items', i, 1);
if (i > -1) {
this.splice('items', i, 1)
}
},
clearCompletedItems: function (){
clearCompletedItems: function() {
this.items = this.items.filter(this.filters.active);
},
setItemsCompleted: function(completed) {
// Since we are mutating elements in an array here
// and we want everyone to know about it we must go through
// 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);
for (var i = 0; i < this.items.length; ++i) {
this.set(['items', i, 'completed'], completed);
}
},
filters: {
active: function(item) {
return !item.completed;
......
......@@ -6,140 +6,142 @@
<dom-module id="td-todos">
<template>
<flatiron-director id="router" route="{{route}}"></flatiron-director>
<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">
<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="{{hasTodos(items.length)}}">
<template is="dom-if" if="{{items.length}}">
<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>
<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>
</template>
</ul>
</section>
</template>
<template is="dom-if" if="{{hasTodos(items.length)}}">
<footer id="footer">
<span id="todo-count"><strong>{{getActiveCount(items, items.*)}}</strong> <span>{{getItemWord(items, items.*)}}</span> left</span>
<iron-selector on-tap="onTap" id="filters" selected="{{getSelectedRoute(route)}}}}" attr-for-selected="name">
<li name="all">
<a href="#/">All</a>
<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 name="active">
<a href="#/active">Active</a>
<li route="active">
<a href="#/active" class$="{{_computeLinkClass(route, 'active')}}">Active</a>
</li>
<li name="completed">
<a href="#/completed">Completed</a>
<li route="completed">
<a href="#/completed" class$="{{_computeLinkClass(route, 'completed')}}">Completed</a>
</li>
</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>
</footer>
</template>
</section>
</template>
<script>
(function() {
'use strict';
var ENTER_KEY = 13;
var ESC_KEY = 27;
Polymer({
is: 'td-todos',
properties: {
modelId: String,
areAllCompleted: {
type: Boolean,
computed: 'allCompleted(items.*)'
},
items: {
type: Array
},
model: {
type: Object
},
modelId: {
type: String
},
route: {
type: String,
observer: 'refreshFiltered'
type: String
},
allCompleted: {
type: Boolean,
computed: '_computeAllCompleted(anyCompleted, activeCount)'
},
activeCount: {
type: Number,
computed: '_computeActiveCount(items, items.*)'
},
anyCompleted: {
type: Boolean,
computed: '_computeAnyCompleted(items, items.*)'
}
},
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) {
return this.model.getCompletedCount(items) > 0;
},
getActiveCount: function(items) {
return this.model.getActiveCount(items);
},
refreshFiltered: function() {
// WAT: So it would be nice if repeat would be able to "observe" and external instance prop
// since it does not we have to "hack" this.
var elm = this.querySelector('#todo-list-repeater');
elm && elm._applyFullRefresh();
attached: function() {
this.model = document.querySelector('#' + this.modelId);
},
matchesFilter: function(item) {
return this.model.matchesFilter(item, this.route);
_computeLinkClass: function(currRoute, route) {
return currRoute === route ? 'selected' : '';
},
attached: function() {
document.querySelector('#router').addEventListener('director-route', this.routeChanged.bind(this));
// get a reference to the "model" which is our datastore interface
this.set('model', document.querySelector('#model'));
// this seems like smell... however I am not sure of a better way
this.addEventListener('dom-change', function() {
if (this.querySelector('#filters') !== null) {
this.setActiveFilterChildClass();
this.removeEventListener('dom-change');
}
});
_computeAnyCompleted: function(items) {
return this.model ? this.model.getCompletedCount() > 0 : false;
},
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() {
this.model.newItem(this.$['new-todo'].value);
// when polyfilling Object.observe, make sure we update immediately
this.$['new-todo'].value = '';
},
cancelAddTodoAction: function() {
this.$['new-todo'].value = '';
},
destroyItemAction: function(e, detail) {
this.model.destroyItem(detail);
},
toggleAllCompletedAction: function(e) {
this.model.setItemsCompleted(e.target.checked);
},
clearCompletedAction: function() {
this.model.clearCompletedItems();
}
......
......@@ -3,26 +3,25 @@
<head>
<meta charset="utf-8">
<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-app-css/index.css">
<link rel="import" href="bower_components/iron-localstorage/iron-localstorage.html">
<link rel="import" href="elements/td-model.html">
<link rel="import" href="elements/td-todos.html">
<link rel="import" href="elements/elements.build.html" async>
<!-- <link rel="import" href="elements/elements.html"> -->
</head>
<body>
<div id="todoapp">
<header>
<h1>todos</h1>
</header>
<template is="dom-bind" id="root">
<template is="dom-bind">
<td-model id="model" items="{{todos}}"></td-model>
<td-todos items="{{todos}}"></td-todos>
<td-todos model-id="model" items="{{todos}}"></td-todos>
</template>
</div>
<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>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>
</footer>
<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