Commit 557e65c3 authored by Addy Osmani's avatar Addy Osmani

Merge pull request #1455 from ebidel/master

Polymer 1.0 demo cleanup
parents 41a93a95 2d42e06d
...@@ -28,7 +28,8 @@ ...@@ -28,7 +28,8 @@
"**/bower_components/**", "**/bower_components/**",
"examples/vanilladart/**/*.js", "examples/vanilladart/**/*.js",
"examples/duel/www/**", "examples/duel/www/**",
"examples/duel/src/main/webapp/js/lib/**" "examples/duel/src/main/webapp/js/lib/**",
"examples/polymer/elements/elements.build.js"
], ],
"requireSpaceBeforeBlockStatements": true, "requireSpaceBeforeBlockStatements": true,
"requireParenthesesAroundIIFE": true, "requireParenthesesAroundIIFE": true,
......
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
......
...@@ -2,34 +2,33 @@ ...@@ -2,34 +2,33 @@
> Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers. > Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers.
> _[Polymer - www.polymer-project.org](http://www.polymer-project.org/)_ > _[Polymer - www.polymer-project.org](https://www.polymer-project.org/)_
## Learning Polymer ## Learning Polymer
The [Polymer website](http://www.polymer-project.org) is a great resource for getting started. The [Polymer website](https://www.polymer-project.org) is a great resource for getting started.
Here are some links you may find helpful: Here are some links you may find helpful:
* [Getting Started](http://www.polymer-project.org/docs/start/everything.html) * [Getting Started](https://www.polymer-project.org/1.0/docs/start/getting-the-code.html)
* [FAQ](http://www.polymer-project.org/resources/faq.html) * [FAQ](https://www.polymer-project.org/0.5/resources/faq.html) (old)
* [Browser Compatibility](http://www.polymer-project.org/resources/compatibility.html) * [Browser Compatibility](https://www.polymer-project.org/1.0/resources/compatibility.html)
Get help from Polymer devs and users: Get help from Polymer devs and users:
* Find us on IRC on __#polymer__ at freenode. * Find us Slack - polymer.slack.com
* Join the high-traffic [polymer-dev](https://groups.google.com/forum/?fromgroups=#!forum/polymer-dev) Google group or the announcement-only [polymer-announce](https://groups.google.com/forum/?fromgroups=#!forum/polymer-announce) Google group. * Join the high-traffic [polymer-dev](https://groups.google.com/forum/?fromgroups=#!forum/polymer-dev) Google group or the announcement-only [polymer-announce](https://groups.google.com/forum/?fromgroups=#!forum/polymer-announce) Google group.
## Implementation ## Implementation
The Polymer implementation of TodoMVC has a few key differences with other implementations: The Polymer implementation of TodoMVC has a few key differences with other implementations:
* Since [Web Components](http://w3c.github.io/webcomponents/explainer/) allow you to create new types of DOM elements, the DOM tree is very different from other implementations. * [Web Components](http://w3c.github.io/webcomponents/explainer/) allow you to create new HTML elements that are reusable, composable, and encapsulated.
* The template, styling, and behavior are fully encapsulated in each custom element. Instead of having an overall stylesheet (`base.css` or `app.css`), each element that needs styling has its own stylesheet. * Non-visual elements such as the router and the model are also implemented as custom elements and appear in the DOM. Implementing them as custom elements instead of plain objects allows you to take advantage of Polymer's data binding and event handling throughout the app.
* Non-visual elements such as the router and the model are also implemented as custom elements and appear in the DOM. Implementing them as custom elements instead of plain objects allows you to take advantage of Polymer data binding and event handling throughout the app.
## Compatibility ## Compatibility
Polymer and its polyfills are intended to work in the latest version of [evergreen browsers](http://tomdale.net/2013/05/evergreen-browsers/). IE9 is not supported. Please refer to [Browser Compatibility](http://www.polymer-project.org/resources/compatibility.html) for more details. Polymer and the web component polyfills are intended to work in the latest version of [evergreen browsers](http://tomdale.net/2013/05/evergreen-browsers/). IE9 is not supported. Please refer to [Browser Compatibility](https://www.polymer-project.org/1.0/resources/compatibility.html) for more details.
## Running this sample ## Running this sample
...@@ -42,3 +41,11 @@ Polymer and its polyfills are intended to work in the latest version of [evergre ...@@ -42,3 +41,11 @@ Polymer and its polyfills are intended to work in the latest version of [evergre
`python -m SimpleHTTPServer` `python -m SimpleHTTPServer`
1. Browse to the server root 1. Browse to the server root
## Making updates
If you want to make a change to any of the elements in elements/elements.html, you'll need to install `polybuild` (Polymer's build tool) and re-vulcanize elements.build.html.
1. Run `npm install` (first time only)
1. Make a change
1. Run `npm run build`
{ {
"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({
is: 'td-input', Polymer({
extends: 'input', is: 'td-input',
listeners: {
'keyup': 'keyupAction', extends: 'input',
'keypress': 'keypressAction'
}, listeners: {
keypressAction: function(e, detail, sender) { 'keyup': '_keyupAction',
// Listen for enter on keypress but esc on keyup, because 'keypress': '_keypressAction'
// IE doesn't fire keyup for enter. },
if (e.keyCode === ENTER_KEY) {
this.fire('td-input-commit'); _keypressAction: function(e, detail, sender) {
} // Listen for enter on keypress but esc on keyup, because
}, // IE doesn't fire keyup for enter.
keyupAction: function(e, detail, sender) { if (e.keyCode === ENTER_KEY) {
if (e.keyCode === ESC_KEY) { this.fire('td-input-commit');
this.fire('td-input-cancel'); }
} },
}
}); _keyupAction: function(e, detail, sender) {
})(); if (e.keyCode === ESC_KEY) {
</script> this.fire('td-input-cancel');
</dom-module> }
}
});
})();
</script>
...@@ -4,23 +4,30 @@ ...@@ -4,23 +4,30 @@
<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>
(function() { (function () {
'use strict'; 'use strict';
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.async(function() { this._editingValue = this.item.title;
var elm = this.querySelector('#edit'); // Wait one tick template to stamp.
// It looks like polymer is trying to be smart here and not updating the this.async(function () {
// title attribute on the input when it is changed. To work around this, we manually have this.querySelector('#edit').focus();
// 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 () {
'use strict'; 'use strict';
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
}, },
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) { 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,141 +6,143 @@ ...@@ -6,141 +6,143 @@
<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
}, },
modelId: {
type: String
},
route: { route: {
type: String, type: String
observer: 'refreshFiltered' },
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(); attached: function () {
}, this.model = document.querySelector('#' + this.modelId);
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();
}, },
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)); _computeAnyCompleted: function(items) {
return this.model ? this.model.getCompletedCount() > 0 : false;
// 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');
}
});
}, },
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,28 +3,41 @@ ...@@ -3,28 +3,41 @@
<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>
<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"> for dev -->
<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" async></script>
<script>
(function () {
// Feature detect and conditionally load the polyfills.
var webComponentsSupported = ('registerElement' in document
&& 'import' in document.createElement('link')
&& 'content' in document.createElement('template'));
if (!webComponentsSupported) {
var script = document.createElement('script');
script.async = true;
script.src = 'bower_components/webcomponentsjs/webcomponents-lite.min.js';
document.head.appendChild(script);
}
})();
</script>
</body> </body>
</html> </html>
{
"private": true,
"scripts": {
"postinstall": "npm run build",
"build": "polybuild --maximum-crush elements/elements.html"
},
"devDependencies": {
"polybuild": "^1.0.5"
}
}
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