OLD | NEW |
| (Empty) |
1 if ((!HTMLElement.prototype.createShadowRoot && | |
2 !HTMLElement.prototype.webkitCreateShadowRoot) || | |
3 window.__forceShadowDomPolyfill) { | |
4 | |
5 /* | |
6 * Copyright 2013 The Polymer Authors. All rights reserved. | |
7 * Use of this source code is governed by a BSD-style | |
8 * license that can be found in the LICENSE file. | |
9 */ | |
10 (function() { | |
11 // TODO(jmesserly): fix dart:html to use unprefixed name | |
12 if (Element.prototype.webkitCreateShadowRoot) { | |
13 Element.prototype.webkitCreateShadowRoot = function() { | |
14 return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot(); | |
15 }; | |
16 } | |
17 })(); | |
18 | |
19 /* | |
20 * Copyright 2012 The Polymer Authors. All rights reserved. | |
21 * Use of this source code is goverened by a BSD-style | |
22 * license that can be found in the LICENSE file. | |
23 */ | |
24 | |
25 // SideTable is a weak map where possible. If WeakMap is not available the | |
26 // association is stored as an expando property. | |
27 var SideTable; | |
28 // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox | |
29 if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') <
0) { | |
30 SideTable = WeakMap; | |
31 } else { | |
32 (function() { | |
33 var defineProperty = Object.defineProperty; | |
34 var hasOwnProperty = Object.hasOwnProperty; | |
35 var counter = new Date().getTime() % 1e9; | |
36 | |
37 SideTable = function() { | |
38 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | |
39 }; | |
40 | |
41 SideTable.prototype = { | |
42 set: function(key, value) { | |
43 defineProperty(key, this.name, {value: value, writable: true}); | |
44 }, | |
45 get: function(key) { | |
46 return hasOwnProperty.call(key, this.name) ? key[this.name] : undefined; | |
47 }, | |
48 delete: function(key) { | |
49 this.set(key, undefined); | |
50 } | |
51 } | |
52 })(); | |
53 } | |
54 | |
55 // Copyright 2012 The Polymer Authors. All rights reserved. | |
56 // Use of this source code is goverened by a BSD-style | |
57 // license that can be found in the LICENSE file. | |
58 | |
59 var ShadowDOMPolyfill = {}; | |
60 | |
61 (function(scope) { | |
62 'use strict'; | |
63 | |
64 var wrapperTable = new SideTable(); | |
65 var constructorTable = new SideTable(); | |
66 var wrappers = Object.create(null); | |
67 | |
68 function assert(b) { | |
69 if (!b) | |
70 throw new Error('Assertion failed'); | |
71 }; | |
72 | |
73 function mixin(to, from) { | |
74 Object.getOwnPropertyNames(from).forEach(function(name) { | |
75 Object.defineProperty(to, name, | |
76 Object.getOwnPropertyDescriptor(from, name)); | |
77 }); | |
78 return to; | |
79 }; | |
80 | |
81 function mixinStatics(to, from) { | |
82 Object.getOwnPropertyNames(from).forEach(function(name) { | |
83 switch (name) { | |
84 case 'arguments': | |
85 case 'caller': | |
86 case 'length': | |
87 case 'name': | |
88 case 'prototype': | |
89 case 'toString': | |
90 return; | |
91 } | |
92 Object.defineProperty(to, name, | |
93 Object.getOwnPropertyDescriptor(from, name)); | |
94 }); | |
95 return to; | |
96 }; | |
97 | |
98 // Mozilla's old DOM bindings are bretty busted: | |
99 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844 | |
100 // Make sure they are create before we start modifying things. | |
101 Object.getOwnPropertyNames(window); | |
102 | |
103 function getWrapperConstructor(node) { | |
104 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node); | |
105 var wrapperConstructor = constructorTable.get(nativePrototype); | |
106 if (wrapperConstructor) | |
107 return wrapperConstructor; | |
108 | |
109 var parentWrapperConstructor = getWrapperConstructor(nativePrototype); | |
110 | |
111 var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor); | |
112 registerInternal(nativePrototype, GeneratedWrapper, node); | |
113 | |
114 return GeneratedWrapper; | |
115 } | |
116 | |
117 function addForwardingProperties(nativePrototype, wrapperPrototype) { | |
118 installProperty(nativePrototype, wrapperPrototype, true); | |
119 } | |
120 | |
121 function registerInstanceProperties(wrapperPrototype, instanceObject) { | |
122 installProperty(instanceObject, wrapperPrototype, false); | |
123 } | |
124 | |
125 var isFirefox = /Firefox/.test(navigator.userAgent); | |
126 | |
127 // This is used as a fallback when getting the descriptor fails in | |
128 // installProperty. | |
129 var dummyDescriptor = { | |
130 get: function() {}, | |
131 set: function(v) {}, | |
132 configurable: true, | |
133 enumerable: true | |
134 }; | |
135 | |
136 function installProperty(source, target, allowMethod) { | |
137 Object.getOwnPropertyNames(source).forEach(function(name) { | |
138 if (name in target) | |
139 return; | |
140 | |
141 if (isFirefox) { | |
142 // Tickle Firefox's old bindings. | |
143 source.__lookupGetter__(name); | |
144 } | |
145 var descriptor; | |
146 try { | |
147 descriptor = Object.getOwnPropertyDescriptor(source, name); | |
148 } catch (ex) { | |
149 // JSC and V8 both use data properties instead accessors which can cause | |
150 // getting the property desciptor throw an exception. | |
151 // https://bugs.webkit.org/show_bug.cgi?id=49739 | |
152 descriptor = dummyDescriptor; | |
153 } | |
154 var getter, setter; | |
155 if (allowMethod && typeof descriptor.value === 'function') { | |
156 target[name] = function() { | |
157 return this.impl[name].apply(this.impl, arguments); | |
158 }; | |
159 return; | |
160 } | |
161 | |
162 getter = function() { | |
163 return this.impl[name]; | |
164 }; | |
165 | |
166 if (descriptor.writable || descriptor.set) { | |
167 setter = function(value) { | |
168 this.impl[name] = value; | |
169 }; | |
170 } | |
171 | |
172 Object.defineProperty(target, name, { | |
173 get: getter, | |
174 set: setter, | |
175 configurable: descriptor.configurable, | |
176 enumerable: descriptor.enumerable | |
177 }); | |
178 }); | |
179 } | |
180 | |
181 /** | |
182 * @param {Function} nativeConstructor | |
183 * @param {Function} wrapperConstructor | |
184 * @param {string|Object=} opt_instance If present, this is used to extract | |
185 * properties from an instance object. If this is a string | |
186 * |document.createElement| is used to create an instance. | |
187 */ | |
188 function register(nativeConstructor, wrapperConstructor, opt_instance) { | |
189 var nativePrototype = nativeConstructor.prototype; | |
190 registerInternal(nativePrototype, wrapperConstructor, opt_instance); | |
191 mixinStatics(wrapperConstructor, nativeConstructor); | |
192 } | |
193 | |
194 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { | |
195 var wrapperPrototype = wrapperConstructor.prototype; | |
196 assert(constructorTable.get(nativePrototype) === undefined); | |
197 constructorTable.set(nativePrototype, wrapperConstructor); | |
198 addForwardingProperties(nativePrototype, wrapperPrototype); | |
199 if (opt_instance) | |
200 registerInstanceProperties(wrapperPrototype, opt_instance); | |
201 } | |
202 | |
203 function isWrapperFor(wrapperConstructor, nativeConstructor) { | |
204 return constructorTable.get(nativeConstructor.prototype) === | |
205 wrapperConstructor; | |
206 } | |
207 | |
208 /** | |
209 * Creates a generic wrapper constructor based on |object| and its | |
210 * constructor. | |
211 * Sometimes the constructor does not have an associated instance | |
212 * (CharacterData for example). In that case you can pass the constructor that | |
213 * you want to map the object to using |opt_nativeConstructor|. | |
214 * @param {Node} object | |
215 * @param {Function=} opt_nativeConstructor | |
216 * @return {Function} The generated constructor. | |
217 */ | |
218 function registerObject(object) { | |
219 var nativePrototype = Object.getPrototypeOf(object); | |
220 | |
221 var superWrapperConstructor = getWrapperConstructor(nativePrototype); | |
222 var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor); | |
223 registerInternal(nativePrototype, GeneratedWrapper, object); | |
224 | |
225 return GeneratedWrapper; | |
226 } | |
227 | |
228 function createWrapperConstructor(superWrapperConstructor) { | |
229 function GeneratedWrapper(node) { | |
230 superWrapperConstructor.call(this, node); | |
231 } | |
232 GeneratedWrapper.prototype = | |
233 Object.create(superWrapperConstructor.prototype); | |
234 GeneratedWrapper.prototype.constructor = GeneratedWrapper; | |
235 | |
236 return GeneratedWrapper; | |
237 } | |
238 | |
239 var OriginalDOMImplementation = DOMImplementation; | |
240 var OriginalEvent = Event; | |
241 var OriginalNode = Node; | |
242 var OriginalWindow = Window; | |
243 | |
244 function isWrapper(object) { | |
245 return object instanceof wrappers.EventTarget || | |
246 object instanceof wrappers.Event || | |
247 object instanceof wrappers.DOMImplementation; | |
248 } | |
249 | |
250 function isNative(object) { | |
251 return object instanceof OriginalNode || | |
252 object instanceof OriginalEvent || | |
253 object instanceof OriginalWindow || | |
254 object instanceof OriginalDOMImplementation; | |
255 } | |
256 | |
257 /** | |
258 * Wraps a node in a WrapperNode. If there already exists a wrapper for the | |
259 * |node| that wrapper is returned instead. | |
260 * @param {Node} node | |
261 * @return {WrapperNode} | |
262 */ | |
263 function wrap(impl) { | |
264 if (impl === null) | |
265 return null; | |
266 | |
267 assert(isNative(impl)); | |
268 var wrapper = wrapperTable.get(impl); | |
269 if (!wrapper) { | |
270 var wrapperConstructor = getWrapperConstructor(impl); | |
271 wrapper = new wrapperConstructor(impl); | |
272 wrapperTable.set(impl, wrapper); | |
273 } | |
274 return wrapper; | |
275 } | |
276 | |
277 /** | |
278 * Unwraps a wrapper and returns the node it is wrapping. | |
279 * @param {WrapperNode} wrapper | |
280 * @return {Node} | |
281 */ | |
282 function unwrap(wrapper) { | |
283 if (wrapper === null) | |
284 return null; | |
285 assert(isWrapper(wrapper)); | |
286 return wrapper.impl; | |
287 } | |
288 | |
289 /** | |
290 * Unwraps object if it is a wrapper. | |
291 * @param {Object} object | |
292 * @return {Object} The native implementation object. | |
293 */ | |
294 function unwrapIfNeeded(object) { | |
295 return object && isWrapper(object) ? unwrap(object) : object; | |
296 } | |
297 | |
298 /** | |
299 * Wraps object if it is not a wrapper. | |
300 * @param {Object} object | |
301 * @return {Object} The wrapper for object. | |
302 */ | |
303 function wrapIfNeeded(object) { | |
304 return object && !isWrapper(object) ? wrap(object) : object; | |
305 } | |
306 | |
307 /** | |
308 * Overrides the current wrapper (if any) for node. | |
309 * @param {Node} node | |
310 * @param {WrapperNode=} wrapper If left out the wrapper will be created as | |
311 * needed next time someone wraps the node. | |
312 */ | |
313 function rewrap(node, wrapper) { | |
314 if (wrapper === null) | |
315 return; | |
316 assert(isNative(node)); | |
317 assert(wrapper === undefined || isWrapper(wrapper)); | |
318 wrapperTable.set(node, wrapper); | |
319 } | |
320 | |
321 function defineGetter(constructor, name, getter) { | |
322 Object.defineProperty(constructor.prototype, name, { | |
323 get: getter, | |
324 configurable: true, | |
325 enumerable: true | |
326 }); | |
327 } | |
328 | |
329 function defineWrapGetter(constructor, name) { | |
330 defineGetter(constructor, name, function() { | |
331 return wrap(this.impl[name]); | |
332 }); | |
333 } | |
334 | |
335 /** | |
336 * Forwards existing methods on the native object to the wrapper methods. | |
337 * This does not wrap any of the arguments or the return value since the | |
338 * wrapper implementation already takes care of that. | |
339 * @param {Array.<Function>} constructors | |
340 * @parem {Array.<string>} names | |
341 */ | |
342 function forwardMethodsToWrapper(constructors, names) { | |
343 constructors.forEach(function(constructor) { | |
344 names.forEach(function(name) { | |
345 constructor.prototype[name] = function() { | |
346 var w = wrap(this); | |
347 return w[name].apply(w, arguments); | |
348 }; | |
349 }); | |
350 }); | |
351 } | |
352 | |
353 scope.assert = assert; | |
354 scope.defineGetter = defineGetter; | |
355 scope.defineWrapGetter = defineWrapGetter; | |
356 scope.forwardMethodsToWrapper = forwardMethodsToWrapper; | |
357 scope.isWrapper = isWrapper; | |
358 scope.isWrapperFor = isWrapperFor; | |
359 scope.mixin = mixin; | |
360 scope.registerObject = registerObject; | |
361 scope.registerWrapper = register; | |
362 scope.rewrap = rewrap; | |
363 scope.unwrap = unwrap; | |
364 scope.unwrapIfNeeded = unwrapIfNeeded; | |
365 scope.wrap = wrap; | |
366 scope.wrapIfNeeded = wrapIfNeeded; | |
367 scope.wrappers = wrappers; | |
368 | |
369 })(this.ShadowDOMPolyfill); | |
370 // Copyright 2013 The Polymer Authors. All rights reserved. | |
371 // Use of this source code is goverened by a BSD-style | |
372 // license that can be found in the LICENSE file. | |
373 | |
374 (function(scope) { | |
375 'use strict'; | |
376 | |
377 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
378 var mixin = scope.mixin; | |
379 var registerWrapper = scope.registerWrapper; | |
380 var unwrap = scope.unwrap; | |
381 var wrap = scope.wrap; | |
382 var wrappers = scope.wrappers; | |
383 | |
384 var wrappedFuns = new SideTable(); | |
385 var listenersTable = new SideTable(); | |
386 var handledEventsTable = new SideTable(); | |
387 var targetTable = new SideTable(); | |
388 var currentTargetTable = new SideTable(); | |
389 var relatedTargetTable = new SideTable(); | |
390 var eventPhaseTable = new SideTable(); | |
391 var stopPropagationTable = new SideTable(); | |
392 var stopImmediatePropagationTable = new SideTable(); | |
393 | |
394 function isShadowRoot(node) { | |
395 return node instanceof wrappers.ShadowRoot; | |
396 } | |
397 | |
398 function isInsertionPoint(node) { | |
399 var localName = node.localName; | |
400 return localName === 'content' || localName === 'shadow'; | |
401 } | |
402 | |
403 function isShadowHost(node) { | |
404 return !!node.shadowRoot; | |
405 } | |
406 | |
407 function getEventParent(node) { | |
408 var dv; | |
409 return node.parentNode || (dv = node.defaultView) && wrap(dv) || null; | |
410 } | |
411 | |
412 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df
n-adjusted-parent | |
413 function calculateParents(node, context, ancestors) { | |
414 if (ancestors.length) | |
415 return ancestors.shift(); | |
416 | |
417 // 1. | |
418 if (isShadowRoot(node)) | |
419 return node.insertionParent || scope.getHostForShadowRoot(node); | |
420 | |
421 // 2. | |
422 var eventParents = scope.eventParentsTable.get(node); | |
423 if (eventParents) { | |
424 // Copy over the remaining event parents for next iteration. | |
425 for (var i = 1; i < eventParents.length; i++) { | |
426 ancestors[i - 1] = eventParents[i]; | |
427 } | |
428 return eventParents[0]; | |
429 } | |
430 | |
431 // 3. | |
432 if (context && isInsertionPoint(node)) { | |
433 var parentNode = node.parentNode; | |
434 if (parentNode && isShadowHost(parentNode)) { | |
435 var trees = scope.getShadowTrees(parentNode); | |
436 var p = context.insertionParent; | |
437 for (var i = 0; i < trees.length; i++) { | |
438 if (trees[i].contains(p)) | |
439 return p; | |
440 } | |
441 } | |
442 } | |
443 | |
444 return getEventParent(node); | |
445 } | |
446 | |
447 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ev
ent-retargeting | |
448 function retarget(node) { | |
449 var stack = []; // 1. | |
450 var ancestor = node; // 2. | |
451 var targets = []; | |
452 var ancestors = []; | |
453 while (ancestor) { // 3. | |
454 var context = null; // 3.2. | |
455 // TODO(arv): Change order of these. If the stack is empty we always end | |
456 // up pushing ancestor, no matter what. | |
457 if (isInsertionPoint(ancestor)) { // 3.1. | |
458 context = topMostNotInsertionPoint(stack); // 3.1.1. | |
459 var top = stack[stack.length - 1] || ancestor; // 3.1.2. | |
460 stack.push(top); | |
461 } else if (!stack.length) { | |
462 stack.push(ancestor); // 3.3. | |
463 } | |
464 var target = stack[stack.length - 1]; // 3.4. | |
465 targets.push({target: target, currentTarget: ancestor}); // 3.5. | |
466 if (isShadowRoot(ancestor)) // 3.6. | |
467 stack.pop(); // 3.6.1. | |
468 | |
469 ancestor = calculateParents(ancestor, context, ancestors); // 3.7. | |
470 } | |
471 return targets; | |
472 } | |
473 | |
474 function topMostNotInsertionPoint(stack) { | |
475 for (var i = stack.length - 1; i >= 0; i--) { | |
476 if (!isInsertionPoint(stack[i])) | |
477 return stack[i]; | |
478 } | |
479 return null; | |
480 } | |
481 | |
482 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#df
n-adjusted-related-target | |
483 function adjustRelatedTarget(target, related) { | |
484 var ancestors = []; | |
485 while (target) { // 3. | |
486 var stack = []; // 3.1. | |
487 var ancestor = related; // 3.2. | |
488 var last = undefined; // 3.3. Needs to be reset every iteration. | |
489 while (ancestor) { | |
490 var context = null; | |
491 if (!stack.length) { | |
492 stack.push(ancestor); | |
493 } else { | |
494 if (isInsertionPoint(ancestor)) { // 3.4.3. | |
495 context = topMostNotInsertionPoint(stack); | |
496 // isDistributed is more general than checking whether last is | |
497 // assigned into ancestor. | |
498 if (isDistributed(last)) { // 3.4.3.2. | |
499 var head = stack[stack.length - 1]; | |
500 stack.push(head); | |
501 } | |
502 } | |
503 } | |
504 | |
505 if (inSameTree(ancestor, target)) // 3.4.4. | |
506 return stack[stack.length - 1]; | |
507 | |
508 if (isShadowRoot(ancestor)) // 3.4.5. | |
509 stack.pop(); | |
510 | |
511 last = ancestor; // 3.4.6. | |
512 ancestor = calculateParents(ancestor, context, ancestors); // 3.4.7. | |
513 } | |
514 if (isShadowRoot(target)) // 3.5. | |
515 target = scope.getHostForShadowRoot(target); | |
516 else | |
517 target = target.parentNode; // 3.6. | |
518 } | |
519 } | |
520 | |
521 function isDistributed(node) { | |
522 return node.insertionParent; | |
523 } | |
524 | |
525 function rootOfNode(node) { | |
526 var p; | |
527 while (p = node.parentNode) { | |
528 node = p; | |
529 } | |
530 return node; | |
531 } | |
532 | |
533 function inSameTree(a, b) { | |
534 return rootOfNode(a) === rootOfNode(b); | |
535 } | |
536 | |
537 function isMutationEvent(type) { | |
538 switch (type) { | |
539 case 'DOMAttrModified': | |
540 case 'DOMAttributeNameChanged': | |
541 case 'DOMCharacterDataModified': | |
542 case 'DOMElementNameChanged': | |
543 case 'DOMNodeInserted': | |
544 case 'DOMNodeInsertedIntoDocument': | |
545 case 'DOMNodeRemoved': | |
546 case 'DOMNodeRemovedFromDocument': | |
547 case 'DOMSubtreeModified': | |
548 return true; | |
549 } | |
550 return false; | |
551 } | |
552 | |
553 function dispatchOriginalEvent(originalEvent) { | |
554 // Make sure this event is only dispatched once. | |
555 if (handledEventsTable.get(originalEvent)) | |
556 return; | |
557 handledEventsTable.set(originalEvent, true); | |
558 | |
559 // Don't do rendering if this is a mutation event since rendering might | |
560 // mutate the DOM which would fire more events and we would most likely | |
561 // just iloop. | |
562 if (!isMutationEvent(originalEvent.type)) | |
563 scope.renderAllPending(); | |
564 | |
565 var target = wrap(originalEvent.target); | |
566 var event = wrap(originalEvent); | |
567 return dispatchEvent(event, target); | |
568 } | |
569 | |
570 function dispatchEvent(event, originalWrapperTarget) { | |
571 var eventPath = retarget(originalWrapperTarget); | |
572 | |
573 // For window load events the load event is dispatched at the window but | |
574 // the target is set to the document. | |
575 // | |
576 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#
the-end | |
577 // | |
578 // TODO(arv): Find a loess hacky way to do this. | |
579 if (event.type === 'load' && | |
580 eventPath.length === 2 && | |
581 eventPath[0].target instanceof wrappers.Document) { | |
582 eventPath.shift(); | |
583 } | |
584 | |
585 if (dispatchCapturing(event, eventPath)) { | |
586 if (dispatchAtTarget(event, eventPath)) { | |
587 dispatchBubbling(event, eventPath); | |
588 } | |
589 } | |
590 | |
591 eventPhaseTable.set(event, Event.NONE); | |
592 currentTargetTable.set(event, null); | |
593 | |
594 return event.defaultPrevented; | |
595 } | |
596 | |
597 function dispatchCapturing(event, eventPath) { | |
598 var phase; | |
599 | |
600 for (var i = eventPath.length - 1; i > 0; i--) { | |
601 var target = eventPath[i].target; | |
602 var currentTarget = eventPath[i].currentTarget; | |
603 if (target === currentTarget) | |
604 continue; | |
605 | |
606 phase = Event.CAPTURING_PHASE; | |
607 if (!invoke(eventPath[i], event, phase)) | |
608 return false; | |
609 } | |
610 | |
611 return true; | |
612 } | |
613 | |
614 function dispatchAtTarget(event, eventPath) { | |
615 var phase = Event.AT_TARGET; | |
616 return invoke(eventPath[0], event, phase); | |
617 } | |
618 | |
619 function dispatchBubbling(event, eventPath) { | |
620 var bubbles = event.bubbles; | |
621 var phase; | |
622 | |
623 for (var i = 1; i < eventPath.length; i++) { | |
624 var target = eventPath[i].target; | |
625 var currentTarget = eventPath[i].currentTarget; | |
626 if (target === currentTarget) | |
627 phase = Event.AT_TARGET; | |
628 else if (bubbles && !stopImmediatePropagationTable.get(event)) | |
629 phase = Event.BUBBLING_PHASE; | |
630 else | |
631 continue; | |
632 | |
633 if (!invoke(eventPath[i], event, phase)) | |
634 return; | |
635 } | |
636 } | |
637 | |
638 function invoke(tuple, event, phase) { | |
639 var target = tuple.target; | |
640 var currentTarget = tuple.currentTarget; | |
641 | |
642 var listeners = listenersTable.get(currentTarget); | |
643 if (!listeners) | |
644 return true; | |
645 | |
646 if ('relatedTarget' in event) { | |
647 var originalEvent = unwrap(event); | |
648 var relatedTarget = wrap(originalEvent.relatedTarget); | |
649 | |
650 var adjusted = adjustRelatedTarget(currentTarget, relatedTarget); | |
651 if (adjusted === target) | |
652 return true; | |
653 | |
654 relatedTargetTable.set(event, adjusted); | |
655 } | |
656 | |
657 eventPhaseTable.set(event, phase); | |
658 var type = event.type; | |
659 | |
660 var anyRemoved = false; | |
661 targetTable.set(event, target); | |
662 currentTargetTable.set(event, currentTarget); | |
663 | |
664 for (var i = 0; i < listeners.length; i++) { | |
665 var listener = listeners[i]; | |
666 if (listener.removed) { | |
667 anyRemoved = true; | |
668 continue; | |
669 } | |
670 | |
671 if (listener.type !== type || | |
672 !listener.capture && phase === Event.CAPTURING_PHASE || | |
673 listener.capture && phase === Event.BUBBLING_PHASE) { | |
674 continue; | |
675 } | |
676 | |
677 try { | |
678 if (typeof listener.handler === 'function') | |
679 listener.handler.call(currentTarget, event); | |
680 else | |
681 listener.handler.handleEvent(event); | |
682 | |
683 if (stopImmediatePropagationTable.get(event)) | |
684 return false; | |
685 | |
686 } catch (ex) { | |
687 if (window.onerror) | |
688 window.onerror(ex.message); | |
689 else | |
690 console.error(ex); | |
691 } | |
692 } | |
693 | |
694 if (anyRemoved) { | |
695 var copy = listeners.slice(); | |
696 listeners.length = 0; | |
697 for (var i = 0; i < copy.length; i++) { | |
698 if (!copy[i].removed) | |
699 listeners.push(copy[i]); | |
700 } | |
701 } | |
702 | |
703 return !stopPropagationTable.get(event); | |
704 } | |
705 | |
706 function Listener(type, handler, capture) { | |
707 this.type = type; | |
708 this.handler = handler; | |
709 this.capture = Boolean(capture); | |
710 } | |
711 Listener.prototype = { | |
712 equals: function(that) { | |
713 return this.handler === that.handler && this.type === that.type && | |
714 this.capture === that.capture; | |
715 }, | |
716 get removed() { | |
717 return this.handler === null; | |
718 }, | |
719 remove: function() { | |
720 this.handler = null; | |
721 } | |
722 }; | |
723 | |
724 var OriginalEvent = window.Event; | |
725 | |
726 /** | |
727 * Creates a new Event wrapper or wraps an existin native Event object. | |
728 * @param {string|Event} type | |
729 * @param {Object=} options | |
730 * @constructor | |
731 */ | |
732 function Event(type, options) { | |
733 if (type instanceof OriginalEvent) | |
734 this.impl = type; | |
735 else | |
736 return wrap(constructEvent(OriginalEvent, 'Event', type, options)); | |
737 } | |
738 Event.prototype = { | |
739 get target() { | |
740 return targetTable.get(this); | |
741 }, | |
742 get currentTarget() { | |
743 return currentTargetTable.get(this); | |
744 }, | |
745 get eventPhase() { | |
746 return eventPhaseTable.get(this); | |
747 }, | |
748 stopPropagation: function() { | |
749 stopPropagationTable.set(this, true); | |
750 }, | |
751 stopImmediatePropagation: function() { | |
752 stopPropagationTable.set(this, true); | |
753 stopImmediatePropagationTable.set(this, true); | |
754 } | |
755 }; | |
756 registerWrapper(OriginalEvent, Event, document.createEvent('Event')); | |
757 | |
758 function unwrapOptions(options) { | |
759 if (!options || !options.relatedTarget) | |
760 return options; | |
761 return Object.create(options, { | |
762 relatedTarget: {value: unwrap(options.relatedTarget)} | |
763 }); | |
764 } | |
765 | |
766 function registerGenericEvent(name, SuperEvent, prototype) { | |
767 var OriginalEvent = window[name]; | |
768 var GenericEvent = function(type, options) { | |
769 if (type instanceof OriginalEvent) | |
770 this.impl = type; | |
771 else | |
772 return wrap(constructEvent(OriginalEvent, name, type, options)); | |
773 }; | |
774 GenericEvent.prototype = Object.create(SuperEvent.prototype); | |
775 if (prototype) | |
776 mixin(GenericEvent.prototype, prototype); | |
777 // Firefox does not support FocusEvent | |
778 // https://bugzilla.mozilla.org/show_bug.cgi?id=855741 | |
779 if (OriginalEvent) | |
780 registerWrapper(OriginalEvent, GenericEvent, document.createEvent(name)); | |
781 return GenericEvent; | |
782 } | |
783 | |
784 var UIEvent = registerGenericEvent('UIEvent', Event); | |
785 var CustomEvent = registerGenericEvent('CustomEvent', Event); | |
786 | |
787 var relatedTargetProto = { | |
788 get relatedTarget() { | |
789 return relatedTargetTable.get(this) || wrap(unwrap(this).relatedTarget); | |
790 } | |
791 }; | |
792 | |
793 function getInitFunction(name, relatedTargetIndex) { | |
794 return function() { | |
795 arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]); | |
796 var impl = unwrap(this); | |
797 impl[name].apply(impl, arguments); | |
798 }; | |
799 } | |
800 | |
801 var mouseEventProto = mixin({ | |
802 initMouseEvent: getInitFunction('initMouseEvent', 14) | |
803 }, relatedTargetProto); | |
804 | |
805 var focusEventProto = mixin({ | |
806 initFocusEvent: getInitFunction('initFocusEvent', 5) | |
807 }, relatedTargetProto); | |
808 | |
809 var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto); | |
810 var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto); | |
811 | |
812 var MutationEvent = registerGenericEvent('MutationEvent', Event, { | |
813 initMutationEvent: getInitFunction('initMutationEvent', 3), | |
814 get relatedNode() { | |
815 return wrap(this.impl.relatedNode); | |
816 }, | |
817 }); | |
818 | |
819 // In case the browser does not support event constructors we polyfill that | |
820 // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to | |
821 // `initFooEvent` are derived from the registered default event init dict. | |
822 var defaultInitDicts = Object.create(null); | |
823 | |
824 var supportsEventConstructors = (function() { | |
825 try { | |
826 new window.MouseEvent('click'); | |
827 } catch (ex) { | |
828 return false; | |
829 } | |
830 return true; | |
831 })(); | |
832 | |
833 /** | |
834 * Constructs a new native event. | |
835 */ | |
836 function constructEvent(OriginalEvent, name, type, options) { | |
837 if (supportsEventConstructors) | |
838 return new OriginalEvent(type, unwrapOptions(options)); | |
839 | |
840 // Create the arguments from the default dictionary. | |
841 var event = unwrap(document.createEvent(name)); | |
842 var defaultDict = defaultInitDicts[name]; | |
843 var args = [type]; | |
844 Object.keys(defaultDict).forEach(function(key) { | |
845 var v = options != null && key in options ? | |
846 options[key] : defaultDict[key]; | |
847 if (key === 'relatedTarget') | |
848 v = unwrap(v); | |
849 args.push(v); | |
850 }); | |
851 event['init' + name].apply(event, args); | |
852 return event; | |
853 } | |
854 | |
855 if (!supportsEventConstructors) { | |
856 var configureEventConstructor = function(name, initDict, superName) { | |
857 if (superName) { | |
858 var superDict = defaultInitDicts[superName]; | |
859 initDict = mixin(mixin({}, superDict), initDict); | |
860 } | |
861 | |
862 defaultInitDicts[name] = initDict; | |
863 }; | |
864 | |
865 // The order of the default event init dictionary keys is important, the | |
866 // arguments to initFooEvent is derived from that. | |
867 configureEventConstructor('Event', {bubbles: false, cancelable: false}); | |
868 configureEventConstructor('CustomEvent', {detail: null}, 'Event'); | |
869 configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event'); | |
870 configureEventConstructor('MouseEvent', { | |
871 screenX: 0, | |
872 screenY: 0, | |
873 clientX: 0, | |
874 clientY: 0, | |
875 ctrlKey: false, | |
876 altKey: false, | |
877 shiftKey: false, | |
878 metaKey: false, | |
879 button: 0, | |
880 relatedTarget: null | |
881 }, 'UIEvent'); | |
882 configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent'); | |
883 } | |
884 | |
885 function isValidListener(fun) { | |
886 if (typeof fun === 'function') | |
887 return true; | |
888 return fun && fun.handleEvent; | |
889 } | |
890 | |
891 var OriginalEventTarget = window.EventTarget; | |
892 | |
893 /** | |
894 * This represents a wrapper for an EventTarget. | |
895 * @param {!EventTarget} impl The original event target. | |
896 * @constructor | |
897 */ | |
898 function EventTarget(impl) { | |
899 this.impl = impl; | |
900 } | |
901 | |
902 // Node and Window have different internal type checks in WebKit so we cannot | |
903 // use the same method as the original function. | |
904 var methodNames = [ | |
905 'addEventListener', | |
906 'removeEventListener', | |
907 'dispatchEvent' | |
908 ]; | |
909 | |
910 [Element, Window, Document].forEach(function(constructor) { | |
911 var p = constructor.prototype; | |
912 methodNames.forEach(function(name) { | |
913 Object.defineProperty(p, name + '_', {value: p[name]}); | |
914 }); | |
915 }); | |
916 | |
917 function getTargetToListenAt(wrapper) { | |
918 if (wrapper instanceof wrappers.ShadowRoot) | |
919 wrapper = scope.getHostForShadowRoot(wrapper); | |
920 return unwrap(wrapper); | |
921 } | |
922 | |
923 EventTarget.prototype = { | |
924 addEventListener: function(type, fun, capture) { | |
925 if (!isValidListener(fun)) | |
926 return; | |
927 | |
928 var listener = new Listener(type, fun, capture); | |
929 var listeners = listenersTable.get(this); | |
930 if (!listeners) { | |
931 listeners = []; | |
932 listenersTable.set(this, listeners); | |
933 } else { | |
934 // Might have a duplicate. | |
935 for (var i = 0; i < listeners.length; i++) { | |
936 if (listener.equals(listeners[i])) | |
937 return; | |
938 } | |
939 } | |
940 | |
941 listeners.push(listener); | |
942 | |
943 var target = getTargetToListenAt(this); | |
944 target.addEventListener_(type, dispatchOriginalEvent, true); | |
945 }, | |
946 removeEventListener: function(type, fun, capture) { | |
947 capture = Boolean(capture); | |
948 var listeners = listenersTable.get(this); | |
949 if (!listeners) | |
950 return; | |
951 var count = 0, found = false; | |
952 for (var i = 0; i < listeners.length; i++) { | |
953 if (listeners[i].type === type && listeners[i].capture === capture) { | |
954 count++; | |
955 if (listeners[i].handler === fun) { | |
956 found = true; | |
957 listeners[i].remove(); | |
958 } | |
959 } | |
960 } | |
961 | |
962 if (found && count === 1) { | |
963 var target = getTargetToListenAt(this); | |
964 target.removeEventListener_(type, dispatchOriginalEvent, true); | |
965 } | |
966 }, | |
967 dispatchEvent: function(event) { | |
968 scope.renderAllPending(); | |
969 var target = getTargetToListenAt(this); | |
970 return target.dispatchEvent_(unwrap(event)); | |
971 } | |
972 }; | |
973 | |
974 if (OriginalEventTarget) | |
975 registerWrapper(OriginalEventTarget, EventTarget); | |
976 | |
977 function wrapEventTargetMethods(constructors) { | |
978 forwardMethodsToWrapper(constructors, methodNames); | |
979 } | |
980 | |
981 | |
982 var originalElementFromPoint = document.elementFromPoint; | |
983 | |
984 function elementFromPoint(self, document, x, y) { | |
985 scope.renderAllPending(); | |
986 | |
987 var element = wrap(originalElementFromPoint.call(document.impl, x, y)); | |
988 var targets = retarget(element, this) | |
989 for (var i = 0; i < targets.length; i++) { | |
990 var target = targets[i]; | |
991 if (target.currentTarget === self) | |
992 return target.target; | |
993 } | |
994 return null; | |
995 } | |
996 | |
997 scope.adjustRelatedTarget = adjustRelatedTarget; | |
998 scope.elementFromPoint = elementFromPoint; | |
999 scope.wrapEventTargetMethods = wrapEventTargetMethods; | |
1000 scope.wrappers.CustomEvent = CustomEvent; | |
1001 scope.wrappers.Event = Event; | |
1002 scope.wrappers.EventTarget = EventTarget; | |
1003 scope.wrappers.FocusEvent = FocusEvent; | |
1004 scope.wrappers.MouseEvent = MouseEvent; | |
1005 scope.wrappers.MutationEvent = MutationEvent; | |
1006 scope.wrappers.UIEvent = UIEvent; | |
1007 | |
1008 })(this.ShadowDOMPolyfill); | |
1009 | |
1010 // Copyright 2012 The Polymer Authors. All rights reserved. | |
1011 // Use of this source code is goverened by a BSD-style | |
1012 // license that can be found in the LICENSE file. | |
1013 | |
1014 (function(scope) { | |
1015 'use strict'; | |
1016 | |
1017 var wrap = scope.wrap; | |
1018 | |
1019 function nonEnum(obj, prop) { | |
1020 Object.defineProperty(obj, prop, {enumerable: false}); | |
1021 } | |
1022 | |
1023 function NodeList() { | |
1024 this.length = 0; | |
1025 nonEnum(this, 'length'); | |
1026 } | |
1027 NodeList.prototype = { | |
1028 item: function(index) { | |
1029 return this[index]; | |
1030 } | |
1031 }; | |
1032 nonEnum(NodeList.prototype, 'item'); | |
1033 | |
1034 function wrapNodeList(list) { | |
1035 if (list == null) | |
1036 return list; | |
1037 var wrapperList = new NodeList(); | |
1038 for (var i = 0, length = list.length; i < length; i++) { | |
1039 wrapperList[i] = wrap(list[i]); | |
1040 } | |
1041 wrapperList.length = length; | |
1042 return wrapperList; | |
1043 } | |
1044 | |
1045 function addWrapNodeListMethod(wrapperConstructor, name) { | |
1046 wrapperConstructor.prototype[name] = function() { | |
1047 return wrapNodeList(this.impl[name].apply(this.impl, arguments)); | |
1048 }; | |
1049 } | |
1050 | |
1051 scope.wrappers.NodeList = NodeList; | |
1052 scope.addWrapNodeListMethod = addWrapNodeListMethod; | |
1053 scope.wrapNodeList = wrapNodeList; | |
1054 | |
1055 })(this.ShadowDOMPolyfill); | |
1056 // Copyright 2012 The Polymer Authors. All rights reserved. | |
1057 // Use of this source code is goverened by a BSD-style | |
1058 // license that can be found in the LICENSE file. | |
1059 | |
1060 (function(scope) { | |
1061 'use strict'; | |
1062 | |
1063 var EventTarget = scope.wrappers.EventTarget; | |
1064 var NodeList = scope.wrappers.NodeList; | |
1065 var defineWrapGetter = scope.defineWrapGetter; | |
1066 var assert = scope.assert; | |
1067 var mixin = scope.mixin; | |
1068 var registerWrapper = scope.registerWrapper; | |
1069 var unwrap = scope.unwrap; | |
1070 var wrap = scope.wrap; | |
1071 | |
1072 function assertIsNodeWrapper(node) { | |
1073 assert(node instanceof Node); | |
1074 } | |
1075 | |
1076 /** | |
1077 * Collects nodes from a DocumentFragment or a Node for removal followed | |
1078 * by an insertion. | |
1079 * | |
1080 * This updates the internal pointers for node, previousNode and nextNode. | |
1081 */ | |
1082 function collectNodes(node, parentNode, previousNode, nextNode) { | |
1083 if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { | |
1084 if (node.parentNode) | |
1085 node.parentNode.removeChild(node); | |
1086 node.parentNode_ = parentNode; | |
1087 node.previousSibling_ = previousNode; | |
1088 node.nextSibling_ = nextNode; | |
1089 if (previousNode) | |
1090 previousNode.nextSibling_ = node; | |
1091 if (nextNode) | |
1092 nextNode.previousSibling_ = node; | |
1093 return [node]; | |
1094 } | |
1095 | |
1096 var nodes = []; | |
1097 var firstChild; | |
1098 while (firstChild = node.firstChild) { | |
1099 node.removeChild(firstChild); | |
1100 nodes.push(firstChild); | |
1101 firstChild.parentNode_ = parentNode; | |
1102 } | |
1103 | |
1104 for (var i = 0; i < nodes.length; i++) { | |
1105 nodes[i].previousSibling_ = nodes[i - 1] || previousNode; | |
1106 nodes[i].nextSibling_ = nodes[i + 1] || nextNode; | |
1107 } | |
1108 | |
1109 if (previousNode) | |
1110 previousNode.nextSibling_ = nodes[0]; | |
1111 if (nextNode) | |
1112 nextNode.previousSibling_ = nodes[nodes.length - 1]; | |
1113 | |
1114 return nodes; | |
1115 } | |
1116 | |
1117 function unwrapNodesForInsertion(nodes) { | |
1118 if (nodes.length === 1) | |
1119 return unwrap(nodes[0]); | |
1120 | |
1121 var df = unwrap(document.createDocumentFragment()); | |
1122 for (var i = 0; i < nodes.length; i++) { | |
1123 df.appendChild(unwrap(nodes[i])); | |
1124 } | |
1125 return df; | |
1126 } | |
1127 | |
1128 function removeAllChildNodes(wrapper) { | |
1129 var childWrapper = wrapper.firstChild; | |
1130 while (childWrapper) { | |
1131 assert(childWrapper.parentNode === wrapper); | |
1132 var nextSibling = childWrapper.nextSibling; | |
1133 var childNode = unwrap(childWrapper); | |
1134 var parentNode = childNode.parentNode; | |
1135 if (parentNode) | |
1136 originalRemoveChild.call(parentNode, childNode); | |
1137 childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.p
arentNode_ = null; | |
1138 childWrapper = nextSibling; | |
1139 } | |
1140 wrapper.firstChild_ = wrapper.lastChild_ = null; | |
1141 } | |
1142 | |
1143 var OriginalNode = window.Node; | |
1144 | |
1145 /** | |
1146 * This represents a wrapper of a native DOM node. | |
1147 * @param {!Node} original The original DOM node, aka, the visual DOM node. | |
1148 * @constructor | |
1149 * @extends {EventTarget} | |
1150 */ | |
1151 function Node(original) { | |
1152 assert(original instanceof OriginalNode); | |
1153 | |
1154 EventTarget.call(this, original); | |
1155 | |
1156 // These properties are used to override the visual references with the | |
1157 // logical ones. If the value is undefined it means that the logical is the | |
1158 // same as the visual. | |
1159 | |
1160 /** | |
1161 * @type {Node|undefined} | |
1162 * @private | |
1163 */ | |
1164 this.parentNode_ = undefined; | |
1165 | |
1166 /** | |
1167 * @type {Node|undefined} | |
1168 * @private | |
1169 */ | |
1170 this.firstChild_ = undefined; | |
1171 | |
1172 /** | |
1173 * @type {Node|undefined} | |
1174 * @private | |
1175 */ | |
1176 this.lastChild_ = undefined; | |
1177 | |
1178 /** | |
1179 * @type {Node|undefined} | |
1180 * @private | |
1181 */ | |
1182 this.nextSibling_ = undefined; | |
1183 | |
1184 /** | |
1185 * @type {Node|undefined} | |
1186 * @private | |
1187 */ | |
1188 this.previousSibling_ = undefined; | |
1189 }; | |
1190 | |
1191 var originalAppendChild = OriginalNode.prototype.appendChild; | |
1192 var originalInsertBefore = OriginalNode.prototype.insertBefore; | |
1193 var originalReplaceChild = OriginalNode.prototype.replaceChild; | |
1194 var originalRemoveChild = OriginalNode.prototype.removeChild; | |
1195 var originalCompareDocumentPosition = | |
1196 OriginalNode.prototype.compareDocumentPosition; | |
1197 | |
1198 Node.prototype = Object.create(EventTarget.prototype); | |
1199 mixin(Node.prototype, { | |
1200 appendChild: function(childWrapper) { | |
1201 assertIsNodeWrapper(childWrapper); | |
1202 | |
1203 this.invalidateShadowRenderer(); | |
1204 | |
1205 var previousNode = this.lastChild; | |
1206 var nextNode = null; | |
1207 var nodes = collectNodes(childWrapper, this, | |
1208 previousNode, nextNode); | |
1209 | |
1210 this.lastChild_ = nodes[nodes.length - 1]; | |
1211 if (!previousNode) | |
1212 this.firstChild_ = nodes[0]; | |
1213 | |
1214 // TODO(arv): It is unclear if we need to update the visual DOM here. | |
1215 // A better aproach might be to make sure we only get here for nodes that | |
1216 // are related to a shadow host and then invalidate that and re-render | |
1217 // the host (on reflow?). | |
1218 originalAppendChild.call(this.impl, unwrapNodesForInsertion(nodes)); | |
1219 | |
1220 return childWrapper; | |
1221 }, | |
1222 | |
1223 insertBefore: function(childWrapper, refWrapper) { | |
1224 // TODO(arv): Unify with appendChild | |
1225 if (!refWrapper) | |
1226 return this.appendChild(childWrapper); | |
1227 | |
1228 assertIsNodeWrapper(childWrapper); | |
1229 assertIsNodeWrapper(refWrapper); | |
1230 assert(refWrapper.parentNode === this); | |
1231 | |
1232 this.invalidateShadowRenderer(); | |
1233 | |
1234 var previousNode = refWrapper.previousSibling; | |
1235 var nextNode = refWrapper; | |
1236 var nodes = collectNodes(childWrapper, this, | |
1237 previousNode, nextNode); | |
1238 | |
1239 | |
1240 if (this.firstChild === refWrapper) | |
1241 this.firstChild_ = nodes[0]; | |
1242 | |
1243 // insertBefore refWrapper no matter what the parent is? | |
1244 var refNode = unwrap(refWrapper); | |
1245 var parentNode = refNode.parentNode; | |
1246 if (parentNode) { | |
1247 originalInsertBefore.call( | |
1248 parentNode, | |
1249 unwrapNodesForInsertion(nodes), | |
1250 refNode); | |
1251 } | |
1252 | |
1253 return childWrapper; | |
1254 }, | |
1255 | |
1256 removeChild: function(childWrapper) { | |
1257 assertIsNodeWrapper(childWrapper); | |
1258 if (childWrapper.parentNode !== this) { | |
1259 // TODO(arv): DOMException | |
1260 throw new Error('NotFoundError'); | |
1261 } | |
1262 | |
1263 this.invalidateShadowRenderer(); | |
1264 | |
1265 // We need to remove the real node from the DOM before updating the | |
1266 // pointers. This is so that that mutation event is dispatched before | |
1267 // the pointers have changed. | |
1268 var thisFirstChild = this.firstChild; | |
1269 var thisLastChild = this.lastChild; | |
1270 var childWrapperNextSibling = childWrapper.nextSibling; | |
1271 var childWrapperPreviousSibling = childWrapper.previousSibling; | |
1272 | |
1273 var childNode = unwrap(childWrapper); | |
1274 var parentNode = childNode.parentNode; | |
1275 if (parentNode) | |
1276 originalRemoveChild.call(parentNode, childNode); | |
1277 | |
1278 if (thisFirstChild === childWrapper) | |
1279 this.firstChild_ = childWrapperNextSibling; | |
1280 if (thisLastChild === childWrapper) | |
1281 this.lastChild_ = childWrapperPreviousSibling; | |
1282 if (childWrapperPreviousSibling) | |
1283 childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling; | |
1284 if (childWrapperNextSibling) | |
1285 childWrapperNextSibling.previousSibling_ = childWrapperPreviousSibling; | |
1286 | |
1287 childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.p
arentNode_ = null; | |
1288 | |
1289 return childWrapper; | |
1290 }, | |
1291 | |
1292 replaceChild: function(newChildWrapper, oldChildWrapper) { | |
1293 assertIsNodeWrapper(newChildWrapper); | |
1294 assertIsNodeWrapper(oldChildWrapper); | |
1295 | |
1296 if (oldChildWrapper.parentNode !== this) { | |
1297 // TODO(arv): DOMException | |
1298 throw new Error('NotFoundError'); | |
1299 } | |
1300 | |
1301 this.invalidateShadowRenderer(); | |
1302 | |
1303 var previousNode = oldChildWrapper.previousSibling; | |
1304 var nextNode = oldChildWrapper.nextSibling; | |
1305 if (nextNode === newChildWrapper) | |
1306 nextNode = newChildWrapper.nextSibling; | |
1307 var nodes = collectNodes(newChildWrapper, this, | |
1308 previousNode, nextNode); | |
1309 | |
1310 if (this.firstChild === oldChildWrapper) | |
1311 this.firstChild_ = nodes[0]; | |
1312 if (this.lastChild === oldChildWrapper) | |
1313 this.lastChild_ = nodes[nodes.length - 1]; | |
1314 | |
1315 oldChildWrapper.previousSibling_ = null; | |
1316 oldChildWrapper.nextSibling_ = null; | |
1317 oldChildWrapper.parentNode_ = null; | |
1318 | |
1319 // replaceChild no matter what the parent is? | |
1320 var oldChildNode = unwrap(oldChildWrapper); | |
1321 if (oldChildNode.parentNode) { | |
1322 originalReplaceChild.call( | |
1323 oldChildNode.parentNode, | |
1324 unwrapNodesForInsertion(nodes), | |
1325 oldChildNode); | |
1326 } | |
1327 | |
1328 return oldChildWrapper; | |
1329 }, | |
1330 | |
1331 hasChildNodes: function() { | |
1332 return this.firstChild === null; | |
1333 }, | |
1334 | |
1335 /** @type {Node} */ | |
1336 get parentNode() { | |
1337 // If the parentNode has not been overridden, use the original parentNode. | |
1338 return this.parentNode_ !== undefined ? | |
1339 this.parentNode_ : wrap(this.impl.parentNode); | |
1340 }, | |
1341 | |
1342 /** @type {Node} */ | |
1343 get firstChild() { | |
1344 return this.firstChild_ !== undefined ? | |
1345 this.firstChild_ : wrap(this.impl.firstChild); | |
1346 }, | |
1347 | |
1348 /** @type {Node} */ | |
1349 get lastChild() { | |
1350 return this.lastChild_ !== undefined ? | |
1351 this.lastChild_ : wrap(this.impl.lastChild); | |
1352 }, | |
1353 | |
1354 /** @type {Node} */ | |
1355 get nextSibling() { | |
1356 return this.nextSibling_ !== undefined ? | |
1357 this.nextSibling_ : wrap(this.impl.nextSibling); | |
1358 }, | |
1359 | |
1360 /** @type {Node} */ | |
1361 get previousSibling() { | |
1362 return this.previousSibling_ !== undefined ? | |
1363 this.previousSibling_ : wrap(this.impl.previousSibling); | |
1364 }, | |
1365 | |
1366 get parentElement() { | |
1367 var p = this.parentNode; | |
1368 while (p && p.nodeType !== Node.ELEMENT_NODE) { | |
1369 p = p.parentNode; | |
1370 } | |
1371 return p; | |
1372 }, | |
1373 | |
1374 get textContent() { | |
1375 // TODO(arv): This should fallback to this.impl.textContent if there | |
1376 // are no shadow trees below or above the context node. | |
1377 var s = ''; | |
1378 for (var child = this.firstChild; child; child = child.nextSibling) { | |
1379 s += child.textContent; | |
1380 } | |
1381 return s; | |
1382 }, | |
1383 set textContent(textContent) { | |
1384 removeAllChildNodes(this); | |
1385 this.invalidateShadowRenderer(); | |
1386 if (textContent !== '') { | |
1387 var textNode = this.impl.ownerDocument.createTextNode(textContent); | |
1388 this.appendChild(textNode); | |
1389 } | |
1390 }, | |
1391 | |
1392 get childNodes() { | |
1393 var wrapperList = new NodeList(); | |
1394 var i = 0; | |
1395 for (var child = this.firstChild; child; child = child.nextSibling) { | |
1396 wrapperList[i++] = child; | |
1397 } | |
1398 wrapperList.length = i; | |
1399 return wrapperList; | |
1400 }, | |
1401 | |
1402 cloneNode: function(deep) { | |
1403 if (!this.invalidateShadowRenderer()) | |
1404 return wrap(this.impl.cloneNode(deep)); | |
1405 | |
1406 var clone = wrap(this.impl.cloneNode(false)); | |
1407 if (deep) { | |
1408 for (var child = this.firstChild; child; child = child.nextSibling) { | |
1409 clone.appendChild(child.cloneNode(true)); | |
1410 } | |
1411 } | |
1412 // TODO(arv): Some HTML elements also clone other data like value. | |
1413 return clone; | |
1414 }, | |
1415 | |
1416 // insertionParent is added in ShadowRender.js | |
1417 | |
1418 contains: function(child) { | |
1419 if (!child) | |
1420 return false; | |
1421 | |
1422 // TODO(arv): Optimize using ownerDocument etc. | |
1423 if (child === this) | |
1424 return true; | |
1425 var parentNode = child.parentNode; | |
1426 if (!parentNode) | |
1427 return false; | |
1428 return this.contains(parentNode); | |
1429 }, | |
1430 | |
1431 compareDocumentPosition: function(otherNode) { | |
1432 // This only wraps, it therefore only operates on the composed DOM and not | |
1433 // the logical DOM. | |
1434 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode)); | |
1435 } | |
1436 }); | |
1437 | |
1438 defineWrapGetter(Node, 'ownerDocument'); | |
1439 | |
1440 // We use a DocumentFragment as a base and then delete the properties of | |
1441 // DocumentFragment.prototype from the wrapper Node. Since delete makes | |
1442 // objects slow in some JS engines we recreate the prototype object. | |
1443 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); | |
1444 delete Node.prototype.querySelector; | |
1445 delete Node.prototype.querySelectorAll; | |
1446 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); | |
1447 | |
1448 scope.wrappers.Node = Node; | |
1449 | |
1450 })(this.ShadowDOMPolyfill); | |
1451 | |
1452 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1453 // Use of this source code is governed by a BSD-style | |
1454 // license that can be found in the LICENSE file. | |
1455 | |
1456 (function(scope) { | |
1457 'use strict'; | |
1458 | |
1459 function findOne(node, selector) { | |
1460 var m, el = node.firstElementChild; | |
1461 while (el) { | |
1462 if (el.matches(selector)) | |
1463 return el; | |
1464 m = findOne(el, selector); | |
1465 if (m) | |
1466 return m; | |
1467 el = el.nextElementSibling; | |
1468 } | |
1469 return null; | |
1470 } | |
1471 | |
1472 function findAll(node, selector, results) { | |
1473 var el = node.firstElementChild; | |
1474 while (el) { | |
1475 if (el.matches(selector)) | |
1476 results[results.length++] = el; | |
1477 findAll(el, selector, results); | |
1478 el = el.nextElementSibling; | |
1479 } | |
1480 return results; | |
1481 } | |
1482 | |
1483 // find and findAll will only match Simple Selectors, | |
1484 // Structural Pseudo Classes are not guarenteed to be correct | |
1485 // http://www.w3.org/TR/css3-selectors/#simple-selectors | |
1486 | |
1487 var SelectorsInterface = { | |
1488 querySelector: function(selector) { | |
1489 return findOne(this, selector); | |
1490 }, | |
1491 querySelectorAll: function(selector) { | |
1492 return findAll(this, selector, new NodeList()) | |
1493 } | |
1494 }; | |
1495 | |
1496 var GetElementsByInterface = { | |
1497 getElementsByTagName: function(tagName) { | |
1498 // TODO(arv): Check tagName? | |
1499 return this.querySelectorAll(tagName); | |
1500 }, | |
1501 getElementsByClassName: function(className) { | |
1502 // TODO(arv): Check className? | |
1503 return this.querySelectorAll('.' + className); | |
1504 }, | |
1505 getElementsByTagNameNS: function(ns, tagName) { | |
1506 if (ns === '*') | |
1507 return this.getElementsByTagName(tagName); | |
1508 | |
1509 // TODO(arv): Check tagName? | |
1510 var result = new NodeList; | |
1511 var els = this.getElementsByTagName(tagName); | |
1512 for (var i = 0, j = 0; i < els.length; i++) { | |
1513 if (els[i].namespaceURI === ns) | |
1514 result[j++] = els[i]; | |
1515 } | |
1516 result.length = j; | |
1517 return result; | |
1518 } | |
1519 }; | |
1520 | |
1521 scope.GetElementsByInterface = GetElementsByInterface; | |
1522 scope.SelectorsInterface = SelectorsInterface; | |
1523 | |
1524 })(this.ShadowDOMPolyfill); | |
1525 | |
1526 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1527 // Use of this source code is goverened by a BSD-style | |
1528 // license that can be found in the LICENSE file. | |
1529 | |
1530 (function(scope) { | |
1531 'use strict'; | |
1532 | |
1533 var NodeList = scope.wrappers.NodeList; | |
1534 | |
1535 function forwardElement(node) { | |
1536 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
1537 node = node.nextSibling; | |
1538 } | |
1539 return node; | |
1540 } | |
1541 | |
1542 function backwardsElement(node) { | |
1543 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
1544 node = node.previousSibling; | |
1545 } | |
1546 return node; | |
1547 } | |
1548 | |
1549 var ParentNodeInterface = { | |
1550 get firstElementChild() { | |
1551 return forwardElement(this.firstChild); | |
1552 }, | |
1553 | |
1554 get lastElementChild() { | |
1555 return backwardsElement(this.lastChild); | |
1556 }, | |
1557 | |
1558 get childElementCount() { | |
1559 var count = 0; | |
1560 for (var child = this.firstElementChild; | |
1561 child; | |
1562 child = child.nextElementSibling) { | |
1563 count++; | |
1564 } | |
1565 return count; | |
1566 }, | |
1567 | |
1568 get children() { | |
1569 var wrapperList = new NodeList(); | |
1570 var i = 0; | |
1571 for (var child = this.firstElementChild; | |
1572 child; | |
1573 child = child.nextElementSibling) { | |
1574 wrapperList[i++] = child; | |
1575 } | |
1576 wrapperList.length = i; | |
1577 return wrapperList; | |
1578 } | |
1579 }; | |
1580 | |
1581 var ChildNodeInterface = { | |
1582 get nextElementSibling() { | |
1583 return forwardElement(this.nextSibling); | |
1584 }, | |
1585 | |
1586 get previousElementSibling() { | |
1587 return backwardsElement(this.nextSibling); | |
1588 } | |
1589 }; | |
1590 | |
1591 scope.ChildNodeInterface = ChildNodeInterface; | |
1592 scope.ParentNodeInterface = ParentNodeInterface; | |
1593 | |
1594 })(this.ShadowDOMPolyfill); | |
1595 | |
1596 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1597 // Use of this source code is goverened by a BSD-style | |
1598 // license that can be found in the LICENSE file. | |
1599 | |
1600 (function(scope) { | |
1601 'use strict'; | |
1602 | |
1603 var ChildNodeInterface = scope.ChildNodeInterface; | |
1604 var Node = scope.wrappers.Node; | |
1605 var mixin = scope.mixin; | |
1606 var registerWrapper = scope.registerWrapper; | |
1607 | |
1608 var OriginalCharacterData = window.CharacterData; | |
1609 | |
1610 function CharacterData(node) { | |
1611 Node.call(this, node); | |
1612 } | |
1613 CharacterData.prototype = Object.create(Node.prototype); | |
1614 mixin(CharacterData.prototype, { | |
1615 get textContent() { | |
1616 return this.data; | |
1617 }, | |
1618 set textContent(value) { | |
1619 this.data = value; | |
1620 } | |
1621 }); | |
1622 | |
1623 mixin(CharacterData.prototype, ChildNodeInterface); | |
1624 | |
1625 registerWrapper(OriginalCharacterData, CharacterData, | |
1626 document.createTextNode('')); | |
1627 | |
1628 scope.wrappers.CharacterData = CharacterData; | |
1629 })(this.ShadowDOMPolyfill); | |
1630 | |
1631 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1632 // Use of this source code is goverened by a BSD-style | |
1633 // license that can be found in the LICENSE file. | |
1634 | |
1635 (function(scope) { | |
1636 'use strict'; | |
1637 | |
1638 var ChildNodeInterface = scope.ChildNodeInterface; | |
1639 var GetElementsByInterface = scope.GetElementsByInterface; | |
1640 var Node = scope.wrappers.Node; | |
1641 var ParentNodeInterface = scope.ParentNodeInterface; | |
1642 var SelectorsInterface = scope.SelectorsInterface; | |
1643 var addWrapNodeListMethod = scope.addWrapNodeListMethod; | |
1644 var mixin = scope.mixin; | |
1645 var registerWrapper = scope.registerWrapper; | |
1646 var wrappers = scope.wrappers; | |
1647 | |
1648 var shadowRootTable = new SideTable(); | |
1649 var OriginalElement = window.Element; | |
1650 | |
1651 var originalMatches = | |
1652 OriginalElement.prototype.matches || | |
1653 OriginalElement.prototype.mozMatchesSelector || | |
1654 OriginalElement.prototype.msMatchesSelector || | |
1655 OriginalElement.prototype.webkitMatchesSelector; | |
1656 | |
1657 function Element(node) { | |
1658 Node.call(this, node); | |
1659 } | |
1660 Element.prototype = Object.create(Node.prototype); | |
1661 mixin(Element.prototype, { | |
1662 createShadowRoot: function() { | |
1663 var newShadowRoot = new wrappers.ShadowRoot(this); | |
1664 shadowRootTable.set(this, newShadowRoot); | |
1665 | |
1666 scope.getRendererForHost(this); | |
1667 | |
1668 this.invalidateShadowRenderer(true); | |
1669 | |
1670 return newShadowRoot; | |
1671 }, | |
1672 | |
1673 get shadowRoot() { | |
1674 return shadowRootTable.get(this) || null; | |
1675 }, | |
1676 | |
1677 setAttribute: function(name, value) { | |
1678 this.impl.setAttribute(name, value); | |
1679 // This is a bit agressive. We need to invalidate if it affects | |
1680 // the rendering content[select] or if it effects the value of a content | |
1681 // select. | |
1682 this.invalidateShadowRenderer(); | |
1683 }, | |
1684 | |
1685 matches: function(selector) { | |
1686 return originalMatches.call(this.impl, selector); | |
1687 } | |
1688 }); | |
1689 | |
1690 mixin(Element.prototype, ChildNodeInterface); | |
1691 mixin(Element.prototype, GetElementsByInterface); | |
1692 mixin(Element.prototype, ParentNodeInterface); | |
1693 mixin(Element.prototype, SelectorsInterface); | |
1694 | |
1695 registerWrapper(OriginalElement, Element); | |
1696 | |
1697 scope.wrappers.Element = Element; | |
1698 })(this.ShadowDOMPolyfill); | |
1699 | |
1700 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1701 // Use of this source code is goverened by a BSD-style | |
1702 // license that can be found in the LICENSE file. | |
1703 | |
1704 (function(scope) { | |
1705 'use strict'; | |
1706 | |
1707 var Element = scope.wrappers.Element; | |
1708 var defineGetter = scope.defineGetter; | |
1709 var mixin = scope.mixin; | |
1710 var registerWrapper = scope.registerWrapper; | |
1711 var unwrap = scope.unwrap; | |
1712 var wrap = scope.wrap; | |
1713 | |
1714 ///////////////////////////////////////////////////////////////////////////// | |
1715 // innerHTML and outerHTML | |
1716 | |
1717 var escapeRegExp = /&|<|"/g; | |
1718 | |
1719 function escapeReplace(c) { | |
1720 switch (c) { | |
1721 case '&': | |
1722 return '&'; | |
1723 case '<': | |
1724 return '<'; | |
1725 case '"': | |
1726 return '"' | |
1727 } | |
1728 } | |
1729 | |
1730 function escape(s) { | |
1731 return s.replace(escapeRegExp, escapeReplace); | |
1732 } | |
1733 | |
1734 // http://www.whatwg.org/specs/web-apps/current-work/#void-elements | |
1735 var voidElements = { | |
1736 'area': true, | |
1737 'base': true, | |
1738 'br': true, | |
1739 'col': true, | |
1740 'command': true, | |
1741 'embed': true, | |
1742 'hr': true, | |
1743 'img': true, | |
1744 'input': true, | |
1745 'keygen': true, | |
1746 'link': true, | |
1747 'meta': true, | |
1748 'param': true, | |
1749 'source': true, | |
1750 'track': true, | |
1751 'wbr': true | |
1752 }; | |
1753 | |
1754 function getOuterHTML(node) { | |
1755 switch (node.nodeType) { | |
1756 case Node.ELEMENT_NODE: | |
1757 var tagName = node.tagName.toLowerCase(); | |
1758 var s = '<' + tagName; | |
1759 var attrs = node.attributes; | |
1760 for (var i = 0, attr; attr = attrs[i]; i++) { | |
1761 s += ' ' + attr.name + '="' + escape(attr.value) + '"'; | |
1762 } | |
1763 s += '>'; | |
1764 if (voidElements[tagName]) | |
1765 return s; | |
1766 | |
1767 return s + getInnerHTML(node) + '</' + tagName + '>'; | |
1768 | |
1769 case Node.TEXT_NODE: | |
1770 return escape(node.nodeValue); | |
1771 | |
1772 case Node.COMMENT_NODE: | |
1773 return '<!--' + escape(node.nodeValue) + '-->'; | |
1774 default: | |
1775 console.error(node); | |
1776 throw new Error('not implemented'); | |
1777 } | |
1778 } | |
1779 | |
1780 function getInnerHTML(node) { | |
1781 var s = ''; | |
1782 for (var child = node.firstChild; child; child = child.nextSibling) { | |
1783 s += getOuterHTML(child); | |
1784 } | |
1785 return s; | |
1786 } | |
1787 | |
1788 function setInnerHTML(node, value, opt_tagName) { | |
1789 var tagName = opt_tagName || 'div'; | |
1790 node.textContent = ''; | |
1791 var tempElement =unwrap(node.ownerDocument.createElement(tagName)); | |
1792 tempElement.innerHTML = value; | |
1793 var firstChild; | |
1794 while (firstChild = tempElement.firstChild) { | |
1795 node.appendChild(wrap(firstChild)); | |
1796 } | |
1797 } | |
1798 | |
1799 var OriginalHTMLElement = window.HTMLElement; | |
1800 | |
1801 function HTMLElement(node) { | |
1802 Element.call(this, node); | |
1803 } | |
1804 HTMLElement.prototype = Object.create(Element.prototype); | |
1805 mixin(HTMLElement.prototype, { | |
1806 get innerHTML() { | |
1807 // TODO(arv): This should fallback to this.impl.innerHTML if there | |
1808 // are no shadow trees below or above the context node. | |
1809 return getInnerHTML(this); | |
1810 }, | |
1811 set innerHTML(value) { | |
1812 setInnerHTML(this, value, this.tagName); | |
1813 }, | |
1814 | |
1815 get outerHTML() { | |
1816 // TODO(arv): This should fallback to HTMLElement_prototype.outerHTML if t
here | |
1817 // are no shadow trees below or above the context node. | |
1818 return getOuterHTML(this); | |
1819 }, | |
1820 set outerHTML(value) { | |
1821 if (!this.invalidateShadowRenderer()) { | |
1822 this.impl.outerHTML = value; | |
1823 } else { | |
1824 throw new Error('not implemented'); | |
1825 } | |
1826 } | |
1827 }); | |
1828 | |
1829 function getterRequiresRendering(name) { | |
1830 defineGetter(HTMLElement, name, function() { | |
1831 scope.renderAllPending(); | |
1832 return this.impl[name]; | |
1833 }); | |
1834 } | |
1835 | |
1836 [ | |
1837 'clientHeight', | |
1838 'clientLeft', | |
1839 'clientTop', | |
1840 'clientWidth', | |
1841 'offsetHeight', | |
1842 'offsetLeft', | |
1843 'offsetTop', | |
1844 'offsetWidth', | |
1845 'scrollHeight', | |
1846 'scrollLeft', | |
1847 'scrollTop', | |
1848 'scrollWidth', | |
1849 ].forEach(getterRequiresRendering); | |
1850 | |
1851 function methodRequiresRendering(name) { | |
1852 Object.defineProperty(HTMLElement.prototype, name, { | |
1853 value: function() { | |
1854 scope.renderAllPending(); | |
1855 return this.impl[name].apply(this.impl, arguments); | |
1856 }, | |
1857 configurable: true, | |
1858 enumerable: true | |
1859 }); | |
1860 } | |
1861 | |
1862 [ | |
1863 'getBoundingClientRect', | |
1864 'getClientRects', | |
1865 'scrollIntoView' | |
1866 ].forEach(methodRequiresRendering); | |
1867 | |
1868 // HTMLElement is abstract so we use a subclass that has no members. | |
1869 registerWrapper(OriginalHTMLElement, HTMLElement, | |
1870 document.createElement('b')); | |
1871 | |
1872 scope.wrappers.HTMLElement = HTMLElement; | |
1873 | |
1874 // TODO: Find a better way to share these two with WrapperShadowRoot. | |
1875 scope.getInnerHTML = getInnerHTML; | |
1876 scope.setInnerHTML = setInnerHTML | |
1877 })(this.ShadowDOMPolyfill); | |
1878 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1879 // Use of this source code is goverened by a BSD-style | |
1880 // license that can be found in the LICENSE file. | |
1881 | |
1882 (function(scope) { | |
1883 'use strict'; | |
1884 | |
1885 var HTMLElement = scope.wrappers.HTMLElement; | |
1886 var mixin = scope.mixin; | |
1887 var registerWrapper = scope.registerWrapper; | |
1888 | |
1889 var OriginalHTMLContentElement = window.HTMLContentElement; | |
1890 | |
1891 function HTMLContentElement(node) { | |
1892 HTMLElement.call(this, node); | |
1893 } | |
1894 HTMLContentElement.prototype = Object.create(HTMLElement.prototype); | |
1895 mixin(HTMLContentElement.prototype, { | |
1896 get select() { | |
1897 return this.getAttribute('select'); | |
1898 }, | |
1899 set select(value) { | |
1900 this.setAttribute('select', value); | |
1901 }, | |
1902 | |
1903 setAttribute: function(n, v) { | |
1904 HTMLElement.prototype.setAttribute.call(this, n, v); | |
1905 if (String(n).toLowerCase() === 'select') | |
1906 this.invalidateShadowRenderer(true); | |
1907 } | |
1908 | |
1909 // getDistributedNodes is added in ShadowRenderer | |
1910 | |
1911 // TODO: attribute boolean resetStyleInheritance; | |
1912 }); | |
1913 | |
1914 if (OriginalHTMLContentElement) | |
1915 registerWrapper(OriginalHTMLContentElement, HTMLContentElement); | |
1916 | |
1917 scope.wrappers.HTMLContentElement = HTMLContentElement; | |
1918 })(this.ShadowDOMPolyfill); | |
1919 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1920 // Use of this source code is goverened by a BSD-style | |
1921 // license that can be found in the LICENSE file. | |
1922 | |
1923 (function(scope) { | |
1924 'use strict'; | |
1925 | |
1926 var HTMLElement = scope.wrappers.HTMLElement; | |
1927 var mixin = scope.mixin; | |
1928 var registerWrapper = scope.registerWrapper; | |
1929 | |
1930 var OriginalHTMLShadowElement = window.HTMLShadowElement; | |
1931 | |
1932 function HTMLShadowElement(node) { | |
1933 HTMLElement.call(this, node); | |
1934 this.olderShadowRoot_ = null; | |
1935 } | |
1936 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype); | |
1937 mixin(HTMLShadowElement.prototype, { | |
1938 get olderShadowRoot() { | |
1939 return this.olderShadowRoot_; | |
1940 }, | |
1941 | |
1942 invalidateShadowRenderer: function() { | |
1943 HTMLElement.prototype.invalidateShadowRenderer.call(this, true); | |
1944 }, | |
1945 | |
1946 // TODO: attribute boolean resetStyleInheritance; | |
1947 }); | |
1948 | |
1949 if (OriginalHTMLShadowElement) | |
1950 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement); | |
1951 | |
1952 scope.wrappers.HTMLShadowElement = HTMLShadowElement; | |
1953 })(this.ShadowDOMPolyfill); | |
1954 // Copyright 2013 The Polymer Authors. All rights reserved. | |
1955 // Use of this source code is goverened by a BSD-style | |
1956 // license that can be found in the LICENSE file. | |
1957 | |
1958 (function(scope) { | |
1959 'use strict'; | |
1960 | |
1961 var HTMLElement = scope.wrappers.HTMLElement; | |
1962 var getInnerHTML = scope.getInnerHTML; | |
1963 var mixin = scope.mixin; | |
1964 var registerWrapper = scope.registerWrapper; | |
1965 var setInnerHTML = scope.setInnerHTML; | |
1966 var wrap = scope.wrap; | |
1967 | |
1968 var contentTable = new SideTable(); | |
1969 var templateContentsOwnerTable = new SideTable(); | |
1970 | |
1971 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner | |
1972 function getTemplateContentsOwner(doc) { | |
1973 if (!doc.defaultView) | |
1974 return doc; | |
1975 var d = templateContentsOwnerTable.get(doc); | |
1976 if (!d) { | |
1977 // TODO(arv): This should either be a Document or HTMLDocument depending | |
1978 // on doc. | |
1979 d = doc.implementation.createHTMLDocument(''); | |
1980 while (d.lastChild) { | |
1981 d.removeChild(d.lastChild); | |
1982 } | |
1983 templateContentsOwnerTable.set(doc, d); | |
1984 } | |
1985 return d; | |
1986 } | |
1987 | |
1988 function extractContent(templateElement) { | |
1989 var doc = getTemplateContentsOwner(templateElement.ownerDocument); | |
1990 var df = doc.createDocumentFragment(); | |
1991 var nextSibling; | |
1992 var child; | |
1993 while (child = templateElement.firstChild) { | |
1994 df.appendChild(child); | |
1995 } | |
1996 return df; | |
1997 } | |
1998 | |
1999 var OriginalHTMLTemplateElement = window.HTMLTemplateElement; | |
2000 | |
2001 function HTMLTemplateElement(node) { | |
2002 HTMLElement.call(this, node); | |
2003 } | |
2004 HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype); | |
2005 | |
2006 mixin(HTMLTemplateElement.prototype, { | |
2007 get content() { | |
2008 if (OriginalHTMLTemplateElement) | |
2009 return wrap(this.impl.content); | |
2010 | |
2011 // TODO(arv): This should be done in createCallback. I initially tried to | |
2012 // do this in the constructor but the wrapper is not yet created at that | |
2013 // point in time so we hit an iloop. | |
2014 var content = contentTable.get(this); | |
2015 if (!content) { | |
2016 content = extractContent(this); | |
2017 contentTable.set(this, content); | |
2018 } | |
2019 return content; | |
2020 }, | |
2021 | |
2022 get innerHTML() { | |
2023 return getInnerHTML(this.content); | |
2024 }, | |
2025 set innerHTML(value) { | |
2026 setInnerHTML(this.content, value); | |
2027 this.invalidateShadowRenderer(); | |
2028 } | |
2029 | |
2030 // TODO(arv): cloneNode needs to clone content. | |
2031 | |
2032 }); | |
2033 | |
2034 if (OriginalHTMLTemplateElement) | |
2035 registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement); | |
2036 | |
2037 scope.wrappers.HTMLTemplateElement = HTMLTemplateElement; | |
2038 })(this.ShadowDOMPolyfill); | |
2039 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2040 // Use of this source code is goverened by a BSD-style | |
2041 // license that can be found in the LICENSE file. | |
2042 | |
2043 (function(scope) { | |
2044 'use strict'; | |
2045 | |
2046 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
2047 var HTMLElement = scope.wrappers.HTMLElement; | |
2048 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | |
2049 var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement; | |
2050 var mixin = scope.mixin; | |
2051 var registerWrapper = scope.registerWrapper; | |
2052 | |
2053 var OriginalHTMLUnknownElement = window.HTMLUnknownElement; | |
2054 | |
2055 function HTMLUnknownElement(node) { | |
2056 switch (node.localName) { | |
2057 case 'content': | |
2058 return new HTMLContentElement(node); | |
2059 case 'shadow': | |
2060 return new HTMLShadowElement(node); | |
2061 case 'template': | |
2062 return new HTMLTemplateElement(node); | |
2063 } | |
2064 HTMLElement.call(this, node); | |
2065 } | |
2066 HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype); | |
2067 registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement); | |
2068 scope.wrappers.HTMLUnknownElement = HTMLUnknownElement; | |
2069 })(this.ShadowDOMPolyfill); | |
2070 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2071 // Use of this source code is goverened by a BSD-style | |
2072 // license that can be found in the LICENSE file. | |
2073 | |
2074 (function(scope) { | |
2075 'use strict'; | |
2076 | |
2077 var GetElementsByInterface = scope.GetElementsByInterface; | |
2078 var ParentNodeInterface = scope.ParentNodeInterface; | |
2079 var SelectorsInterface = scope.SelectorsInterface; | |
2080 var mixin = scope.mixin; | |
2081 var registerObject = scope.registerObject; | |
2082 | |
2083 var DocumentFragment = registerObject(document.createDocumentFragment()); | |
2084 mixin(DocumentFragment.prototype, ParentNodeInterface); | |
2085 mixin(DocumentFragment.prototype, SelectorsInterface); | |
2086 mixin(DocumentFragment.prototype, GetElementsByInterface); | |
2087 | |
2088 var Text = registerObject(document.createTextNode('')); | |
2089 var Comment = registerObject(document.createComment('')); | |
2090 | |
2091 scope.wrappers.Comment = Comment; | |
2092 scope.wrappers.DocumentFragment = DocumentFragment; | |
2093 scope.wrappers.Text = Text; | |
2094 | |
2095 })(this.ShadowDOMPolyfill); | |
2096 | |
2097 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2098 // Use of this source code is goverened by a BSD-style | |
2099 // license that can be found in the LICENSE file. | |
2100 | |
2101 (function(scope) { | |
2102 'use strict'; | |
2103 | |
2104 var DocumentFragment = scope.wrappers.DocumentFragment; | |
2105 var elementFromPoint = scope.elementFromPoint; | |
2106 var getInnerHTML = scope.getInnerHTML; | |
2107 var mixin = scope.mixin; | |
2108 var rewrap = scope.rewrap; | |
2109 var setInnerHTML = scope.setInnerHTML; | |
2110 var unwrap = scope.unwrap; | |
2111 | |
2112 var shadowHostTable = new SideTable(); | |
2113 | |
2114 function ShadowRoot(hostWrapper) { | |
2115 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); | |
2116 DocumentFragment.call(this, node); | |
2117 | |
2118 // createDocumentFragment associates the node with a wrapper | |
2119 // DocumentFragment instance. Override that. | |
2120 rewrap(node, this); | |
2121 | |
2122 var oldShadowRoot = hostWrapper.shadowRoot; | |
2123 scope.nextOlderShadowTreeTable.set(this, oldShadowRoot); | |
2124 | |
2125 shadowHostTable.set(this, hostWrapper); | |
2126 } | |
2127 ShadowRoot.prototype = Object.create(DocumentFragment.prototype); | |
2128 mixin(ShadowRoot.prototype, { | |
2129 get innerHTML() { | |
2130 return getInnerHTML(this); | |
2131 }, | |
2132 set innerHTML(value) { | |
2133 setInnerHTML(this, value); | |
2134 this.invalidateShadowRenderer(); | |
2135 }, | |
2136 | |
2137 invalidateShadowRenderer: function() { | |
2138 return shadowHostTable.get(this).invalidateShadowRenderer(); | |
2139 }, | |
2140 | |
2141 elementFromPoint: function(x, y) { | |
2142 return elementFromPoint(this, this.ownerDocument, x, y); | |
2143 }, | |
2144 | |
2145 getElementById: function(id) { | |
2146 return this.querySelector('#' + id); | |
2147 } | |
2148 }); | |
2149 | |
2150 scope.wrappers.ShadowRoot = ShadowRoot; | |
2151 scope.getHostForShadowRoot = function(node) { | |
2152 return shadowHostTable.get(node); | |
2153 }; | |
2154 })(this.ShadowDOMPolyfill); | |
2155 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2156 // Use of this source code is governed by a BSD-style | |
2157 // license that can be found in the LICENSE file. | |
2158 | |
2159 (function(scope) { | |
2160 'use strict'; | |
2161 | |
2162 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
2163 var Node = scope.wrappers.Node; | |
2164 var assert = scope.assert; | |
2165 var mixin = scope.mixin; | |
2166 var unwrap = scope.unwrap; | |
2167 var wrap = scope.wrap; | |
2168 | |
2169 /** | |
2170 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
2171 * Up means parentNode | |
2172 * Sideways means previous and next sibling. | |
2173 * @param {!Node} wrapper | |
2174 */ | |
2175 function updateWrapperUpAndSideways(wrapper) { | |
2176 wrapper.previousSibling_ = wrapper.previousSibling; | |
2177 wrapper.nextSibling_ = wrapper.nextSibling; | |
2178 wrapper.parentNode_ = wrapper.parentNode; | |
2179 } | |
2180 | |
2181 /** | |
2182 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
2183 * Down means first and last child | |
2184 * @param {!Node} wrapper | |
2185 */ | |
2186 function updateWrapperDown(wrapper) { | |
2187 wrapper.firstChild_ = wrapper.firstChild; | |
2188 wrapper.lastChild_ = wrapper.lastChild; | |
2189 } | |
2190 | |
2191 function updateAllChildNodes(parentNodeWrapper) { | |
2192 assert(parentNodeWrapper instanceof Node); | |
2193 for (var childWrapper = parentNodeWrapper.firstChild; | |
2194 childWrapper; | |
2195 childWrapper = childWrapper.nextSibling) { | |
2196 updateWrapperUpAndSideways(childWrapper); | |
2197 } | |
2198 updateWrapperDown(parentNodeWrapper); | |
2199 } | |
2200 | |
2201 // This object groups DOM operations. This is supposed to be the DOM as the | |
2202 // browser/render tree sees it. | |
2203 // When changes are done to the visual DOM the logical DOM needs to be updated | |
2204 // to reflect the correct tree. | |
2205 function removeAllChildNodes(parentNodeWrapper) { | |
2206 var parentNode = unwrap(parentNodeWrapper); | |
2207 updateAllChildNodes(parentNodeWrapper); | |
2208 parentNode.textContent = ''; | |
2209 } | |
2210 | |
2211 function appendChild(parentNodeWrapper, childWrapper) { | |
2212 var parentNode = unwrap(parentNodeWrapper); | |
2213 var child = unwrap(childWrapper); | |
2214 if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
2215 updateAllChildNodes(childWrapper); | |
2216 | |
2217 } else { | |
2218 remove(childWrapper); | |
2219 updateWrapperUpAndSideways(childWrapper); | |
2220 } | |
2221 | |
2222 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; | |
2223 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) | |
2224 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild; | |
2225 | |
2226 var lastChildWrapper = wrap(parentNode.lastChild); | |
2227 if (lastChildWrapper) { | |
2228 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling; | |
2229 } | |
2230 | |
2231 parentNode.appendChild(child); | |
2232 } | |
2233 | |
2234 function removeChild(parentNodeWrapper, childWrapper) { | |
2235 var parentNode = unwrap(parentNodeWrapper); | |
2236 var child = unwrap(childWrapper); | |
2237 | |
2238 updateWrapperUpAndSideways(childWrapper); | |
2239 | |
2240 if (childWrapper.previousSibling) | |
2241 childWrapper.previousSibling.nextSibling_ = childWrapper; | |
2242 if (childWrapper.nextSibling) | |
2243 childWrapper.nextSibling.previousSibling_ = childWrapper; | |
2244 | |
2245 if (parentNodeWrapper.lastChild === childWrapper) | |
2246 parentNodeWrapper.lastChild_ = childWrapper; | |
2247 if (parentNodeWrapper.firstChild === childWrapper) | |
2248 parentNodeWrapper.firstChild_ = childWrapper; | |
2249 | |
2250 parentNode.removeChild(child); | |
2251 } | |
2252 | |
2253 function remove(nodeWrapper) { | |
2254 var node = unwrap(nodeWrapper) | |
2255 var parentNode = node.parentNode; | |
2256 if (parentNode) | |
2257 removeChild(wrap(parentNode), nodeWrapper); | |
2258 } | |
2259 | |
2260 var distributedChildNodesTable = new SideTable(); | |
2261 var eventParentsTable = new SideTable(); | |
2262 var insertionParentTable = new SideTable(); | |
2263 var nextOlderShadowTreeTable = new SideTable(); | |
2264 var rendererForHostTable = new SideTable(); | |
2265 var shadowDOMRendererTable = new SideTable(); | |
2266 | |
2267 var reprCounter = 0; | |
2268 | |
2269 function repr(node) { | |
2270 if (!node.displayName) | |
2271 node.displayName = node.nodeName + '-' + ++reprCounter; | |
2272 return node.displayName; | |
2273 } | |
2274 | |
2275 function distributeChildToInsertionPoint(child, insertionPoint) { | |
2276 getDistributedChildNodes(insertionPoint).push(child); | |
2277 insertionParentTable.set(child, insertionPoint); | |
2278 | |
2279 var eventParents = eventParentsTable.get(child); | |
2280 if (!eventParents) | |
2281 eventParentsTable.set(child, eventParents = []); | |
2282 eventParents.push(insertionPoint); | |
2283 } | |
2284 | |
2285 function resetDistributedChildNodes(insertionPoint) { | |
2286 distributedChildNodesTable.set(insertionPoint, []); | |
2287 } | |
2288 | |
2289 function getDistributedChildNodes(insertionPoint) { | |
2290 return distributedChildNodesTable.get(insertionPoint); | |
2291 } | |
2292 | |
2293 function getChildNodesSnapshot(node) { | |
2294 var result = [], i = 0; | |
2295 for (var child = node.firstChild; child; child = child.nextSibling) { | |
2296 result[i++] = child; | |
2297 } | |
2298 return result; | |
2299 } | |
2300 | |
2301 /** | |
2302 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor| | |
2303 * function returns |false| the traversal is aborted. | |
2304 * @param {!Node} tree | |
2305 * @param {function(!Node) : boolean} predicate | |
2306 * @param {function(!Node) : *} visitor | |
2307 */ | |
2308 function visit(tree, predicate, visitor) { | |
2309 // This operates on logical DOM. | |
2310 var nodes = getChildNodesSnapshot(tree); | |
2311 for (var i = 0; i < nodes.length; i++) { | |
2312 var node = nodes[i]; | |
2313 if (predicate(node)) { | |
2314 if (visitor(node) === false) | |
2315 return; | |
2316 } else { | |
2317 visit(node, predicate, visitor); | |
2318 } | |
2319 } | |
2320 } | |
2321 | |
2322 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#dfn
-distribution-algorithm | |
2323 function distribute(tree, pool) { | |
2324 var anyRemoved = false; | |
2325 | |
2326 visit(tree, isActiveInsertionPoint, | |
2327 function(insertionPoint) { | |
2328 resetDistributedChildNodes(insertionPoint); | |
2329 for (var i = 0; i < pool.length; i++) { // 1.2 | |
2330 var node = pool[i]; // 1.2.1 | |
2331 if (node === undefined) // removed | |
2332 continue; | |
2333 if (matchesCriteria(node, insertionPoint)) { // 1.2.2 | |
2334 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2.1 | |
2335 pool[i] = undefined; // 1.2.2.2 | |
2336 anyRemoved = true; | |
2337 } | |
2338 } | |
2339 }); | |
2340 | |
2341 if (!anyRemoved) | |
2342 return pool; | |
2343 | |
2344 return pool.filter(function(item) { | |
2345 return item !== undefined; | |
2346 }); | |
2347 } | |
2348 | |
2349 // Matching Insertion Points | |
2350 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#mat
ching-insertion-points | |
2351 | |
2352 // TODO(arv): Verify this... I don't remember why I picked this regexp. | |
2353 var selectorMatchRegExp = /^[*.:#[a-zA-Z_|]/; | |
2354 | |
2355 var allowedPseudoRegExp = new RegExp('^:(' + [ | |
2356 'link', | |
2357 'visited', | |
2358 'target', | |
2359 'enabled', | |
2360 'disabled', | |
2361 'checked', | |
2362 'indeterminate', | |
2363 'nth-child', | |
2364 'nth-last-child', | |
2365 'nth-of-type', | |
2366 'nth-last-of-type', | |
2367 'first-child', | |
2368 'last-child', | |
2369 'first-of-type', | |
2370 'last-of-type', | |
2371 'only-of-type', | |
2372 ].join('|') + ')'); | |
2373 | |
2374 | |
2375 function oneOf(object, propertyNames) { | |
2376 for (var i = 0; i < propertyNames.length; i++) { | |
2377 if (propertyNames[i] in object) | |
2378 return propertyNames[i]; | |
2379 } | |
2380 } | |
2381 | |
2382 /** | |
2383 * @param {Element} node | |
2384 * @oaram {Element} point The insertion point element. | |
2385 * @return {boolean} Whether the node matches the insertion point. | |
2386 */ | |
2387 function matchesCriteria(node, point) { | |
2388 var select = point.getAttribute('select'); | |
2389 if (!select) | |
2390 return true; | |
2391 | |
2392 // Here we know the select attribute is a non empty string. | |
2393 select = select.trim(); | |
2394 if (!select) | |
2395 return true; | |
2396 | |
2397 if (node.nodeType !== Node.ELEMENT_NODE) | |
2398 return false; | |
2399 | |
2400 // TODO(arv): This does not seem right. Need to check for a simple selector. | |
2401 if (!selectorMatchRegExp.test(select)) | |
2402 return false; | |
2403 | |
2404 if (select[0] === ':' &&!allowedPseudoRegExp.test(select)) | |
2405 return false; | |
2406 | |
2407 try { | |
2408 return node.matches(select); | |
2409 } catch (ex) { | |
2410 // Invalid selector. | |
2411 return false; | |
2412 } | |
2413 } | |
2414 | |
2415 var request = oneOf(window, [ | |
2416 'requestAnimationFrame', | |
2417 'mozRequestAnimationFrame', | |
2418 'webkitRequestAnimationFrame', | |
2419 'setTimeout' | |
2420 ]); | |
2421 | |
2422 var pendingDirtyRenderers = []; | |
2423 var renderTimer; | |
2424 | |
2425 function renderAllPending() { | |
2426 renderTimer = null; | |
2427 pendingDirtyRenderers.forEach(function(owner) { | |
2428 owner.render(); | |
2429 }); | |
2430 pendingDirtyRenderers = []; | |
2431 } | |
2432 | |
2433 function ShadowRenderer(host) { | |
2434 this.host = host; | |
2435 this.dirty = false; | |
2436 this.associateNode(host); | |
2437 } | |
2438 | |
2439 function getRendererForHost(host) { | |
2440 var renderer = rendererForHostTable.get(host); | |
2441 if (!renderer) { | |
2442 renderer = new ShadowRenderer(host); | |
2443 rendererForHostTable.set(host, renderer); | |
2444 } | |
2445 return renderer; | |
2446 } | |
2447 | |
2448 ShadowRenderer.prototype = { | |
2449 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees | |
2450 render: function() { | |
2451 if (!this.dirty) | |
2452 return; | |
2453 | |
2454 var host = this.host; | |
2455 this.treeComposition(); | |
2456 var shadowDOM = host.shadowRoot; | |
2457 if (!shadowDOM) | |
2458 return; | |
2459 | |
2460 this.removeAllChildNodes(this.host); | |
2461 | |
2462 var shadowDOMChildNodes = getChildNodesSnapshot(shadowDOM); | |
2463 shadowDOMChildNodes.forEach(function(node) { | |
2464 this.renderNode(host, shadowDOM, node, false); | |
2465 }, this); | |
2466 | |
2467 this.dirty = false; | |
2468 }, | |
2469 | |
2470 invalidate: function() { | |
2471 if (!this.dirty) { | |
2472 this.dirty = true; | |
2473 pendingDirtyRenderers.push(this); | |
2474 if (renderTimer) | |
2475 return; | |
2476 renderTimer = window[request](renderAllPending, 0); | |
2477 } | |
2478 }, | |
2479 | |
2480 renderNode: function(visualParent, tree, node, isNested) { | |
2481 if (isShadowHost(node)) { | |
2482 this.appendChild(visualParent, node); | |
2483 var renderer = getRendererForHost(node); | |
2484 renderer.dirty = true; // Need to rerender due to reprojection. | |
2485 renderer.render(); | |
2486 } else if (isInsertionPoint(node)) { | |
2487 this.renderInsertionPoint(visualParent, tree, node, isNested); | |
2488 } else if (isShadowInsertionPoint(node)) { | |
2489 this.renderShadowInsertionPoint(visualParent, tree, node); | |
2490 } else { | |
2491 this.renderAsAnyDomTree(visualParent, tree, node, isNested); | |
2492 } | |
2493 }, | |
2494 | |
2495 renderAsAnyDomTree: function(visualParent, tree, child, isNested) { | |
2496 this.appendChild(visualParent, child); | |
2497 | |
2498 if (isShadowHost(child)) { | |
2499 render(child); | |
2500 } else { | |
2501 var parent = child; | |
2502 var logicalChildNodes = getChildNodesSnapshot(parent); | |
2503 logicalChildNodes.forEach(function(node) { | |
2504 this.renderNode(parent, tree, node, isNested); | |
2505 }, this); | |
2506 } | |
2507 }, | |
2508 | |
2509 renderInsertionPoint: function(visualParent, tree, insertionPoint, isNested)
{ | |
2510 var distributedChildNodes = getDistributedChildNodes(insertionPoint); | |
2511 if (distributedChildNodes.length) { | |
2512 this.removeAllChildNodes(insertionPoint); | |
2513 | |
2514 distributedChildNodes.forEach(function(child) { | |
2515 if (isInsertionPoint(child) && isNested) | |
2516 this.renderInsertionPoint(visualParent, tree, child, isNested); | |
2517 else | |
2518 this.renderAsAnyDomTree(visualParent, tree, child, isNested); | |
2519 }, this); | |
2520 } else { | |
2521 this.renderFallbackContent(visualParent, insertionPoint); | |
2522 } | |
2523 this.remove(insertionPoint); | |
2524 }, | |
2525 | |
2526 renderShadowInsertionPoint: function(visualParent, tree, shadowInsertionPoin
t) { | |
2527 var nextOlderTree = getNextOlderTree(tree); | |
2528 if (nextOlderTree) { | |
2529 // This makes ShadowRoot have its insertionParent be the <shadow>. | |
2530 insertionParentTable.set(nextOlderTree, shadowInsertionPoint); | |
2531 shadowInsertionPoint.olderShadowRoot_ = nextOlderTree; | |
2532 this.remove(shadowInsertionPoint); | |
2533 var shadowDOMChildNodes = getChildNodesSnapshot(nextOlderTree); | |
2534 shadowDOMChildNodes.forEach(function(node) { | |
2535 this.renderNode(visualParent, nextOlderTree, node, true); | |
2536 }, this); | |
2537 } else { | |
2538 this.renderFallbackContent(visualParent, shadowInsertionPoint); | |
2539 } | |
2540 }, | |
2541 | |
2542 renderFallbackContent: function (visualParent, fallbackHost) { | |
2543 var logicalChildNodes = getChildNodesSnapshot(fallbackHost); | |
2544 logicalChildNodes.forEach(function(node) { | |
2545 this.appendChild(visualParent, node); | |
2546 }, this); | |
2547 }, | |
2548 | |
2549 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-tree-composition | |
2550 treeComposition: function () { | |
2551 var shadowHost = this.host; | |
2552 var tree = shadowHost.shadowRoot; // 1. | |
2553 var pool = []; // 2. | |
2554 var shadowHostChildNodes = getChildNodesSnapshot(shadowHost); | |
2555 shadowHostChildNodes.forEach(function(child) { // 3. | |
2556 if (isInsertionPoint(child)) { // 3.2. | |
2557 var reprojected = getDistributedChildNodes(child); // 3.2.1. | |
2558 // if reprojected is undef... reset it? | |
2559 if (!reprojected || !reprojected.length) // 3.2.2. | |
2560 reprojected = getChildNodesSnapshot(child); | |
2561 pool.push.apply(pool, reprojected); // 3.2.3. | |
2562 } else { | |
2563 pool.push(child); // 3.3. | |
2564 } | |
2565 }); | |
2566 | |
2567 var shadowInsertionPoint, point; | |
2568 while (tree) { // 4. | |
2569 // 4.1. | |
2570 shadowInsertionPoint = undefined; // Reset every iteration. | |
2571 visit(tree, isActiveShadowInsertionPoint, function(point) { | |
2572 shadowInsertionPoint = point; | |
2573 return false; | |
2574 }); | |
2575 point = shadowInsertionPoint; | |
2576 | |
2577 pool = distribute(tree, pool); // 4.2. | |
2578 if (point) { // 4.3. | |
2579 var nextOlderTree = getNextOlderTree(tree); // 4.3.1. | |
2580 if (!nextOlderTree) { | |
2581 break; // 4.3.1.1. | |
2582 } else { | |
2583 tree = nextOlderTree; // 4.3.2.2. | |
2584 assignShadowTreeToShadowInsertionPoint(tree, point); // 4.3.2.2. | |
2585 continue; // 4.3.2.3. | |
2586 } | |
2587 } else { | |
2588 break; // 4.4. | |
2589 } | |
2590 } | |
2591 }, | |
2592 | |
2593 // Visual DOM mutation. | |
2594 appendChild: function(parent, child) { | |
2595 appendChild(parent, child); | |
2596 this.associateNode(child); | |
2597 }, | |
2598 | |
2599 remove: function(node) { | |
2600 remove(node); | |
2601 this.associateNode(node); | |
2602 }, | |
2603 | |
2604 removeAllChildNodes: function(parent) { | |
2605 removeAllChildNodes(parent); | |
2606 // TODO(arv): Does this need to associate all the nodes with this renderer
? | |
2607 }, | |
2608 | |
2609 associateNode: function(node) { | |
2610 // TODO: Clear when moved out of shadow tree. | |
2611 shadowDOMRendererTable.set(node, this); | |
2612 } | |
2613 }; | |
2614 | |
2615 function isInsertionPoint(node) { | |
2616 // Should this include <shadow>? | |
2617 return node.localName === 'content'; | |
2618 } | |
2619 | |
2620 function isActiveInsertionPoint(node) { | |
2621 // <content> inside another <content> or <shadow> is considered inactive. | |
2622 return node.localName === 'content'; | |
2623 } | |
2624 | |
2625 function isShadowInsertionPoint(node) { | |
2626 return node.localName === 'shadow'; | |
2627 } | |
2628 | |
2629 function isActiveShadowInsertionPoint(node) { | |
2630 // <shadow> inside another <content> or <shadow> is considered inactive. | |
2631 return node.localName === 'shadow'; | |
2632 } | |
2633 | |
2634 function isShadowHost(shadowHost) { | |
2635 return !!shadowHost.shadowRoot; | |
2636 } | |
2637 | |
2638 /** | |
2639 * @param {WrapperShadowRoot} tree | |
2640 */ | |
2641 function getNextOlderTree(tree) { | |
2642 return nextOlderShadowTreeTable.get(tree); | |
2643 } | |
2644 | |
2645 function getShadowTrees(host) { | |
2646 var trees = []; | |
2647 | |
2648 for (var tree = host.shadowRoot; | |
2649 tree; | |
2650 tree = nextOlderShadowTreeTable.get(tree)) { | |
2651 trees.push(tree); | |
2652 } | |
2653 return trees; | |
2654 } | |
2655 | |
2656 function assignShadowTreeToShadowInsertionPoint(tree, point) { | |
2657 insertionParentTable.set(tree, point); | |
2658 } | |
2659 | |
2660 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#ren
dering-shadow-trees | |
2661 function render(host) { | |
2662 new ShadowRenderer(host).render(); | |
2663 }; | |
2664 | |
2665 Node.prototype.invalidateShadowRenderer = function(force) { | |
2666 // TODO: If this is in light DOM we only need to invalidate renderer if this | |
2667 // is a direct child of a ShadowRoot. | |
2668 // Maybe we should only associate renderers with direct child nodes of a | |
2669 // shadow root (and all nodes in the shadow dom). | |
2670 var renderer = shadowDOMRendererTable.get(this); | |
2671 if (!renderer) | |
2672 return false; | |
2673 | |
2674 var p; | |
2675 if (force || this.shadowRoot || | |
2676 (p = this.parentNode) && (p.shadowRoot || p instanceof ShadowRoot)) { | |
2677 renderer.invalidate(); | |
2678 } | |
2679 | |
2680 return true; | |
2681 }; | |
2682 | |
2683 HTMLContentElement.prototype.getDistributedNodes = function() { | |
2684 // TODO(arv): We should associate the element with the shadow root so we | |
2685 // only have to rerender this ShadowRenderer. | |
2686 renderAllPending(); | |
2687 return getDistributedChildNodes(this); | |
2688 }; | |
2689 | |
2690 mixin(Node.prototype, { | |
2691 get insertionParent() { | |
2692 return insertionParentTable.get(this) || null; | |
2693 } | |
2694 }); | |
2695 | |
2696 scope.eventParentsTable = eventParentsTable; | |
2697 scope.getRendererForHost = getRendererForHost; | |
2698 scope.getShadowTrees = getShadowTrees; | |
2699 scope.nextOlderShadowTreeTable = nextOlderShadowTreeTable; | |
2700 scope.renderAllPending = renderAllPending; | |
2701 | |
2702 // Exposed for testing | |
2703 scope.visual = { | |
2704 removeAllChildNodes: removeAllChildNodes, | |
2705 appendChild: appendChild, | |
2706 removeChild: removeChild | |
2707 }; | |
2708 | |
2709 })(this.ShadowDOMPolyfill); | |
2710 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2711 // Use of this source code is goverened by a BSD-style | |
2712 // license that can be found in the LICENSE file. | |
2713 | |
2714 (function(scope) { | |
2715 'use strict'; | |
2716 | |
2717 var GetElementsByInterface = scope.GetElementsByInterface; | |
2718 var Node = scope.wrappers.Node; | |
2719 var ParentNodeInterface = scope.ParentNodeInterface; | |
2720 var SelectorsInterface = scope.SelectorsInterface; | |
2721 var defineWrapGetter = scope.defineWrapGetter; | |
2722 var elementFromPoint = scope.elementFromPoint; | |
2723 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
2724 var mixin = scope.mixin; | |
2725 var registerWrapper = scope.registerWrapper; | |
2726 var unwrap = scope.unwrap; | |
2727 var wrap = scope.wrap; | |
2728 var wrapEventTargetMethods = scope.wrapEventTargetMethods; | |
2729 var wrapNodeList = scope.wrapNodeList; | |
2730 | |
2731 var implementationTable = new SideTable(); | |
2732 | |
2733 function Document(node) { | |
2734 Node.call(this, node); | |
2735 } | |
2736 Document.prototype = Object.create(Node.prototype); | |
2737 | |
2738 defineWrapGetter(Document, 'documentElement'); | |
2739 | |
2740 // Conceptually both body and head can be in a shadow but suporting that seems | |
2741 // overkill at this point. | |
2742 defineWrapGetter(Document, 'body'); | |
2743 defineWrapGetter(Document, 'head'); | |
2744 | |
2745 // document cannot be overridden so we override a bunch of its methods | |
2746 // directly on the instance. | |
2747 | |
2748 function wrapMethod(name) { | |
2749 var original = document[name]; | |
2750 Document.prototype[name] = function() { | |
2751 return wrap(original.apply(this.impl, arguments)); | |
2752 }; | |
2753 } | |
2754 | |
2755 [ | |
2756 'getElementById', | |
2757 'createElement', | |
2758 'createElementNS', | |
2759 'createTextNode', | |
2760 'createDocumentFragment', | |
2761 'createEvent', | |
2762 'createEventNS', | |
2763 ].forEach(wrapMethod); | |
2764 | |
2765 var originalAdoptNode = document.adoptNode; | |
2766 var originalWrite = document.write; | |
2767 | |
2768 mixin(Document.prototype, { | |
2769 adoptNode: function(node) { | |
2770 originalAdoptNode.call(this.impl, unwrap(node)); | |
2771 return node; | |
2772 }, | |
2773 elementFromPoint: function(x, y) { | |
2774 return elementFromPoint(this, this, x, y); | |
2775 }, | |
2776 write: function(s) { | |
2777 var all = this.querySelectorAll('*'); | |
2778 var last = all[all.length - 1]; | |
2779 while (last.nextSibling) { | |
2780 last = last.nextSibling; | |
2781 } | |
2782 var p = last.parentNode; | |
2783 p.lastChild_ = undefined; | |
2784 last.nextSibling_ = undefined; | |
2785 originalWrite.call(this.impl, s); | |
2786 } | |
2787 }); | |
2788 | |
2789 // We also override some of the methods on document.body and document.head | |
2790 // for convenience. | |
2791 forwardMethodsToWrapper([ | |
2792 window.HTMLBodyElement, | |
2793 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
2794 window.HTMLHeadElement, | |
2795 ], [ | |
2796 'appendChild', | |
2797 'compareDocumentPosition', | |
2798 'getElementsByClassName', | |
2799 'getElementsByTagName', | |
2800 'getElementsByTagNameNS', | |
2801 'insertBefore', | |
2802 'querySelector', | |
2803 'querySelectorAll', | |
2804 'removeChild', | |
2805 'replaceChild', | |
2806 ]); | |
2807 | |
2808 forwardMethodsToWrapper([ | |
2809 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
2810 ], [ | |
2811 'adoptNode', | |
2812 'createDocumentFragment', | |
2813 'createElement', | |
2814 'createElementNS', | |
2815 'createEvent', | |
2816 'createEventNS', | |
2817 'createTextNode', | |
2818 'elementFromPoint', | |
2819 'getElementById', | |
2820 'write', | |
2821 ]); | |
2822 | |
2823 mixin(Document.prototype, GetElementsByInterface); | |
2824 mixin(Document.prototype, ParentNodeInterface); | |
2825 mixin(Document.prototype, SelectorsInterface); | |
2826 | |
2827 mixin(Document.prototype, { | |
2828 get implementation() { | |
2829 var implementation = implementationTable.get(this); | |
2830 if (implementation) | |
2831 return implementation; | |
2832 implementation = | |
2833 new DOMImplementation(unwrap(this).implementation); | |
2834 implementationTable.set(this, implementation); | |
2835 return implementation; | |
2836 } | |
2837 }); | |
2838 | |
2839 registerWrapper(window.Document, Document, | |
2840 document.implementation.createHTMLDocument('')); | |
2841 | |
2842 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has | |
2843 // one Document interface and IE implements the standard correctly. | |
2844 if (window.HTMLDocument) | |
2845 registerWrapper(window.HTMLDocument, Document); | |
2846 | |
2847 wrapEventTargetMethods([ | |
2848 window.HTMLBodyElement, | |
2849 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
2850 window.HTMLHeadElement, | |
2851 ]); | |
2852 | |
2853 function DOMImplementation(impl) { | |
2854 this.impl = impl; | |
2855 } | |
2856 | |
2857 function wrapImplMethod(constructor, name) { | |
2858 var original = document.implementation[name]; | |
2859 constructor.prototype[name] = function() { | |
2860 return wrap(original.apply(this.impl, arguments)); | |
2861 }; | |
2862 } | |
2863 | |
2864 function forwardImplMethod(constructor, name) { | |
2865 var original = document.implementation[name]; | |
2866 constructor.prototype[name] = function() { | |
2867 return original.apply(this.impl, arguments); | |
2868 }; | |
2869 } | |
2870 | |
2871 wrapImplMethod(DOMImplementation, 'createDocumentType'); | |
2872 wrapImplMethod(DOMImplementation, 'createDocument'); | |
2873 wrapImplMethod(DOMImplementation, 'createHTMLDocument'); | |
2874 forwardImplMethod(DOMImplementation, 'hasFeature'); | |
2875 | |
2876 registerWrapper(window.DOMImplementation, DOMImplementation); | |
2877 | |
2878 forwardMethodsToWrapper([ | |
2879 window.DOMImplementation, | |
2880 ], [ | |
2881 'createDocumentType', | |
2882 'createDocument', | |
2883 'createHTMLDocument', | |
2884 'hasFeature', | |
2885 ]); | |
2886 | |
2887 scope.wrappers.Document = Document; | |
2888 scope.wrappers.DOMImplementation = DOMImplementation; | |
2889 | |
2890 })(this.ShadowDOMPolyfill); | |
2891 | |
2892 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2893 // Use of this source code is goverened by a BSD-style | |
2894 // license that can be found in the LICENSE file. | |
2895 | |
2896 (function(scope) { | |
2897 'use strict'; | |
2898 | |
2899 var EventTarget = scope.wrappers.EventTarget; | |
2900 var mixin = scope.mixin; | |
2901 var registerWrapper = scope.registerWrapper; | |
2902 var unwrap = scope.unwrap; | |
2903 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
2904 var wrap = scope.wrap; | |
2905 | |
2906 var OriginalWindow = window.Window; | |
2907 | |
2908 function Window(impl) { | |
2909 EventTarget.call(this, impl); | |
2910 } | |
2911 Window.prototype = Object.create(EventTarget.prototype); | |
2912 | |
2913 var originalGetComputedStyle = window.getComputedStyle; | |
2914 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { | |
2915 return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el), | |
2916 pseudo); | |
2917 }; | |
2918 | |
2919 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( | |
2920 function(name) { | |
2921 OriginalWindow.prototype[name] = function() { | |
2922 var w = wrap(this || window); | |
2923 return w[name].apply(w, arguments); | |
2924 }; | |
2925 }); | |
2926 | |
2927 mixin(Window.prototype, { | |
2928 getComputedStyle: function(el, pseudo) { | |
2929 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), | |
2930 pseudo); | |
2931 } | |
2932 }); | |
2933 | |
2934 registerWrapper(OriginalWindow, Window); | |
2935 | |
2936 scope.wrappers.Window = Window; | |
2937 | |
2938 })(this.ShadowDOMPolyfill); | |
2939 | |
2940 // Copyright 2013 The Polymer Authors. All rights reserved. | |
2941 // Use of this source code is goverened by a BSD-style | |
2942 // license that can be found in the LICENSE file. | |
2943 | |
2944 (function(scope) { | |
2945 'use strict'; | |
2946 | |
2947 var defineGetter = scope.defineGetter; | |
2948 var defineWrapGetter = scope.defineWrapGetter; | |
2949 var registerWrapper = scope.registerWrapper; | |
2950 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
2951 var wrapNodeList = scope.wrapNodeList; | |
2952 var wrappers = scope.wrappers; | |
2953 | |
2954 var OriginalMutationObserver = window.MutationObserver || | |
2955 window.WebKitMutationObserver; | |
2956 | |
2957 if (!OriginalMutationObserver) | |
2958 return; | |
2959 | |
2960 var OriginalMutationRecord = window.MutationRecord; | |
2961 | |
2962 function MutationRecord(impl) { | |
2963 this.impl = impl; | |
2964 } | |
2965 | |
2966 MutationRecord.prototype = { | |
2967 get addedNodes() { | |
2968 return wrapNodeList(this.impl.addedNodes); | |
2969 }, | |
2970 get removedNodes() { | |
2971 return wrapNodeList(this.impl.removedNodes); | |
2972 } | |
2973 }; | |
2974 | |
2975 ['target', 'previousSibling', 'nextSibling'].forEach(function(name) { | |
2976 defineWrapGetter(MutationRecord, name); | |
2977 }); | |
2978 | |
2979 // WebKit/Blink treats these as instance properties so we override | |
2980 [ | |
2981 'type', | |
2982 'attributeName', | |
2983 'attributeNamespace', | |
2984 'oldValue' | |
2985 ].forEach(function(name) { | |
2986 defineGetter(MutationRecord, name, function() { | |
2987 return this.impl[name]; | |
2988 }); | |
2989 }); | |
2990 | |
2991 if (OriginalMutationRecord) | |
2992 registerWrapper(OriginalMutationRecord, MutationRecord); | |
2993 | |
2994 function wrapRecord(record) { | |
2995 return new MutationRecord(record); | |
2996 } | |
2997 | |
2998 function wrapRecords(records) { | |
2999 return records.map(wrapRecord); | |
3000 } | |
3001 | |
3002 function MutationObserver(callback) { | |
3003 var self = this; | |
3004 this.impl = new OriginalMutationObserver(function(mutations, observer) { | |
3005 callback.call(self, wrapRecords(mutations), self); | |
3006 }); | |
3007 } | |
3008 | |
3009 var OriginalNode = window.Node; | |
3010 | |
3011 MutationObserver.prototype = { | |
3012 observe: function(target, options) { | |
3013 this.impl.observe(unwrapIfNeeded(target), options); | |
3014 }, | |
3015 disconnect: function() { | |
3016 this.impl.disconnect(); | |
3017 }, | |
3018 takeRecords: function() { | |
3019 return wrapRecords(this.impl.takeRecords()); | |
3020 } | |
3021 }; | |
3022 | |
3023 scope.wrappers.MutationObserver = MutationObserver; | |
3024 scope.wrappers.MutationRecord = MutationRecord; | |
3025 | |
3026 })(this.ShadowDOMPolyfill); | |
3027 | |
3028 // Copyright 2013 The Polymer Authors. All rights reserved. | |
3029 // Use of this source code is goverened by a BSD-style | |
3030 // license that can be found in the LICENSE file. | |
3031 | |
3032 (function(scope) { | |
3033 'use strict'; | |
3034 | |
3035 var isWrapperFor = scope.isWrapperFor; | |
3036 | |
3037 // This is a list of the elements we currently override the global constructor | |
3038 // for. | |
3039 var elements = { | |
3040 'a': 'HTMLAnchorElement', | |
3041 'applet': 'HTMLAppletElement', | |
3042 'area': 'HTMLAreaElement', | |
3043 'audio': 'HTMLAudioElement', | |
3044 'br': 'HTMLBRElement', | |
3045 'base': 'HTMLBaseElement', | |
3046 'body': 'HTMLBodyElement', | |
3047 'button': 'HTMLButtonElement', | |
3048 'canvas': 'HTMLCanvasElement', | |
3049 // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko. | |
3050 'dl': 'HTMLDListElement', | |
3051 'datalist': 'HTMLDataListElement', | |
3052 'dir': 'HTMLDirectoryElement', | |
3053 'div': 'HTMLDivElement', | |
3054 'embed': 'HTMLEmbedElement', | |
3055 'fieldset': 'HTMLFieldSetElement', | |
3056 'font': 'HTMLFontElement', | |
3057 'form': 'HTMLFormElement', | |
3058 'frame': 'HTMLFrameElement', | |
3059 'frameset': 'HTMLFrameSetElement', | |
3060 'hr': 'HTMLHRElement', | |
3061 'head': 'HTMLHeadElement', | |
3062 'h1': 'HTMLHeadingElement', | |
3063 'html': 'HTMLHtmlElement', | |
3064 'iframe': 'HTMLIFrameElement', | |
3065 | |
3066 // Uses HTMLSpanElement in Firefox. | |
3067 // https://bugzilla.mozilla.org/show_bug.cgi?id=843881 | |
3068 // 'image', | |
3069 | |
3070 'input': 'HTMLInputElement', | |
3071 'li': 'HTMLLIElement', | |
3072 'label': 'HTMLLabelElement', | |
3073 'legend': 'HTMLLegendElement', | |
3074 'link': 'HTMLLinkElement', | |
3075 'map': 'HTMLMapElement', | |
3076 // 'media', Covered by audio and video | |
3077 'menu': 'HTMLMenuElement', | |
3078 'menuitem': 'HTMLMenuItemElement', | |
3079 'meta': 'HTMLMetaElement', | |
3080 'meter': 'HTMLMeterElement', | |
3081 'del': 'HTMLModElement', | |
3082 'ol': 'HTMLOListElement', | |
3083 'object': 'HTMLObjectElement', | |
3084 'optgroup': 'HTMLOptGroupElement', | |
3085 'option': 'HTMLOptionElement', | |
3086 'output': 'HTMLOutputElement', | |
3087 'p': 'HTMLParagraphElement', | |
3088 'param': 'HTMLParamElement', | |
3089 'pre': 'HTMLPreElement', | |
3090 'progress': 'HTMLProgressElement', | |
3091 'q': 'HTMLQuoteElement', | |
3092 'script': 'HTMLScriptElement', | |
3093 'select': 'HTMLSelectElement', | |
3094 'source': 'HTMLSourceElement', | |
3095 'span': 'HTMLSpanElement', | |
3096 'style': 'HTMLStyleElement', | |
3097 'caption': 'HTMLTableCaptionElement', | |
3098 // WebKit and Moz are wrong: | |
3099 // https://bugs.webkit.org/show_bug.cgi?id=111469 | |
3100 // https://bugzilla.mozilla.org/show_bug.cgi?id=848096 | |
3101 // 'td': 'HTMLTableCellElement', | |
3102 'col': 'HTMLTableColElement', | |
3103 'table': 'HTMLTableElement', | |
3104 'tr': 'HTMLTableRowElement', | |
3105 'thead': 'HTMLTableSectionElement', | |
3106 'tbody': 'HTMLTableSectionElement', | |
3107 'textarea': 'HTMLTextAreaElement', | |
3108 'title': 'HTMLTitleElement', | |
3109 'ul': 'HTMLUListElement', | |
3110 'video': 'HTMLVideoElement', | |
3111 }; | |
3112 | |
3113 function overrideConstructor(tagName) { | |
3114 var nativeConstructorName = elements[tagName]; | |
3115 var nativeConstructor = window[nativeConstructorName]; | |
3116 if (!nativeConstructor) | |
3117 return; | |
3118 var element = document.createElement(tagName); | |
3119 var wrapperConstructor = element.constructor; | |
3120 window[nativeConstructorName] = wrapperConstructor; | |
3121 } | |
3122 | |
3123 Object.keys(elements).forEach(overrideConstructor); | |
3124 | |
3125 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) { | |
3126 window[name] = scope.wrappers[name] | |
3127 }); | |
3128 | |
3129 // Export for testing. | |
3130 scope.knownElements = elements; | |
3131 | |
3132 })(this.ShadowDOMPolyfill); | |
3133 /* | |
3134 * Copyright 2013 The Polymer Authors. All rights reserved. | |
3135 * Use of this source code is governed by a BSD-style | |
3136 * license that can be found in the LICENSE file. | |
3137 */ | |
3138 (function() { | |
3139 var ShadowDOMPolyfill = window.ShadowDOMPolyfill; | |
3140 var wrap = ShadowDOMPolyfill.wrap; | |
3141 | |
3142 // patch in prefixed name | |
3143 Object.defineProperties(HTMLElement.prototype, { | |
3144 //TODO(sjmiles): review accessor alias with Arv | |
3145 webkitShadowRoot: { | |
3146 get: function() { | |
3147 return this.shadowRoot; | |
3148 } | |
3149 } | |
3150 }); | |
3151 | |
3152 //TODO(sjmiles): review method alias with Arv | |
3153 HTMLElement.prototype.webkitCreateShadowRoot = | |
3154 HTMLElement.prototype.createShadowRoot; | |
3155 | |
3156 // TODO(jmesserly): figure out what to do about these Dart-specific patches | |
3157 // Right now it depends on some dart2js impl details to patch getTypeNameOf | |
3158 // TODO(jmesserly): we need to wrap document somehow (a dart:html hook?) | |
3159 window.dartMainRunner = function(main) { | |
3160 var NodeList = ShadowDOMPolyfill.wrappers.NodeList; | |
3161 var ShadowRoot = ShadowDOMPolyfill.wrappers.ShadowRoot; | |
3162 var isWrapper = ShadowDOMPolyfill.isWrapper; | |
3163 var unwrap = ShadowDOMPolyfill.unwrap; | |
3164 var innerTypeNameOf = window.$.getFunctionForTypeNameOf(); | |
3165 | |
3166 function typeNameOfShadowDOM(obj) { | |
3167 var result; | |
3168 if (obj instanceof NodeList) { | |
3169 result = 'NodeList'; | |
3170 } else if (obj instanceof ShadowRoot) { | |
3171 result = 'ShadowRoot'; | |
3172 } else if (obj instanceof MutationRecord) { | |
3173 result = 'MutationRecord'; | |
3174 } else if (obj instanceof MutationObserver) { | |
3175 result = 'MutationObserver'; | |
3176 } else { | |
3177 if (isWrapper(obj)) { | |
3178 obj = unwrap(obj); | |
3179 | |
3180 // Fix up class names for Firefox. For some of them like | |
3181 // HTMLFormElement and HTMLInputElement, the "constructor" property of | |
3182 // the unwrapped nodes points at the wrapper for some reason. | |
3183 // TODO(jmesserly): figure out why this is happening. | |
3184 var ctor = obj.constructor; | |
3185 if (ctor && !ctor.builtin$cls && ctor.name == 'GeneratedWrapper') { | |
3186 var name = Object.prototype.toString.call(obj); | |
3187 name = name.substring(8, name.length - 1); | |
3188 ctor.builtin$cls = name; | |
3189 } | |
3190 } | |
3191 | |
3192 result = innerTypeNameOf.call$1(obj); | |
3193 } | |
3194 | |
3195 return result; | |
3196 } | |
3197 | |
3198 function constructorNameShadowDOM(object) { | |
3199 var $constructor, $name, string; | |
3200 if (object == null) | |
3201 return "Null"; | |
3202 $constructor = object.constructor; | |
3203 if (typeof $constructor === "function") { | |
3204 $name = $constructor.builtin$cls; | |
3205 if ($name != null) | |
3206 return $name; | |
3207 } | |
3208 | |
3209 } | |
3210 | |
3211 window.$.constructorNameFallback = constructorNameShadowDOM; | |
3212 window.$._getTypeNameOf = { call$1: typeNameOfShadowDOM }; | |
3213 window.Isolate.$isolateProperties._getTypeNameOf = window.$._getTypeNameOf; | |
3214 | |
3215 // Invoke the real main | |
3216 main(); | |
3217 }; | |
3218 | |
3219 })(); | |
3220 | |
3221 } | |
OLD | NEW |