| 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 |