-
Notifications
You must be signed in to change notification settings - Fork 2k
/
configure.html
262 lines (236 loc) · 10 KB
/
configure.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
<!--
@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
-->
<script>
(function() {
/*
Process inputs efficiently via a configure lifecycle callback.
Configure is called top-down, host before local dom. Users should
implement configure to supply a set of default values for the element by
returning an object containing the properties and values to set.
Configured values are not immediately set, instead they are set when
an element becomes ready, after its local dom is ready. This ensures
that any user change handlers are not called before ready time.
*/
/*
Implementation notes:
Configured values are collected into _config. At ready time, properties
are set to the values in _config. This ensures properties are set child
before host and change handlers are called only at ready time. The host
will reset a value already propagated to a child, but this is not
inefficient because of dirty checking at the set point.
Bind notification events are sent when properties are set at ready time
and thus received by the host before it is ready. Since notifications result
in property updates and this triggers side effects, handling notifications
is deferred until ready time.
In general, events can be heard before an element is ready. This may occur
when a user sends an event in a change handler or listens to a data event
directly (on-foo-changed).
*/
var usePolyfillProto = Polymer.Settings.usePolyfillProto;
// When true, `this.properties` is bad juju due to obsolete `properties`
// accessors on instances of HTMLElement
var avoidInstanceProperties =
Boolean(Object.getOwnPropertyDescriptor(document.documentElement, 'properties'));
Polymer.Base._addFeature({
// storage for configuration
_setupConfigure: function(initialConfig) {
this._config = {};
this._handlers = [];
this._aboveConfig = null;
if (initialConfig) {
// don't accept undefined values in intialConfig
for (var i in initialConfig) {
if (initialConfig[i] !== undefined) {
this._config[i] = initialConfig[i];
}
}
}
},
// static attributes are deserialized into _config
_marshalAttributes: function() {
this._takeAttributesToModel(this._config);
},
_attributeChangedImpl: function(name) {
var model = this._clientsReadied ? this : this._config;
this._setAttributeToProperty(model, name);
},
// at configure time values are stored in _config
_configValue: function(name, value) {
var info = this._propertyInfo[name];
if (!info || !info.readOnly) {
this._config[name] = value;
}
},
// Override polymer-mini thunk
_beforeClientsReady: function() {
this._configure();
},
// configure: returns user supplied default property values
// combines with _config to create final property values
_configure: function() {
// some annotation data needs to be handed from host to client
// e.g. hand template content stored in notes to children as part of
// configure flow so templates have their content at ready time
this._configureAnnotationReferences();
// configure instance properties that may have been bound prior to upgrade
this._configureInstanceProperties();
// save copy of configuration that came from above
this._aboveConfig = this.mixin({}, this._config);
// get individual default values from property configs
var config = {};
// mixed-in behaviors
for (var i=0; i < this.behaviors.length; i++) {
this._configureProperties(this.behaviors[i].properties, config);
}
// prototypical behavior
// Read `properties` off of the prototype, as a concession to non-spec
// compliant browsers (e.g. Android UC Browser 11.2.0.915) where
// a.) HTMLElement's have a non-spec `properties` property, and
// b.) the `properties` accessor is on instances rather than
// `HTMLElement.prototype`; going under the instance to the prototype
// avoids the problem. Note can't always go to __proto__ due to IE10
// hence conditional, but IE10 doesn't suffer from the instance properties
// issue (happy coincidence of browser quirks).
this._configureProperties(avoidInstanceProperties ?
this.__proto__.properties : this.properties, config);
// TODO(sorvell): it *may* be faster to loop over _propertyInfo but
// there are some test issues.
//this._configureProperties(this._propertyInfo, config);
// override local configuration with configuration from above
this.mixin(config, this._aboveConfig);
// this is the new _config, which are the final values to be applied
this._config = config;
// pass configuration data to bindings
if (this._clients && this._clients.length) {
this._distributeConfig(this._config);
}
},
_configureInstanceProperties: function() {
for (var i in this._propertyEffects) {
// Allow properties set before upgrade on the instance
// to override default values. This allows late upgrade + an early set
// to not b0rk accessors on the prototype.
// Perf testing has shown `hasOwnProperty` to be ok here.
if (!usePolyfillProto && this.hasOwnProperty(i)) {
this._configValue(i, this[i]);
delete this[i];
}
}
},
_configureProperties: function(properties, config) {
for (var i in properties) {
var c = properties[i];
if (c.value !== undefined) {
var value = c.value;
if (typeof value == 'function') {
// pass existing config values (this._config) to value function
value = value.call(this, this._config);
}
config[i] = value;
}
}
},
// distribute config values to bound nodes.
_distributeConfig: function(config) {
var fx$ = this._propertyEffects;
if (fx$) {
for (var p in config) {
var fx = fx$[p];
if (fx) {
for (var i=0, l=fx.length, x; (i<l) && (x=fx[i]); i++) {
// TODO(kschaaf): computed annotations are excluded from top-down
// configure for now; to be revisited
if (x.kind === 'annotation') {
var node = this._nodes[x.effect.index];
var name = x.effect.propertyName;
// seeding configuration only
var isAttr = (x.effect.kind == 'attribute');
var hasEffect = (node._propertyEffects &&
node._propertyEffects[name]);
if (node._configValue && (hasEffect || !isAttr)) {
var value = (p === x.effect.value) ? config[p] :
this._get(x.effect.value, config);
value = this._computeFinalAnnotationValue(node, name, value,
x.effect);
if (isAttr) {
// For attribute bindings, flow through the same ser/deser
// process to ensure the value is the same as if it were
// bound through the attribute
value = node.deserialize(this.serialize(value),
node._propertyInfo[name].type);
}
node._configValue(name, value);
}
}
}
}
}
}
},
// Override polymer-mini thunk
_afterClientsReady: function() {
// set path properties
this.importPath = this._importPath;
this.rootPath = Polymer.rootPath;
// process static effects, e.g. computations that have only literal arguments
this._executeStaticEffects();
this._applyConfig(this._config, this._aboveConfig);
this._flushHandlers();
},
// NOTE: values are already propagated to children via
// _distributeConfig so propagation triggered by effects here is
// redundant, but safe due to dirty checking
_applyConfig: function(config, aboveConfig) {
for (var n in config) {
// Don't stomp on values that may have been set by other side effects
if (this[n] === undefined) {
// Call _propertySet for any properties with accessors, which will
// initialize read-only properties also; set quietly if value was
// configured from above, as opposed to default
this.__setProperty(n, config[n], n in aboveConfig);
}
}
},
// NOTE: Notifications can be processed before ready since
// they are sent at *child* ready time. Since notifications cause side
// effects and side effects must not be processed before ready time,
// handling is queue/defered until then.
_notifyListener: function(fn, e) {
if (!Polymer.Bind._isEventBogus(e, e.target)) {
var value, path;
if (e.detail) {
value = e.detail.value;
path = e.detail.path;
}
if (!this._clientsReadied) {
this._queueHandler([fn, e.target, value, path]);
} else {
return fn.call(this, e.target, value, path);
}
}
},
_queueHandler: function(args) {
this._handlers.push(args);
},
_flushHandlers: function() {
var h$ = this._handlers;
for (var i=0, l=h$.length, h; (i<l) && (h=h$[i]); i++) {
h[0].call(this, h[1], h[2], h[3]);
}
// reset handlers array
//
// If an element holds a reference to a CustomEvent with a detail
// property, Chrome will leak memory across page refreshes
// https://crbug.com/529941
this._handlers = [];
}
});
})();
</script>