polymer-rails 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -3
  3. data/app/assets/javascripts/polymer/polymer-micro.html +36 -0
  4. data/app/assets/javascripts/polymer/polymer-mini.html +45 -0
  5. data/app/assets/javascripts/polymer/polymer.html +59 -11
  6. data/app/assets/javascripts/polymer/src/lib/annotations/annotations.html +262 -0
  7. data/app/assets/javascripts/polymer/src/lib/annotations/demo/app-chrome.html +60 -0
  8. data/app/assets/javascripts/polymer/src/lib/array-observe.html +118 -0
  9. data/app/assets/javascripts/polymer/src/lib/array-splice.html +262 -0
  10. data/app/assets/javascripts/polymer/src/lib/async.html +68 -0
  11. data/app/assets/javascripts/polymer/src/lib/base.html +117 -0
  12. data/app/assets/javascripts/polymer/src/lib/bind/accessors.html +223 -0
  13. data/app/assets/javascripts/polymer/src/lib/bind/demo/app-chrome.html +28 -0
  14. data/app/assets/javascripts/polymer/src/lib/bind/demo/app.html +29 -0
  15. data/app/assets/javascripts/polymer/src/lib/bind/demo/src/annotations-bind-demo.html +76 -0
  16. data/app/assets/javascripts/polymer/src/lib/bind/demo/src/bind-demo.html +83 -0
  17. data/app/assets/javascripts/polymer/src/lib/bind/effects.html +80 -0
  18. data/app/assets/javascripts/polymer/src/lib/case-map.html +46 -0
  19. data/app/assets/javascripts/polymer/src/lib/collection.html +179 -0
  20. data/app/assets/javascripts/polymer/src/lib/css-parse.html +131 -0
  21. data/app/assets/javascripts/polymer/src/lib/debounce.html +69 -0
  22. data/app/assets/javascripts/polymer/src/lib/dom-api.html +467 -0
  23. data/app/assets/javascripts/polymer/src/lib/dom-module.html +68 -0
  24. data/app/assets/javascripts/polymer/src/lib/event-api.html +92 -0
  25. data/app/assets/javascripts/polymer/src/lib/expr/focus.html +22 -0
  26. data/app/assets/javascripts/polymer/src/lib/expr/gestures.html +1 -0
  27. data/app/assets/javascripts/polymer/src/lib/expr/log.html +21 -0
  28. data/app/assets/javascripts/polymer/src/lib/expr/sinspect.html +235 -0
  29. data/app/assets/javascripts/polymer/src/lib/expr/style-auditor.html +123 -0
  30. data/app/assets/javascripts/polymer/src/lib/expr/style-protector.html +52 -0
  31. data/app/assets/javascripts/polymer/src/lib/gestures.html +284 -0
  32. data/app/assets/javascripts/polymer/src/lib/lang.html +21 -0
  33. data/app/assets/javascripts/polymer/src/lib/module.html +56 -0
  34. data/app/assets/javascripts/polymer/src/lib/polymer-bootstrap.html +78 -0
  35. data/app/assets/javascripts/polymer/src/lib/resolve-url.html +82 -0
  36. data/app/assets/javascripts/polymer/src/lib/settings.html +52 -0
  37. data/app/assets/javascripts/polymer/src/lib/style-defaults.html +32 -0
  38. data/app/assets/javascripts/polymer/src/lib/style-transformer.html +185 -0
  39. data/app/assets/javascripts/polymer/src/lib/style-util.html +77 -0
  40. data/app/assets/javascripts/polymer/src/lib/template/templatizer.html +132 -0
  41. data/app/assets/javascripts/polymer/src/lib/template/x-array-selector.html +178 -0
  42. data/app/assets/javascripts/polymer/src/lib/template/x-autobind.html +80 -0
  43. data/app/assets/javascripts/polymer/src/lib/template/x-if.html +115 -0
  44. data/app/assets/javascripts/polymer/src/lib/template/x-repeat.html +510 -0
  45. data/app/assets/javascripts/polymer/src/lib/template/x-template.html +39 -0
  46. data/app/assets/javascripts/polymer/src/lib/x-style.html +115 -0
  47. data/app/assets/javascripts/polymer/src/micro/attributes.html +180 -0
  48. data/app/assets/javascripts/polymer/src/micro/constructor.html +74 -0
  49. data/app/assets/javascripts/polymer/src/micro/extends.html +79 -0
  50. data/app/assets/javascripts/polymer/src/micro/mixins.html +40 -0
  51. data/app/assets/javascripts/polymer/src/micro/properties.html +96 -0
  52. data/app/assets/javascripts/polymer/src/micro/tag.html +28 -0
  53. data/app/assets/javascripts/polymer/src/mini/ready.html +180 -0
  54. data/app/assets/javascripts/polymer/src/mini/shadow.html +41 -0
  55. data/app/assets/javascripts/polymer/src/mini/shady.html +365 -0
  56. data/app/assets/javascripts/polymer/src/mini/template.html +56 -0
  57. data/app/assets/javascripts/polymer/src/polymer-lib.html +15 -0
  58. data/app/assets/javascripts/polymer/src/standard/annotations.html +198 -0
  59. data/app/assets/javascripts/polymer/src/standard/configure.html +160 -0
  60. data/app/assets/javascripts/polymer/src/standard/effects.html +215 -0
  61. data/app/assets/javascripts/polymer/src/standard/events.html +127 -0
  62. data/app/assets/javascripts/polymer/src/standard/notify-path.html +260 -0
  63. data/app/assets/javascripts/polymer/src/standard/resolveUrl.html +27 -0
  64. data/app/assets/javascripts/polymer/src/standard/styling.html +157 -0
  65. data/app/assets/javascripts/polymer/src/standard/utils.html +158 -0
  66. data/app/assets/javascripts/polymer/src/standard/x-styling.html +300 -0
  67. data/app/assets/javascripts/webcomponentsjs/webcomponents-lite.js +2270 -0
  68. data/lib/generators/polymer/component/templates/component.html.erb +3 -3
  69. data/lib/generators/polymer/install/install_generator.rb +1 -1
  70. data/lib/polymer-rails/version.rb +1 -1
  71. metadata +80 -5
  72. data/app/assets/javascripts/polymer/layout.html +0 -286
  73. data/app/assets/javascripts/polymer/polymer.js +0 -14
  74. data/app/assets/javascripts/polymer/webcomponents.js +0 -14
@@ -0,0 +1,178 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ Code distributed by Google as part of the polymer project is also
8
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ -->
10
+
11
+ <!--
12
+ Keeping structured data in sync requires that Polymer understand the path
13
+ associations of data being bound. The `x-array-selector` element ensures path
14
+ linkage when selecting specific items from an array (either single or multiple).
15
+ The `items` property accepts an array of user data, and via the `select(item)`
16
+ and `deselect(item)` API, updates the `selected` property which may be bound to
17
+ other parts of the application, and any changes to sub-fields of `selected`
18
+ item(s) will be kept in sync with items in the `items` array. When `multi`
19
+ is false, `selected` is a property representing the last selected item. When
20
+ `multi` is true, `selected` is an array of multiply selected items.
21
+
22
+ ```html
23
+ <dom-module id="employee-list">
24
+
25
+ <template>
26
+
27
+ <div> Employee list: </div>
28
+ <template is="x-repeat" id="employeeList" items="{{employees}}">
29
+ <div>First name: <span>{{item.first}}</span></div>
30
+ <div>Last name: <span>{{item.last}}</span></div>
31
+ <button on-click="toggleSelection">Select</button>
32
+ </template>
33
+
34
+ <x-array-selector id="selector" items="{{employees}}" selected="{{selected}}" multi toggle></x-array-selector>
35
+
36
+ <div> Selected employees: </div>
37
+ <template is="x-repeat" items="{{selected}}">
38
+ <div>First name: <span>{{item.first}}</span></div>
39
+ <div>Last name: <span>{{item.last}}</span></div>
40
+ </template>
41
+
42
+ </template>
43
+
44
+ <script>
45
+ Polymer({
46
+ is: 'employee-list',
47
+ ready: function() {
48
+ this.employees = [
49
+ {first: 'Bob', last: 'Smith'},
50
+ {first: 'Sally', last: 'Johnson'},
51
+ ...
52
+ ];
53
+ },
54
+ toggleSelection: function(e) {
55
+ var item = this.$.employeeList.itemForElement(e.target);
56
+ this.$.selector.select(item);
57
+ }
58
+ });
59
+ </script>
60
+
61
+ </dom-module>
62
+ ```
63
+ -->
64
+
65
+ <script>
66
+
67
+ Polymer({
68
+ is: 'x-array-selector',
69
+
70
+ properties: {
71
+
72
+ /**
73
+ * An array containing items from which selection will be made.
74
+ */
75
+ items: {
76
+ type: Array,
77
+ observer: '_itemsChanged'
78
+ },
79
+
80
+ /**
81
+ * When `multi` is true, this is an array that contains any selected.
82
+ * When `multi` is false, this is the currently selected item, or `null`
83
+ * if no item is selected.
84
+ */
85
+ selected: {
86
+ type: Object,
87
+ notify: true
88
+ },
89
+
90
+ /**
91
+ * When `true`, calling `select` on an item that is already selected
92
+ * will deselect the item.
93
+ */
94
+ toggle: Boolean,
95
+
96
+ /**
97
+ * When `true`, multiple items may be selected at once (in this case,
98
+ * `selected` is an array of currently selected items). When `false`,
99
+ * only one item may be selected at a time.
100
+ */
101
+ multi: Boolean
102
+ },
103
+
104
+ _itemsChanged: function() {
105
+ // Unbind previous selection
106
+ if (Array.isArray(this.selected)) {
107
+ for (var i=0; i<this.selected.length; i++) {
108
+ this.unbindPaths('selected.' + i);
109
+ }
110
+ } else {
111
+ this.unbindPaths('selected');
112
+ }
113
+ // Initialize selection
114
+ if (this.multi) {
115
+ this.selected = [];
116
+ } else {
117
+ this.selected = null;
118
+ }
119
+ },
120
+
121
+ /**
122
+ * Deselects the given item if it is already selected.
123
+ */
124
+ deselect: function(item) {
125
+ if (this.multi) {
126
+ var scol = Polymer.Collection.get(this.selected);
127
+ // var skey = scol.getKey(item);
128
+ // if (skey >= 0) {
129
+ var sidx = this.selected.indexOf(item);
130
+ if (sidx >= 0) {
131
+ var skey = scol.getKey(item);
132
+ this.selected.splice(sidx, 1);
133
+ // scol.remove(item);
134
+ this.unbindPaths('selected.' + skey);
135
+ return true;
136
+ }
137
+ } else {
138
+ this.selected = null;
139
+ this.unbindPaths('selected');
140
+ }
141
+ },
142
+
143
+ /**
144
+ * Selects the given item. When `toggle` is true, this will automatically
145
+ * deselect the item if already selected.
146
+ */
147
+ select: function(item) {
148
+ var icol = Polymer.Collection.get(this.items);
149
+ var key = icol.getKey(item);
150
+ if (this.multi) {
151
+ // var sidx = this.selected.indexOf(item);
152
+ // if (sidx < 0) {
153
+ var scol = Polymer.Collection.get(this.selected);
154
+ var skey = scol.getKey(item);
155
+ if (skey >= 0) {
156
+ this.deselect(item);
157
+ } else if (this.toggle) {
158
+ this.selected.push(item);
159
+ // this.bindPaths('selected.' + sidx, 'items.' + skey);
160
+ // skey = Polymer.Collection.get(this.selected).add(item);
161
+ this.async(function() {
162
+ skey = scol.getKey(item);
163
+ this.bindPaths('selected.' + skey, 'items.' + key);
164
+ });
165
+ }
166
+ } else {
167
+ if (this.toggle && item == this.selected) {
168
+ this.deselect();
169
+ } else {
170
+ this.bindPaths('selected', 'items.' + key);
171
+ this.selected = item;
172
+ }
173
+ }
174
+ }
175
+
176
+ });
177
+
178
+ </script>
@@ -0,0 +1,80 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ Code distributed by Google as part of the polymer project is also
8
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ -->
10
+
11
+ <!--
12
+
13
+ **THIS ELEMENT IS EXPERIMENTAL. API AND NAME SUBJECT TO CHANGE.**
14
+
15
+ Polymer's binding features are only available within templates that are managed
16
+ by Polymer. As such, these features are available in templates used to define
17
+ Polymer elements, for example, but not for elements placed directly in the main
18
+ document.
19
+
20
+ In order to use Polymer bindings without defining a new custom element, elements
21
+ utilizing bindings may be wrapped with the `x-autobind` template extension.
22
+ This template will immediately stamp itself into the main document and bind
23
+ elements to the template itself as the binding scope.
24
+
25
+ ```html
26
+ <!doctype html>
27
+ <html>
28
+ <head>
29
+ <meta charset="utf-8">
30
+ <script src="components/webcomponentsjs/webcomponents-lite.js"></script>
31
+ <link rel="import" href="components/polymer/polymer.html">
32
+ <link rel="import" href="components/core-ajax/core-ajax.html">
33
+
34
+ </head>
35
+ <body>
36
+
37
+ <template is="x-autobind">
38
+
39
+ <core-ajax url="http://..." lastresponse="{{data}}"></core-ajax>
40
+
41
+ <template is="x-repeat" items="{{data}}">
42
+ <div><span>{{item.first}}</span> <span>{{item.last}}</span></div>
43
+ </template>
44
+
45
+ </template>
46
+
47
+ </body>
48
+ </html>
49
+ ```
50
+
51
+ -->
52
+
53
+ <script>
54
+
55
+ Polymer({
56
+
57
+ is: 'x-autobind',
58
+
59
+ extends: 'template',
60
+
61
+ registerFeatures: function() {
62
+ this._prepExtends();
63
+ this._prepConstructor();
64
+ },
65
+
66
+ _finishDistribute: function() {
67
+ var parentDom = Polymer.dom(Polymer.dom(this).parentNode);
68
+ parentDom.insertBefore(this.root, this);
69
+ },
70
+
71
+ initFeatures: function() {
72
+ this._template = this;
73
+ this._prepAnnotations();
74
+ this._prepEffects();
75
+ Polymer.Base.initFeatures.call(this);
76
+ }
77
+
78
+ });
79
+
80
+ </script>
@@ -0,0 +1,115 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ Code distributed by Google as part of the polymer project is also
8
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ -->
10
+
11
+ <link rel="import" href="templatizer.html">
12
+
13
+ <script>
14
+
15
+ /**
16
+ * Stamps the template iff the `if` property is truthy.
17
+ *
18
+ * When `if` becomes falsey, the stamped content is hidden but not
19
+ * removed from dom. When `if` subsequently becomes truthy again, the content
20
+ * is simply re-shown. This approach is used due to its favorable performance
21
+ * characteristics: the expense of creating template content is paid only
22
+ * once and lazily.
23
+ *
24
+ * Set the `restamp` property to true to force the stamped content to be
25
+ * created / destroyed when the `if` condition changes.
26
+ */
27
+ Polymer({
28
+
29
+ is: 'x-if',
30
+ extends: 'template',
31
+
32
+ properties: {
33
+
34
+ 'if': {
35
+ type: Boolean
36
+ },
37
+
38
+ item: {
39
+ type: Object
40
+ },
41
+
42
+ restamp: {
43
+ type: Boolean
44
+ }
45
+
46
+ },
47
+
48
+ mixins: [
49
+ Polymer.Templatizer
50
+ ],
51
+
52
+ observers: {
53
+ 'if item restamp': 'render'
54
+ },
55
+
56
+ render: function() {
57
+ if (!this.ctor) {
58
+ this._wrapTextNodes(this._content);
59
+ this.templatize(this);
60
+ }
61
+ if (this.if) {
62
+ this._ensureInstance();
63
+ } else if (this.restamp) {
64
+ this._teardownInstance();
65
+ }
66
+ if (!this.restamp && this._instance) {
67
+ this._showHideInstance(this.if);
68
+ }
69
+ },
70
+
71
+ _ensureInstance: function() {
72
+ if (!this._instance) {
73
+ // TODO(sorvell): pickup stamping logic from x-repeat
74
+ this._instance = this.stamp(this.item);
75
+ var root = this._instance.root;
76
+ this._instance._children = Array.prototype.slice.call(root.childNodes);
77
+ // TODO(sorvell): this incantation needs to be simpler.
78
+ var parent = Polymer.dom(Polymer.dom(this).parentNode);
79
+ parent.insertBefore(root, this);
80
+ }
81
+ },
82
+
83
+ _teardownInstance: function() {
84
+ if (this._instance) {
85
+ var parent = Polymer.dom(Polymer.dom(this).parentNode);
86
+ this._instance._children.forEach(function(n) {
87
+ parent.removeChild(n);
88
+ });
89
+ this._instance = null;
90
+ }
91
+ },
92
+
93
+ _wrapTextNodes: function(root) {
94
+ // wrap text nodes in span so they can be hidden.
95
+ for (var n = root.firstChild; n; n=n.nextSibling) {
96
+ if (n.nodeType === Node.TEXT_NODE) {
97
+ var s = document.createElement('span');
98
+ root.insertBefore(s, n);
99
+ s.appendChild(n);
100
+ n = s;
101
+ }
102
+ }
103
+ },
104
+
105
+ _showHideInstance: function(showing) {
106
+ this._instance._children.forEach(function(n) {
107
+ if (n.setAttribute) {
108
+ this.serializeValueToAttribute(!showing, 'hidden', n);
109
+ }
110
+ }, this);
111
+ }
112
+
113
+ });
114
+
115
+ </script>
@@ -0,0 +1,510 @@
1
+ <!--
2
+ @license
3
+ Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
4
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ Code distributed by Google as part of the polymer project is also
8
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ -->
10
+
11
+ <!--
12
+
13
+ **THIS ELEMENT IS EXPERIMENTAL. API AND NAME SUBJECT TO CHANGE.**
14
+
15
+ The `x-repeat` element is a custom `HTMLTemplateElement` type extension that
16
+ automatically stamps and binds one instance of template content to each object
17
+ in a user-provided array. `x-repeat` accepts an `items` property, and one
18
+ instance of the template is stamped for each item into the DOM at the location
19
+ of the `x-repeat` element. The `item` property will be set on each instance's
20
+ binding scope, thus templates should bind to sub-properties of `item`.
21
+
22
+ Example:
23
+
24
+ ```html
25
+ <dom-module id="employee-list">
26
+
27
+ <template>
28
+
29
+ <div> Employee list: </div>
30
+ <template is="x-repeat" items="{{employees}}">
31
+ <div>First name: <span>{{item.first}}</span></div>
32
+ <div>Last name: <span>{{item.last}}</span></div>
33
+ </template>
34
+
35
+ </template>
36
+
37
+ <script>
38
+ Polymer({
39
+ is: 'employee-list',
40
+ ready: function() {
41
+ this.employees = [
42
+ {first: 'Bob', last: 'Smith'},
43
+ {first: 'Sally', last: 'Johnson'},
44
+ ...
45
+ ];
46
+ }
47
+ });
48
+ </script>
49
+
50
+ </dom-module>
51
+ ```
52
+
53
+ Notifications for changes to items sub-properties will be forwarded to template
54
+ instances, which will update via the normal structured data notification system.
55
+
56
+ Mutations to the `items` array itself (`push`, `pop`, `splice`, `shift`,
57
+ `unshift`) are observed via `Array.observe` (where supported, or an
58
+ shim of this API on unsupported browsers), and template instances are kept in
59
+ sync with the data in the array.
60
+
61
+ A view-specific filter/sort may be applied to each `x-repeat` by supplying a
62
+ `filter` and/or `sort` property. This may be a string that names a function on
63
+ the host, or a function may be assigned to the property directly. The functions
64
+ should implemented following the standard `Array` filter/sort API.
65
+
66
+ In order to re-run the filter or sort functions based on changes to sub-fields
67
+ of `items`, the `observe` property may be set as a space-separated list of
68
+ `item` sub-fields that should cause a re-filter/sort when modified.
69
+
70
+ For example, for an `x-repeat` with a filter of the following:
71
+
72
+ ```js
73
+ isEngineer: function(item) {
74
+ return item.type == 'engineer' || item.manager.type == 'engineer';
75
+ }
76
+ ```
77
+
78
+ Then the `observe` property should be configured as follows:
79
+
80
+ ```html
81
+ <template is="x-repeat" items="{{employees}}"
82
+ filter="isEngineer" observe="type manager.type">
83
+ ```
84
+
85
+ -->
86
+
87
+ <link rel="import" href="templatizer.html">
88
+ <link rel="import" href="../array-observe.html">
89
+ <link rel="import" href="../collection.html">
90
+
91
+ <script>
92
+
93
+ Polymer({
94
+
95
+ is: 'x-repeat',
96
+ extends: 'template',
97
+
98
+ properties: {
99
+
100
+ /**
101
+ * An array containing items determining how many instances of the template
102
+ * to stamp and that that each template instance should bind to.
103
+ */
104
+ items: {
105
+ type: Array
106
+ },
107
+
108
+ /**
109
+ * A function that should determine the sort order of the items. This
110
+ * property should either be provided as a string, indicating a method
111
+ * name on the element's host, or else be an actual function. The
112
+ * function should match the sort function passed to `Array.sort`.
113
+ * Using a sort function has no effect on the underlying `items` array.
114
+ */
115
+ sort: {
116
+ type: Function,
117
+ observer: '_sortChanged'
118
+ },
119
+
120
+ /**
121
+ * A function that can be used to filter items out of the view. This
122
+ * property should either be provided as a string, indicating a method
123
+ * name on the element's host, or else be an actual function. The
124
+ * function should match the sort function passed to `Array.filter`.
125
+ * Using a filter function has no effect on the underlying `items` array.
126
+ */
127
+ filter: {
128
+ type: Function,
129
+ observer: '_filterChanged'
130
+ },
131
+
132
+ /**
133
+ * When using a `filter` or `sort` function, the `observe` property
134
+ * should be set to a space-separated list of the names of item
135
+ * sub-fields that should trigger a re-sort or re-filter when changed.
136
+ * These should generally be fields of `item` that the sort or filter
137
+ * function depends on.
138
+ */
139
+ observe: {
140
+ type: String,
141
+ observer: '_observeChanged'
142
+ },
143
+
144
+ /**
145
+ * When using a `filter` or `sort` function, the `delay` property
146
+ * determines a debounce time after a change to observed item
147
+ * properties that must pass before the filter or sort is re-run.
148
+ * This is useful in rate-limiting shuffing of the view when
149
+ * item changes may be frequent.
150
+ */
151
+ delay: Number
152
+ },
153
+
154
+ mixins: [
155
+ Polymer.Templatizer
156
+ ],
157
+
158
+ observers: {
159
+ 'items.*': '_itemsChanged'
160
+ },
161
+
162
+ created: function() {
163
+ this.boundCollectionObserver = this.render.bind(this);
164
+ },
165
+
166
+ ready: function() {
167
+ // Templatizing (generating the instance constructor) needs to wait
168
+ // until attached, since it may not have its template content handed
169
+ // back to it until then, following its host template stamping
170
+ if (!this.ctor) {
171
+ this.templatize(this);
172
+ }
173
+ if (this._renderPendingAttach) {
174
+ this._renderPendingAttach = false;
175
+ this.render();
176
+ }
177
+ },
178
+
179
+ _sortChanged: function() {
180
+ this._sortFn = this.sort && (typeof this.sort == 'function' ?
181
+ this.sort : this.host[this.sort].bind(this.host));
182
+ this.debounce('render', this.render);
183
+ },
184
+
185
+ _filterChanged: function() {
186
+ this._filterFn = this.filter && (typeof this.filter == 'function' ?
187
+ this.filter : this.host[this.filter].bind(this.host));
188
+ this.debounce('render', this.render);
189
+ },
190
+
191
+ _observeChanged: function() {
192
+ this._observePaths = this.observe &&
193
+ this.observe.replace('.*', '.').split(' ');
194
+ },
195
+
196
+ _itemsChanged: function(items, old, path) {
197
+ if (path) {
198
+ this._notifyElement(path, items);
199
+ this._checkObservedPaths(path);
200
+ } else {
201
+ if (old) {
202
+ this._unobserveCollection(old);
203
+ }
204
+ if (items) {
205
+ this._observeCollection(items);
206
+ this.debounce('render', this.render);
207
+ }
208
+ }
209
+ },
210
+
211
+ _checkObservedPaths: function(path) {
212
+ if (this._observePaths && path.indexOf('items.') === 0) {
213
+ path = path.substring(path.indexOf('.', 6) + 1);
214
+ var paths = this._observePaths;
215
+ for (var i=0; i<paths.length; i++) {
216
+ if (path.indexOf(paths[i]) === 0) {
217
+ this.debounce('render', this.render, this.delay);
218
+ return;
219
+ }
220
+ }
221
+ }
222
+ },
223
+
224
+ _observeCollection: function(items) {
225
+ this.collection = Array.isArray(items) ? Polymer.Collection.get(items) : items;
226
+ this.collection.observe(this.boundCollectionObserver);
227
+ },
228
+
229
+ _unobserveCollection: function(items) {
230
+ var collection = Polymer.Collection.get(items);
231
+ collection.unobserve(this.boundCollectionObserver);
232
+ },
233
+
234
+ render: function(splices) {
235
+ // TODO(kschaaf): should actually queue splices also
236
+ if (!this.isAttached) {
237
+ // Render must follow attachment
238
+ this._renderPendingAttach = true;
239
+ return;
240
+ }
241
+ this._render(splices);
242
+ },
243
+
244
+ _render: function(splices) {
245
+ var c = this.collection;
246
+ if (splices) {
247
+ if (this._sortFn || splices[0].index == null) {
248
+ this._applySplicesViewSort(splices);
249
+ } else {
250
+ this._applySplicesArraySort(splices);
251
+ }
252
+ } else {
253
+ this._sortAndFilter();
254
+ }
255
+ var rowForKey = this._rowForKey = {};
256
+ var keys = this._orderedKeys;
257
+ // Assign items and keys
258
+ this.rows = this.rows || [];
259
+ for (var i=0; i<keys.length; i++) {
260
+ var key = keys[i];
261
+ var item = c.getItem(key);
262
+ var row = this.rows[i];
263
+ if (!row) {
264
+ this.rows.push(row = this._insertRow(i, null, item));
265
+ }
266
+ row.item = item;
267
+ row.key = key;
268
+ rowForKey[key] = i;
269
+ }
270
+ // Remove extra
271
+ for (; i<this.rows.length; i++) {
272
+ this._detachRow(i);
273
+ }
274
+ this.rows.splice(keys.length, this.rows.length-keys.length);
275
+ },
276
+
277
+ _sortAndFilter: function() {
278
+ var c = this.collection;
279
+ this._orderedKeys = c.getKeys();
280
+ // Filter
281
+ if (this._filterFn) {
282
+ this._orderedKeys = this._orderedKeys.filter(function(a) {
283
+ return this._filterFn(c.getItem(a));
284
+ }, this);
285
+ }
286
+ // Sort
287
+ if (this._sortFn) {
288
+ this._orderedKeys.sort(function(a, b) {
289
+ return this._sortFn(c.getItem(a), c.getItem(b));
290
+ }.bind(this));
291
+ }
292
+ },
293
+
294
+ _keySort: function(a, b) {
295
+ return this.collection.getKey(a) - this.collection.getKey(b);
296
+ },
297
+
298
+ _applySplicesViewSort: function(splices) {
299
+ var c = this.collection;
300
+ var keys = this._orderedKeys;
301
+ var rows = this.rows;
302
+ var removedRows = [];
303
+ var addedKeys = [];
304
+ var pool = [];
305
+ var sortFn = this._sortFn || this._keySort.bind(this);
306
+ splices.forEach(function(s) {
307
+ // Collect all removed row idx's
308
+ for (var i=0; i<s.removed.length; i++) {
309
+ var idx = this._rowForKey[s.removed[i]];
310
+ if (idx != null) {
311
+ removedRows.push(idx);
312
+ }
313
+ }
314
+ // Collect all added keys
315
+ for (i=0; i<s.added.length; i++) {
316
+ addedKeys.push(s.added[i]);
317
+ }
318
+ }, this);
319
+ if (removedRows.length) {
320
+ // Sort removed rows idx's
321
+ removedRows.sort();
322
+ // Remove keys and pool rows (backwards, so we don't invalidate rowForKey)
323
+ for (i=removedRows.length-1; i>=0 ; i--) {
324
+ var idx = removedRows[i];
325
+ pool.push(this._detachRow(idx));
326
+ rows.splice(idx, 1);
327
+ keys.splice(idx, 1);
328
+ }
329
+ }
330
+ if (addedKeys.length) {
331
+ // Filter added keys
332
+ if (this._filterFn) {
333
+ addedKeys = addedKeys.filter(function(a) {
334
+ return this._filterFn(c.getItem(a));
335
+ }, this);
336
+ }
337
+ // Sort added keys
338
+ addedKeys.sort(function(a, b) {
339
+ return this.sortFn(c.getItem(a), c.getItem(b));
340
+ }, this);
341
+ // Insert new rows using sort (from pool or newly created)
342
+ var start = 0;
343
+ for (i=0; i<addedKeys.length; i++) {
344
+ start = this._insertRowIntoViewSort(start, addedKeys[i], pool);
345
+ }
346
+ }
347
+ },
348
+
349
+ _insertRowIntoViewSort: function(start, key, pool) {
350
+ var c = this.collection;
351
+ var item = c.getItem(key);
352
+ var end = this.rows.length - 1;
353
+ var idx = -1;
354
+ var sortFn = this._sortFn || this._keySort.bind(this);
355
+ // Binary search for insertion point
356
+ while (start <= end) {
357
+ var mid = (start + end) >> 1;
358
+ var midKey = this._orderedKeys[mid];
359
+ var cmp = sortFn(c.getItem(midKey), item);
360
+ if (cmp < 0) {
361
+ start = mid + 1;
362
+ } else if (cmp > 0) {
363
+ end = mid - 1;
364
+ } else {
365
+ idx = mid;
366
+ break;
367
+ }
368
+ }
369
+ if (idx < 0) {
370
+ idx = end + 1;
371
+ }
372
+ // Insert key & row at insertion point
373
+ this._orderedKeys.splice(idx, 0, key);
374
+ this.rows.splice(idx, 0, this._insertRow(idx, pool));
375
+ return idx;
376
+ },
377
+
378
+ _applySplicesArraySort: function(splices) {
379
+ var keys = this._orderedKeys;
380
+ var pool = [];
381
+ splices.forEach(function(s) {
382
+ // Remove & pool rows first, to ensure we can fully reuse removed rows
383
+ for (var i=0; i<s.removed.length; i++) {
384
+ pool.push(this._detachRow(s.index + i));
385
+ }
386
+ this.rows.splice(s.index, s.removed.length);
387
+ }, this);
388
+ var c = this.collection;
389
+ var filterDelta = 0;
390
+ splices.forEach(function(s) {
391
+ // Filter added keys
392
+ var addedKeys = s.added;
393
+ if (this._filterFn) {
394
+ addedKeys = addedKeys.filter(function(a) {
395
+ return this._filterFn(c.getItem(a));
396
+ }, this);
397
+ filterDelta += (s.added.length - addedKeys.length);
398
+ }
399
+ var idx = s.index - filterDelta;
400
+ // Apply splices to keys
401
+ var args = [idx, s.removed.length].concat(addedKeys);
402
+ keys.splice.apply(keys, args);
403
+ // Insert new rows (from pool or newly created)
404
+ var addedRows = [];
405
+ for (i=0; i<s.added.length; i++) {
406
+ addedRows.push(this._insertRow(idx + i, pool));
407
+ }
408
+ args = [s.index, 0].concat(addedRows);
409
+ this.rows.splice.apply(this.rows, args);
410
+ }, this);
411
+ },
412
+
413
+ _detachRow: function(idx) {
414
+ var row = this.rows[idx];
415
+ var parentNode = Polymer.dom(this).parentNode;
416
+ for (var i=0; i<row._children.length; i++) {
417
+ var el = row._children[i];
418
+ Polymer.dom(row.root).appendChild(el);
419
+ }
420
+ return row;
421
+ },
422
+
423
+ _insertRow: function(idx, pool, item) {
424
+ var row = (pool && pool.pop()) || this._generateRow(idx, item);
425
+ var beforeRow = this.rows[idx];
426
+ var beforeNode = beforeRow ? beforeRow._children[0] : this;
427
+ var parentNode = Polymer.dom(this).parentNode;
428
+ row.root.__styleScoped = true;
429
+ Polymer.dom(parentNode).insertBefore(row.root, beforeNode);
430
+ return row;
431
+ },
432
+
433
+ _generateRow: function(idx, item) {
434
+ var row = this.stamp({
435
+ item: item,
436
+ pathDelegate: this
437
+ });
438
+ // each row is a document fragment which is lost when we appendChild,
439
+ // so we have to track each child individually
440
+ var children = [];
441
+ for (var n = row.root.firstChild; n; n=n.nextSibling) {
442
+ children.push(n);
443
+ n._templateInstance = row;
444
+ }
445
+ // Since archetype overrides Base/HTMLElement, Safari complains
446
+ // when accessing `children`
447
+ row._children = children;
448
+ return row;
449
+ },
450
+
451
+ _notifyDelegatePath: function(row, path, value) {
452
+ this.notifyPath(path.replace('item', 'items.' + row.key), value);
453
+ },
454
+
455
+ _notifyElement: function(path, value) {
456
+ if (this._rowForKey) {
457
+ // 'items.'.length == 6
458
+ var dot = path.indexOf('.', 6);
459
+ var key = path.substring(6, dot < 0 ? path.length : dot);
460
+ var idx = this._rowForKey[key];
461
+ var row = this.rows[idx];
462
+ if (row) {
463
+ if (dot >= 0) {
464
+ path = 'item.' + path.substring(dot+1);
465
+ row.notifyPath(path, value, true);
466
+ } else {
467
+ row.item = value;
468
+ }
469
+ }
470
+ }
471
+ },
472
+
473
+ _instanceForElement: function(el) {
474
+ while (el && !el._templateInstance) {
475
+ el = el.parentNode;
476
+ }
477
+ return el._templateInstance;
478
+ },
479
+
480
+ /**
481
+ * Returns the item associated with a given element stamped by
482
+ * this `x-repeat`.
483
+ */
484
+ itemForElement: function(el) {
485
+ var instance = this._instanceForElement(el);
486
+ return instance && instance.item;
487
+ },
488
+
489
+ /**
490
+ * Returns the `Polymer.Collection` key associated with a given
491
+ * element stamped by this `x-repeat`.
492
+ */
493
+ keyForElement: function(el) {
494
+ var instance = this._instanceForElement(el);
495
+ return instance && instance.key;
496
+ },
497
+
498
+ /**
499
+ * Returns the index in `items` associated with a given element
500
+ * stamped by this `x-repeat`.
501
+ */
502
+ indexForElement: function(el) {
503
+ var instance = this._instanceForElement(el);
504
+ return this.rows.indexOf(instance);
505
+ }
506
+
507
+ });
508
+
509
+
510
+ </script>