-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathprototype.js
255 lines (228 loc) · 9.27 KB
/
prototype.js
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
/*
* 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
*/
(function(scope) {
// imports
var api = scope.api;
var isBase = scope.isBase;
var extend = scope.extend;
var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
// prototype api
var prototype = {
register: function(name, extendeeName) {
// build prototype combining extendee, Polymer base, and named api
this.buildPrototype(name, extendeeName);
// register our custom element with the platform
this.registerPrototype(name, extendeeName);
// reference constructor in a global named by 'constructor' attribute
this.publishConstructor();
},
buildPrototype: function(name, extendeeName) {
// get our custom prototype (before chaining)
var extension = scope.getRegisteredPrototype(name);
// get basal prototype
var base = this.generateBasePrototype(extendeeName);
// implement declarative features
this.desugarBeforeChaining(extension, base);
// join prototypes
this.prototype = this.chainPrototypes(extension, base);
// more declarative features
this.desugarAfterChaining(name, extendeeName);
},
desugarBeforeChaining: function(prototype, base) {
// back reference declaration element
// TODO(sjmiles): replace `element` with `elementElement` or `declaration`
prototype.element = this;
// transcribe `attributes` declarations onto own prototype's `publish`
this.publishAttributes(prototype, base);
// `publish` properties to the prototype and to attribute watch
this.publishProperties(prototype, base);
// infer observers for `observe` list based on method names
this.inferObservers(prototype);
// desugar compound observer syntax, e.g. 'a b c'
this.explodeObservers(prototype);
},
chainPrototypes: function(prototype, base) {
// chain various meta-data objects to inherited versions
this.inheritMetaData(prototype, base);
// chain custom api to inherited
var chained = this.chainObject(prototype, base);
// x-platform fixup
ensurePrototypeTraversal(chained);
return chained;
},
inheritMetaData: function(prototype, base) {
// chain observe object to inherited
this.inheritObject('observe', prototype, base);
// chain publish object to inherited
this.inheritObject('publish', prototype, base);
// chain reflect object to inherited
this.inheritObject('reflect', prototype, base);
// chain our lower-cased publish map to the inherited version
this.inheritObject('_publishLC', prototype, base);
// chain our instance attributes map to the inherited version
this.inheritObject('_instanceAttributes', prototype, base);
// chain our event delegates map to the inherited version
this.inheritObject('eventDelegates', prototype, base);
},
// implement various declarative features
desugarAfterChaining: function(name, extendee) {
// build side-chained lists to optimize iterations
this.optimizePropertyMaps(this.prototype);
this.createPropertyAccessors(this.prototype);
// install mdv delegate on template
this.installBindingDelegate(this.fetchTemplate());
// install external stylesheets as if they are inline
this.installSheets();
// adjust any paths in dom from imports
this.resolveElementPaths(this);
// compile list of attributes to copy to instances
this.accumulateInstanceAttributes();
// parse on-* delegates declared on `this` element
this.parseHostEvents();
//
// install a helper method this.resolvePath to aid in
// setting resource urls. e.g.
// this.$.image.src = this.resolvePath('images/foo.png')
this.addResolvePathApi();
// under ShadowDOMPolyfill, transforms to approximate missing CSS features
if (hasShadowDOMPolyfill) {
WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
extendee);
}
// allow custom element access to the declarative context
if (this.prototype.registerCallback) {
this.prototype.registerCallback(this);
}
},
// if a named constructor is requested in element, map a reference
// to the constructor to the given symbol
publishConstructor: function() {
var symbol = this.getAttribute('constructor');
if (symbol) {
window[symbol] = this.ctor;
}
},
// build prototype combining extendee, Polymer base, and named api
generateBasePrototype: function(extnds) {
var prototype = this.findBasePrototype(extnds);
if (!prototype) {
// create a prototype based on tag-name extension
var prototype = HTMLElement.getPrototypeForTag(extnds);
// insert base api in inheritance chain (if needed)
prototype = this.ensureBaseApi(prototype);
// memoize this base
memoizedBases[extnds] = prototype;
}
return prototype;
},
findBasePrototype: function(name) {
return memoizedBases[name];
},
// install Polymer instance api into prototype chain, as needed
ensureBaseApi: function(prototype) {
if (prototype.PolymerBase) {
return prototype;
}
var extended = Object.create(prototype);
// we need a unique copy of base api for each base prototype
// therefore we 'extend' here instead of simply chaining
api.publish(api.instance, extended);
// TODO(sjmiles): sharing methods across prototype chains is
// not supported by 'super' implementation which optimizes
// by memoizing prototype relationships.
// Probably we should have a version of 'extend' that is
// share-aware: it could study the text of each function,
// look for usage of 'super', and wrap those functions in
// closures.
// As of now, there is only one problematic method, so
// we just patch it manually.
// To avoid re-entrancy problems, the special super method
// installed is called `mixinSuper` and the mixin method
// must use this method instead of the default `super`.
this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
// return buffed-up prototype
return extended;
},
mixinMethod: function(extended, prototype, api, name) {
var $super = function(args) {
return prototype[name].apply(this, args);
};
extended[name] = function() {
this.mixinSuper = $super;
return api[name].apply(this, arguments);
}
},
// ensure prototype[name] inherits from a prototype.prototype[name]
inheritObject: function(name, prototype, base) {
// require an object
var source = prototype[name] || {};
// chain inherited properties onto a new object
prototype[name] = this.chainObject(source, base[name]);
},
// register 'prototype' to custom element 'name', store constructor
registerPrototype: function(name, extendee) {
var info = {
prototype: this.prototype
}
// native element must be specified in extends
var typeExtension = this.findTypeExtension(extendee);
if (typeExtension) {
info.extends = typeExtension;
}
// register the prototype with HTMLElement for name lookup
HTMLElement.register(name, this.prototype);
// register the custom type
this.ctor = document.registerElement(name, info);
},
findTypeExtension: function(name) {
if (name && name.indexOf('-') < 0) {
return name;
} else {
var p = this.findBasePrototype(name);
if (p.element) {
return this.findTypeExtension(p.element.extends);
}
}
}
};
// memoize base prototypes
var memoizedBases = {};
// implementation of 'chainObject' depends on support for __proto__
if (Object.__proto__) {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
object.__proto__ = inherited;
}
return object;
}
} else {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
var chained = Object.create(inherited);
object = extend(chained, object);
}
return object;
}
}
// On platforms that do not support __proto__ (versions of IE), the prototype
// chain of a custom element is simulated via installation of __proto__.
// Although custom elements manages this, we install it here so it's
// available during desugaring.
function ensurePrototypeTraversal(prototype) {
if (!Object.__proto__) {
var ancestor = Object.getPrototypeOf(prototype);
prototype.__proto__ = ancestor;
if (isBase(ancestor)) {
ancestor.__proto__ = Object.getPrototypeOf(ancestor);
}
}
}
// exports
api.declaration.prototype = prototype;
})(Polymer);