OLD | NEW |
| (Empty) |
1 | |
2 | |
3 Polymer.Templatizer = { | |
4 | |
5 templatize: function(template) { | |
6 this._templatized = template; | |
7 // TODO(sjmiles): supply _alternate_ content reference missing from root | |
8 // templates (not nested). `_content` exists to provide content sharing | |
9 // for nested templates. | |
10 if (!template._content) { | |
11 template._content = template.content; | |
12 } | |
13 // fast path if template's anonymous class has been memoized | |
14 if (template._content._ctor) { | |
15 this.ctor = template._content._ctor; | |
16 //console.log('Templatizer.templatize: using memoized archetype'); | |
17 // forward parent properties to archetype | |
18 this._prepParentProperties(this.ctor.prototype); | |
19 return; | |
20 } | |
21 // `archetype` is the prototype of the anonymous | |
22 // class created by the templatizer | |
23 var archetype = Object.create(Polymer.Base); | |
24 // normally Annotations.parseAnnotations(template) but | |
25 // archetypes do special caching | |
26 this.customPrepAnnotations(archetype, template); | |
27 | |
28 // setup accessors | |
29 archetype._prepEffects(); | |
30 archetype._prepBehaviors(); | |
31 archetype._prepBindings(); | |
32 | |
33 // forward parent properties to archetype | |
34 this._prepParentProperties(archetype); | |
35 | |
36 // boilerplate code | |
37 archetype._notifyPath = this._notifyPathImpl; | |
38 archetype._scopeElementClass = this._scopeElementClassImpl; | |
39 // boilerplate code | |
40 var _constructor = this._constructorImpl; | |
41 var ctor = function TemplateInstance(model, host) { | |
42 _constructor.call(this, model, host); | |
43 }; | |
44 // standard references | |
45 ctor.prototype = archetype; | |
46 archetype.constructor = ctor; | |
47 // TODO(sjmiles): constructor cache? | |
48 template._content._ctor = ctor; | |
49 // TODO(sjmiles): choose less general name | |
50 this.ctor = ctor; | |
51 }, | |
52 | |
53 _getRootDataHost: function() { | |
54 return (this.dataHost && this.dataHost._rootDataHost) || this.dataHost; | |
55 }, | |
56 | |
57 _getAllStampedChildren: function(children) { | |
58 children = children || []; | |
59 if (this._getStampedChildren) { | |
60 var c$ = this._getStampedChildren(); | |
61 for (var i=0, c; c = c$[i]; i++) { | |
62 children.push(c); | |
63 if (c._getAllStampedChildren) { | |
64 c._getAllStampedChildren(children); | |
65 } | |
66 } | |
67 } | |
68 return children; | |
69 }, | |
70 | |
71 customPrepAnnotations: function(archetype, template) { | |
72 if (template) { | |
73 archetype._template = template; | |
74 var c = template._content; | |
75 if (c) { | |
76 var rootDataHost = archetype._rootDataHost; | |
77 if (rootDataHost) { | |
78 Polymer.Annotations.prepElement = | |
79 rootDataHost._prepElement.bind(rootDataHost); | |
80 } | |
81 archetype._notes = c._notes || | |
82 Polymer.Annotations.parseAnnotations(template); | |
83 c._notes = archetype._notes; | |
84 Polymer.Annotations.prepElement = null; | |
85 archetype._parentProps = c._parentProps; | |
86 } | |
87 else { | |
88 console.warn('no _content'); | |
89 } | |
90 } | |
91 else { | |
92 console.warn('no _template'); | |
93 } | |
94 }, | |
95 | |
96 // Sets up accessors on the template to call abstract _forwardParentProp | |
97 // API that should be implemented by Templatizer users to get parent | |
98 // properties to their template instances. These accessors are memoized | |
99 // on the archetype and copied to instances. | |
100 _prepParentProperties: function(archetype) { | |
101 var parentProps = this._parentProps = archetype._parentProps; | |
102 if (this._forwardParentProp && parentProps) { | |
103 // Prototype setup (memoized on archetype) | |
104 var proto = archetype._parentPropProto; | |
105 if (!proto) { | |
106 proto = archetype._parentPropProto = Object.create(null); | |
107 if (this._templatized != this) { | |
108 // Assumption: if `this` isn't the template being templatized, | |
109 // assume that the template is not a Poylmer.Base, so prep it | |
110 // for binding | |
111 Polymer.Bind.prepareModel(proto); | |
112 } | |
113 // Create accessors for each parent prop that forward the property | |
114 // to template instances through abstract _forwardParentProp API | |
115 // that should be implemented by Templatizer users | |
116 for (var prop in parentProps) { | |
117 var parentProp = '_parent_' + prop; | |
118 var effects = [{ | |
119 kind: 'function', | |
120 effect: { function: this._createForwardPropEffector(prop) } | |
121 }]; | |
122 Polymer.Bind._createAccessors(proto, parentProp, effects); | |
123 } | |
124 } | |
125 // Instance setup | |
126 if (this._templatized != this) { | |
127 Polymer.Bind.prepareInstance(this._templatized); | |
128 this._templatized._forwardParentProp = | |
129 this._forwardParentProp.bind(this); | |
130 } | |
131 this._extendTemplate(this._templatized, proto); | |
132 } | |
133 }, | |
134 | |
135 _createForwardPropEffector: function(prop) { | |
136 return function(source, value) { | |
137 this._forwardParentProp(prop, value); | |
138 }; | |
139 }, | |
140 | |
141 // Similar to Polymer.Base.extend, but retains any previously set instance | |
142 // values (_propertySet back on instance once accessor is installed) | |
143 _extendTemplate: function(template, proto) { | |
144 Object.getOwnPropertyNames(proto).forEach(function(n) { | |
145 var val = template[n]; | |
146 var pd = Object.getOwnPropertyDescriptor(proto, n); | |
147 Object.defineProperty(template, n, pd); | |
148 if (val !== undefined) { | |
149 template._propertySet(n, val); | |
150 } | |
151 }); | |
152 }, | |
153 | |
154 _notifyPathImpl: function(path, value) { | |
155 var p = path.match(/([^.]*)\.(([^.]*).*)/); | |
156 // 'root.sub.path' | |
157 var root = p[1]; // 'root' | |
158 var sub = p[3]; // 'sub' | |
159 var subPath = p[2]; // 'sub.path' | |
160 // Notify host of parent.* path/property changes | |
161 var dataHost = this.dataHost; | |
162 if (root == 'parent') { | |
163 if (sub == subPath) { | |
164 dataHost.dataHost[sub] = value; | |
165 } else { | |
166 dataHost.notifyPath('_parent_' + subPath, value); | |
167 } | |
168 } | |
169 // Extension point for Templatizer sub-classes | |
170 if (dataHost._forwardInstancePath) { | |
171 dataHost._forwardInstancePath.call(dataHost, this, root, subPath, value)
; | |
172 } | |
173 }, | |
174 | |
175 // Overrides Base notify-path module | |
176 _pathEffector: function(path, value, fromAbove) { | |
177 if (this._forwardParentPath) { | |
178 if (path.indexOf('_parent_') === 0) { | |
179 this._forwardParentPath(path.substring(8), value); | |
180 } | |
181 } | |
182 Polymer.Base._pathEffector.apply(this, arguments); | |
183 }, | |
184 | |
185 _constructorImpl: function(model, host) { | |
186 var rootDataHost = host._getRootDataHost(); | |
187 if (rootDataHost) { | |
188 this.listen = rootDataHost.listen.bind(rootDataHost); | |
189 this._rootDataHost = rootDataHost; | |
190 } | |
191 this._setupConfigure(model); | |
192 this._pushHost(host); | |
193 this.root = this.instanceTemplate(this._template); | |
194 this.root.__styleScoped = true; | |
195 this._popHost(); | |
196 this._marshalAnnotatedNodes(); | |
197 this._marshalInstanceEffects(); | |
198 this._marshalAnnotatedListeners(); | |
199 this._tryReady(); | |
200 }, | |
201 | |
202 _scopeElementClassImpl: function(node, value) { | |
203 var host = this._rootDataHost; | |
204 if (host) { | |
205 return host._scopeElementClass(node, value); | |
206 } | |
207 }, | |
208 | |
209 stamp: function(model) { | |
210 model = model || {}; | |
211 if (this._parentProps) { | |
212 // TODO(kschaaf): Maybe this is okay | |
213 // model.parent = this.dataHost; | |
214 model.parent = model.parent || {}; | |
215 for (var prop in this._parentProps) { | |
216 model.parent[prop] = this['_parent_' + prop]; | |
217 } | |
218 } | |
219 return new this.ctor(model, this); | |
220 } | |
221 | |
222 // TODO(sorvell): note, using the template as host is ~5-10% faster if | |
223 // elements have no default values. | |
224 // _constructorImpl: function(model, host) { | |
225 // this._setupConfigure(model); | |
226 // host._beginHost(); | |
227 // this.root = this.instanceTemplate(this._template); | |
228 // host._popHost(); | |
229 // this._marshalTemplateContent(); | |
230 // this._marshalAnnotatedNodes(); | |
231 // this._marshalInstanceEffects(); | |
232 // this._marshalAnnotatedListeners(); | |
233 // this._ready(); | |
234 // }, | |
235 | |
236 // stamp: function(model) { | |
237 // return new this.ctor(model, this.dataHost); | |
238 // } | |
239 | |
240 | |
241 }; | |
242 | |
OLD | NEW |