OLD | NEW |
1 if ((!HTMLElement.prototype.createShadowRoot && | 1 if ((!HTMLElement.prototype.createShadowRoot && |
2 !HTMLElement.prototype.webkitCreateShadowRoot) || | 2 !HTMLElement.prototype.webkitCreateShadowRoot) || |
3 window.__forceShadowDomPolyfill) { | 3 window.__forceShadowDomPolyfill) { |
4 | 4 |
5 /* | 5 /* |
6 * Copyright 2013 The Polymer Authors. All rights reserved. | 6 * Copyright 2013 The Polymer Authors. All rights reserved. |
7 * Use of this source code is governed by a BSD-style | 7 * Use of this source code is governed by a BSD-style |
8 * license that can be found in the LICENSE file. | 8 * license that can be found in the LICENSE file. |
9 */ | 9 */ |
10 (function() { | 10 (function() { |
11 // TODO(jmesserly): fix dart:html to use unprefixed name | 11 // TODO(jmesserly): fix dart:html to use unprefixed name |
12 if (Element.prototype.webkitCreateShadowRoot) { | 12 if (Element.prototype.webkitCreateShadowRoot) { |
13 Element.prototype.webkitCreateShadowRoot = function() { | 13 Element.prototype.webkitCreateShadowRoot = function() { |
14 return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot(); | 14 return window.ShadowDOMPolyfill.wrapIfNeeded(this).createShadowRoot(); |
15 }; | 15 }; |
16 } | 16 } |
17 })(); | 17 })(); |
18 | 18 |
| 19 // Copyright 2012 Google Inc. |
| 20 // |
| 21 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 22 // you may not use this file except in compliance with the License. |
| 23 // You may obtain a copy of the License at |
| 24 // |
| 25 // http://www.apache.org/licenses/LICENSE-2.0 |
| 26 // |
| 27 // Unless required by applicable law or agreed to in writing, software |
| 28 // distributed under the License is distributed on an "AS IS" BASIS, |
| 29 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 30 // See the License for the specific language governing permissions and |
| 31 // limitations under the License. |
| 32 |
| 33 (function(global) { |
| 34 'use strict'; |
| 35 |
| 36 function detectObjectObserve() { |
| 37 if (typeof Object.observe !== 'function' || |
| 38 typeof Array.observe !== 'function') { |
| 39 return false; |
| 40 } |
| 41 |
| 42 var gotSplice = false; |
| 43 function callback(records) { |
| 44 if (records[0].type === 'splice' && records[1].type === 'splice') |
| 45 gotSplice = true; |
| 46 } |
| 47 |
| 48 var test = [0]; |
| 49 Array.observe(test, callback); |
| 50 test[1] = 1; |
| 51 test.length = 0; |
| 52 Object.deliverChangeRecords(callback); |
| 53 return gotSplice; |
| 54 } |
| 55 |
| 56 var hasObserve = detectObjectObserve(); |
| 57 |
| 58 function detectEval() { |
| 59 // don't test for eval if document has CSP securityPolicy object and we can
see that |
| 60 // eval is not supported. This avoids an error message in console even when
the exception |
| 61 // is caught |
| 62 if (global.document && |
| 63 'securityPolicy' in global.document && |
| 64 !global.document.securityPolicy.allowsEval) { |
| 65 return false; |
| 66 } |
| 67 |
| 68 try { |
| 69 var f = new Function('', 'return true;'); |
| 70 return f(); |
| 71 } catch (ex) { |
| 72 return false; |
| 73 } |
| 74 } |
| 75 |
| 76 var hasEval = detectEval(); |
| 77 |
| 78 function isIndex(s) { |
| 79 return +s === s >>> 0; |
| 80 } |
| 81 |
| 82 function toNumber(s) { |
| 83 return +s; |
| 84 } |
| 85 |
| 86 function isObject(obj) { |
| 87 return obj === Object(obj); |
| 88 } |
| 89 |
| 90 var numberIsNaN = global.Number.isNaN || function isNaN(value) { |
| 91 return typeof value === 'number' && global.isNaN(value); |
| 92 } |
| 93 |
| 94 function areSameValue(left, right) { |
| 95 if (left === right) |
| 96 return left !== 0 || 1 / left === 1 / right; |
| 97 if (numberIsNaN(left) && numberIsNaN(right)) |
| 98 return true; |
| 99 |
| 100 return left !== left && right !== right; |
| 101 } |
| 102 |
| 103 var createObject = ('__proto__' in {}) ? |
| 104 function(obj) { return obj; } : |
| 105 function(obj) { |
| 106 var proto = obj.__proto__; |
| 107 if (!proto) |
| 108 return obj; |
| 109 var newObject = Object.create(proto); |
| 110 Object.getOwnPropertyNames(obj).forEach(function(name) { |
| 111 Object.defineProperty(newObject, name, |
| 112 Object.getOwnPropertyDescriptor(obj, name)); |
| 113 }); |
| 114 return newObject; |
| 115 }; |
| 116 |
| 117 var identStart = '[\$_a-zA-Z]'; |
| 118 var identPart = '[\$_a-zA-Z0-9]'; |
| 119 var ident = identStart + '+' + identPart + '*'; |
| 120 var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)'; |
| 121 var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')'; |
| 122 var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementInd
ex + ')*'; |
| 123 var pathRegExp = new RegExp('^' + path + '$'); |
| 124 |
| 125 function isPathValid(s) { |
| 126 if (typeof s != 'string') |
| 127 return false; |
| 128 s = s.trim(); |
| 129 |
| 130 if (s == '') |
| 131 return true; |
| 132 |
| 133 if (s[0] == '.') |
| 134 return false; |
| 135 |
| 136 return pathRegExp.test(s); |
| 137 } |
| 138 |
| 139 var constructorIsPrivate = {}; |
| 140 |
| 141 function Path(s, privateToken) { |
| 142 if (privateToken !== constructorIsPrivate) |
| 143 throw Error('Use Path.get to retrieve path objects'); |
| 144 |
| 145 if (s.trim() == '') |
| 146 return this; |
| 147 |
| 148 if (isIndex(s)) { |
| 149 this.push(s); |
| 150 return this; |
| 151 } |
| 152 |
| 153 s.split(/\s*\.\s*/).filter(function(part) { |
| 154 return part; |
| 155 }).forEach(function(part) { |
| 156 this.push(part); |
| 157 }, this); |
| 158 |
| 159 if (hasEval && !hasObserve && this.length) { |
| 160 this.getValueFrom = this.compiledGetValueFromFn(); |
| 161 } |
| 162 } |
| 163 |
| 164 // TODO(rafaelw): Make simple LRU cache |
| 165 var pathCache = {}; |
| 166 |
| 167 function getPath(pathString) { |
| 168 if (pathString instanceof Path) |
| 169 return pathString; |
| 170 |
| 171 if (pathString == null) |
| 172 pathString = ''; |
| 173 |
| 174 if (typeof pathString !== 'string') |
| 175 pathString = String(pathString); |
| 176 |
| 177 var path = pathCache[pathString]; |
| 178 if (path) |
| 179 return path; |
| 180 if (!isPathValid(pathString)) |
| 181 return invalidPath; |
| 182 var path = new Path(pathString, constructorIsPrivate); |
| 183 pathCache[pathString] = path; |
| 184 return path; |
| 185 } |
| 186 |
| 187 Path.get = getPath; |
| 188 |
| 189 Path.prototype = createObject({ |
| 190 __proto__: [], |
| 191 valid: true, |
| 192 |
| 193 toString: function() { |
| 194 return this.join('.'); |
| 195 }, |
| 196 |
| 197 getValueFrom: function(obj, observedSet) { |
| 198 for (var i = 0; i < this.length; i++) { |
| 199 if (obj == null) |
| 200 return; |
| 201 if (observedSet) |
| 202 observedSet.observe(obj); |
| 203 obj = obj[this[i]]; |
| 204 } |
| 205 return obj; |
| 206 }, |
| 207 |
| 208 compiledGetValueFromFn: function() { |
| 209 var accessors = this.map(function(ident) { |
| 210 return isIndex(ident) ? '["' + ident + '"]' : '.' + ident; |
| 211 }); |
| 212 |
| 213 var str = ''; |
| 214 var pathString = 'obj'; |
| 215 str += 'if (obj != null'; |
| 216 var i = 0; |
| 217 for (; i < (this.length - 1); i++) { |
| 218 var ident = this[i]; |
| 219 pathString += accessors[i]; |
| 220 str += ' &&\n ' + pathString + ' != null'; |
| 221 } |
| 222 str += ')\n'; |
| 223 |
| 224 pathString += accessors[i]; |
| 225 |
| 226 str += ' return ' + pathString + ';\nelse\n return undefined;'; |
| 227 return new Function('obj', str); |
| 228 }, |
| 229 |
| 230 setValueFrom: function(obj, value) { |
| 231 if (!this.length) |
| 232 return false; |
| 233 |
| 234 for (var i = 0; i < this.length - 1; i++) { |
| 235 if (!isObject(obj)) |
| 236 return false; |
| 237 obj = obj[this[i]]; |
| 238 } |
| 239 |
| 240 if (!isObject(obj)) |
| 241 return false; |
| 242 |
| 243 obj[this[i]] = value; |
| 244 return true; |
| 245 } |
| 246 }); |
| 247 |
| 248 var invalidPath = new Path('', constructorIsPrivate); |
| 249 invalidPath.valid = false; |
| 250 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {}; |
| 251 |
| 252 var MAX_DIRTY_CHECK_CYCLES = 1000; |
| 253 |
| 254 function dirtyCheck(observer) { |
| 255 var cycles = 0; |
| 256 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check()) { |
| 257 observer.report(); |
| 258 cycles++; |
| 259 } |
| 260 if (global.testingExposeCycleCount) |
| 261 global.dirtyCheckCycleCount = cycles; |
| 262 } |
| 263 |
| 264 function objectIsEmpty(object) { |
| 265 for (var prop in object) |
| 266 return false; |
| 267 return true; |
| 268 } |
| 269 |
| 270 function diffIsEmpty(diff) { |
| 271 return objectIsEmpty(diff.added) && |
| 272 objectIsEmpty(diff.removed) && |
| 273 objectIsEmpty(diff.changed); |
| 274 } |
| 275 |
| 276 function diffObjectFromOldObject(object, oldObject) { |
| 277 var added = {}; |
| 278 var removed = {}; |
| 279 var changed = {}; |
| 280 var oldObjectHas = {}; |
| 281 |
| 282 for (var prop in oldObject) { |
| 283 var newValue = object[prop]; |
| 284 |
| 285 if (newValue !== undefined && newValue === oldObject[prop]) |
| 286 continue; |
| 287 |
| 288 if (!(prop in object)) { |
| 289 removed[prop] = undefined; |
| 290 continue; |
| 291 } |
| 292 |
| 293 if (newValue !== oldObject[prop]) |
| 294 changed[prop] = newValue; |
| 295 } |
| 296 |
| 297 for (var prop in object) { |
| 298 if (prop in oldObject) |
| 299 continue; |
| 300 |
| 301 added[prop] = object[prop]; |
| 302 } |
| 303 |
| 304 if (Array.isArray(object) && object.length !== oldObject.length) |
| 305 changed.length = object.length; |
| 306 |
| 307 return { |
| 308 added: added, |
| 309 removed: removed, |
| 310 changed: changed |
| 311 }; |
| 312 } |
| 313 |
| 314 function copyObject(object, opt_copy) { |
| 315 var copy = opt_copy || (Array.isArray(object) ? [] : {}); |
| 316 for (var prop in object) { |
| 317 copy[prop] = object[prop]; |
| 318 }; |
| 319 if (Array.isArray(object)) |
| 320 copy.length = object.length; |
| 321 return copy; |
| 322 } |
| 323 |
| 324 function Observer(object, callback, target, token) { |
| 325 this.closed = false; |
| 326 this.object = object; |
| 327 this.callback = callback; |
| 328 // TODO(rafaelw): Hold this.target weakly when WeakRef is available. |
| 329 this.target = target; |
| 330 this.token = token; |
| 331 this.reporting = true; |
| 332 if (hasObserve) { |
| 333 var self = this; |
| 334 this.boundInternalCallback = function(records) { |
| 335 self.internalCallback(records); |
| 336 }; |
| 337 } |
| 338 |
| 339 addToAll(this); |
| 340 } |
| 341 |
| 342 Observer.prototype = { |
| 343 internalCallback: function(records) { |
| 344 if (this.closed) |
| 345 return; |
| 346 if (this.reporting && this.check(records)) { |
| 347 this.report(); |
| 348 if (this.testingResults) |
| 349 this.testingResults.anyChanged = true; |
| 350 } |
| 351 }, |
| 352 |
| 353 close: function() { |
| 354 if (this.closed) |
| 355 return; |
| 356 if (this.object && typeof this.object.close === 'function') |
| 357 this.object.close(); |
| 358 |
| 359 this.disconnect(); |
| 360 this.object = undefined; |
| 361 this.closed = true; |
| 362 }, |
| 363 |
| 364 deliver: function(testingResults) { |
| 365 if (this.closed) |
| 366 return; |
| 367 if (hasObserve) { |
| 368 this.testingResults = testingResults; |
| 369 Object.deliverChangeRecords(this.boundInternalCallback); |
| 370 this.testingResults = undefined; |
| 371 } else { |
| 372 dirtyCheck(this); |
| 373 } |
| 374 }, |
| 375 |
| 376 report: function() { |
| 377 if (!this.reporting) |
| 378 return; |
| 379 |
| 380 this.sync(false); |
| 381 if (this.callback) { |
| 382 this.reportArgs.push(this.token); |
| 383 this.invokeCallback(this.reportArgs); |
| 384 } |
| 385 this.reportArgs = undefined; |
| 386 }, |
| 387 |
| 388 invokeCallback: function(args) { |
| 389 try { |
| 390 this.callback.apply(this.target, args); |
| 391 } catch (ex) { |
| 392 Observer._errorThrownDuringCallback = true; |
| 393 console.error('Exception caught during observer callback: ' + (ex.stack
|| ex)); |
| 394 } |
| 395 }, |
| 396 |
| 397 reset: function() { |
| 398 if (this.closed) |
| 399 return; |
| 400 |
| 401 if (hasObserve) { |
| 402 this.reporting = false; |
| 403 Object.deliverChangeRecords(this.boundInternalCallback); |
| 404 this.reporting = true; |
| 405 } |
| 406 |
| 407 this.sync(true); |
| 408 } |
| 409 } |
| 410 |
| 411 var collectObservers = !hasObserve || global.forceCollectObservers; |
| 412 var allObservers; |
| 413 Observer._allObserversCount = 0; |
| 414 |
| 415 if (collectObservers) { |
| 416 allObservers = []; |
| 417 } |
| 418 |
| 419 function addToAll(observer) { |
| 420 if (!collectObservers) |
| 421 return; |
| 422 |
| 423 allObservers.push(observer); |
| 424 Observer._allObserversCount++; |
| 425 } |
| 426 |
| 427 var runningMicrotaskCheckpoint = false; |
| 428 |
| 429 var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'func
tion'; |
| 430 |
| 431 global.Platform = global.Platform || {}; |
| 432 |
| 433 global.Platform.performMicrotaskCheckpoint = function() { |
| 434 if (runningMicrotaskCheckpoint) |
| 435 return; |
| 436 |
| 437 if (hasDebugForceFullDelivery) { |
| 438 Object.deliverAllChangeRecords(); |
| 439 return; |
| 440 } |
| 441 |
| 442 if (!collectObservers) |
| 443 return; |
| 444 |
| 445 runningMicrotaskCheckpoint = true; |
| 446 |
| 447 var cycles = 0; |
| 448 var results = {}; |
| 449 |
| 450 do { |
| 451 cycles++; |
| 452 var toCheck = allObservers; |
| 453 allObservers = []; |
| 454 results.anyChanged = false; |
| 455 |
| 456 for (var i = 0; i < toCheck.length; i++) { |
| 457 var observer = toCheck[i]; |
| 458 if (observer.closed) |
| 459 continue; |
| 460 |
| 461 if (hasObserve) { |
| 462 observer.deliver(results); |
| 463 } else if (observer.check()) { |
| 464 results.anyChanged = true; |
| 465 observer.report(); |
| 466 } |
| 467 |
| 468 allObservers.push(observer); |
| 469 } |
| 470 } while (cycles < MAX_DIRTY_CHECK_CYCLES && results.anyChanged); |
| 471 |
| 472 if (global.testingExposeCycleCount) |
| 473 global.dirtyCheckCycleCount = cycles; |
| 474 |
| 475 Observer._allObserversCount = allObservers.length; |
| 476 runningMicrotaskCheckpoint = false; |
| 477 }; |
| 478 |
| 479 if (collectObservers) { |
| 480 global.Platform.clearObservers = function() { |
| 481 allObservers = []; |
| 482 }; |
| 483 } |
| 484 |
| 485 function ObjectObserver(object, callback, target, token) { |
| 486 Observer.call(this, object, callback, target, token); |
| 487 this.connect(); |
| 488 this.sync(true); |
| 489 } |
| 490 |
| 491 ObjectObserver.prototype = createObject({ |
| 492 __proto__: Observer.prototype, |
| 493 |
| 494 connect: function() { |
| 495 if (hasObserve) |
| 496 Object.observe(this.object, this.boundInternalCallback); |
| 497 }, |
| 498 |
| 499 sync: function(hard) { |
| 500 if (!hasObserve) |
| 501 this.oldObject = copyObject(this.object); |
| 502 }, |
| 503 |
| 504 check: function(changeRecords) { |
| 505 var diff; |
| 506 var oldValues; |
| 507 if (hasObserve) { |
| 508 if (!changeRecords) |
| 509 return false; |
| 510 |
| 511 oldValues = {}; |
| 512 diff = diffObjectFromChangeRecords(this.object, changeRecords, |
| 513 oldValues); |
| 514 } else { |
| 515 oldValues = this.oldObject; |
| 516 diff = diffObjectFromOldObject(this.object, this.oldObject); |
| 517 } |
| 518 |
| 519 if (diffIsEmpty(diff)) |
| 520 return false; |
| 521 |
| 522 this.reportArgs = |
| 523 [diff.added || {}, diff.removed || {}, diff.changed || {}]; |
| 524 this.reportArgs.push(function(property) { |
| 525 return oldValues[property]; |
| 526 }); |
| 527 |
| 528 return true; |
| 529 }, |
| 530 |
| 531 disconnect: function() { |
| 532 if (!hasObserve) |
| 533 this.oldObject = undefined; |
| 534 else if (this.object) |
| 535 Object.unobserve(this.object, this.boundInternalCallback); |
| 536 } |
| 537 }); |
| 538 |
| 539 function ArrayObserver(array, callback, target, token) { |
| 540 if (!Array.isArray(array)) |
| 541 throw Error('Provided object is not an Array'); |
| 542 ObjectObserver.call(this, array, callback, target, token); |
| 543 } |
| 544 |
| 545 ArrayObserver.prototype = createObject({ |
| 546 __proto__: ObjectObserver.prototype, |
| 547 |
| 548 connect: function() { |
| 549 if (hasObserve) |
| 550 Array.observe(this.object, this.boundInternalCallback); |
| 551 }, |
| 552 |
| 553 sync: function() { |
| 554 if (!hasObserve) |
| 555 this.oldObject = this.object.slice(); |
| 556 }, |
| 557 |
| 558 check: function(changeRecords) { |
| 559 var splices; |
| 560 if (hasObserve) { |
| 561 if (!changeRecords) |
| 562 return false; |
| 563 splices = projectArraySplices(this.object, changeRecords); |
| 564 } else { |
| 565 splices = calcSplices(this.object, 0, this.object.length, |
| 566 this.oldObject, 0, this.oldObject.length); |
| 567 } |
| 568 |
| 569 if (!splices || !splices.length) |
| 570 return false; |
| 571 |
| 572 this.reportArgs = [splices]; |
| 573 return true; |
| 574 } |
| 575 }); |
| 576 |
| 577 ArrayObserver.applySplices = function(previous, current, splices) { |
| 578 splices.forEach(function(splice) { |
| 579 var spliceArgs = [splice.index, splice.removed.length]; |
| 580 var addIndex = splice.index; |
| 581 while (addIndex < splice.index + splice.addedCount) { |
| 582 spliceArgs.push(current[addIndex]); |
| 583 addIndex++; |
| 584 } |
| 585 |
| 586 Array.prototype.splice.apply(previous, spliceArgs); |
| 587 }); |
| 588 }; |
| 589 |
| 590 function ObservedSet(callback) { |
| 591 this.arr = []; |
| 592 this.callback = callback; |
| 593 this.isObserved = true; |
| 594 } |
| 595 |
| 596 var objProto = Object.getPrototypeOf({}); |
| 597 var arrayProto = Object.getPrototypeOf([]); |
| 598 ObservedSet.prototype = { |
| 599 reset: function() { |
| 600 this.isObserved = !this.isObserved; |
| 601 }, |
| 602 |
| 603 observe: function(obj) { |
| 604 if (!isObject(obj) || obj === objProto || obj === arrayProto) |
| 605 return; |
| 606 var i = this.arr.indexOf(obj); |
| 607 if (i >= 0 && this.arr[i+1] === this.isObserved) |
| 608 return; |
| 609 |
| 610 if (i < 0) { |
| 611 i = this.arr.length; |
| 612 this.arr[i] = obj; |
| 613 Object.observe(obj, this.callback); |
| 614 } |
| 615 |
| 616 this.arr[i+1] = this.isObserved; |
| 617 this.observe(Object.getPrototypeOf(obj)); |
| 618 }, |
| 619 |
| 620 cleanup: function() { |
| 621 var i = 0, j = 0; |
| 622 var isObserved = this.isObserved; |
| 623 while(j < this.arr.length) { |
| 624 var obj = this.arr[j]; |
| 625 if (this.arr[j + 1] == isObserved) { |
| 626 if (i < j) { |
| 627 this.arr[i] = obj; |
| 628 this.arr[i + 1] = isObserved; |
| 629 } |
| 630 i += 2; |
| 631 } else { |
| 632 Object.unobserve(obj, this.callback); |
| 633 } |
| 634 j += 2; |
| 635 } |
| 636 |
| 637 this.arr.length = i; |
| 638 } |
| 639 }; |
| 640 |
| 641 function PathObserver(object, path, callback, target, token, valueFn, |
| 642 setValueFn) { |
| 643 var path = path instanceof Path ? path : getPath(path); |
| 644 if (!path || !path.length || !isObject(object)) { |
| 645 this.value_ = path ? path.getValueFrom(object) : undefined; |
| 646 this.value = valueFn ? valueFn(this.value_) : this.value_; |
| 647 this.closed = true; |
| 648 return; |
| 649 } |
| 650 |
| 651 Observer.call(this, object, callback, target, token); |
| 652 this.valueFn = valueFn; |
| 653 this.setValueFn = setValueFn; |
| 654 this.path = path; |
| 655 |
| 656 this.connect(); |
| 657 this.sync(true); |
| 658 } |
| 659 |
| 660 PathObserver.prototype = createObject({ |
| 661 __proto__: Observer.prototype, |
| 662 |
| 663 connect: function() { |
| 664 if (hasObserve) |
| 665 this.observedSet = new ObservedSet(this.boundInternalCallback); |
| 666 }, |
| 667 |
| 668 disconnect: function() { |
| 669 this.value = undefined; |
| 670 this.value_ = undefined; |
| 671 if (this.observedSet) { |
| 672 this.observedSet.reset(); |
| 673 this.observedSet.cleanup(); |
| 674 this.observedSet = undefined; |
| 675 } |
| 676 }, |
| 677 |
| 678 check: function() { |
| 679 // Note: Extracting this to a member function for use here and below |
| 680 // regresses dirty-checking path perf by about 25% =-(. |
| 681 if (this.observedSet) |
| 682 this.observedSet.reset(); |
| 683 |
| 684 this.value_ = this.path.getValueFrom(this.object, this.observedSet); |
| 685 |
| 686 if (this.observedSet) |
| 687 this.observedSet.cleanup(); |
| 688 |
| 689 if (areSameValue(this.value_, this.oldValue_)) |
| 690 return false; |
| 691 |
| 692 this.value = this.valueFn ? this.valueFn(this.value_) : this.value_; |
| 693 this.reportArgs = [this.value, this.oldValue]; |
| 694 return true; |
| 695 }, |
| 696 |
| 697 sync: function(hard) { |
| 698 if (hard) { |
| 699 if (this.observedSet) |
| 700 this.observedSet.reset(); |
| 701 |
| 702 this.value_ = this.path.getValueFrom(this.object, this.observedSet); |
| 703 this.value = this.valueFn ? this.valueFn(this.value_) : this.value_; |
| 704 |
| 705 if (this.observedSet) |
| 706 this.observedSet.cleanup(); |
| 707 } |
| 708 |
| 709 this.oldValue_ = this.value_; |
| 710 this.oldValue = this.value; |
| 711 }, |
| 712 |
| 713 setValue: function(newValue) { |
| 714 if (!this.path) |
| 715 return; |
| 716 if (typeof this.setValueFn === 'function') |
| 717 newValue = this.setValueFn(newValue); |
| 718 this.path.setValueFrom(this.object, newValue); |
| 719 } |
| 720 }); |
| 721 |
| 722 function CompoundPathObserver(callback, target, token, valueFn) { |
| 723 Observer.call(this, undefined, callback, target, token); |
| 724 this.valueFn = valueFn; |
| 725 |
| 726 this.observed = []; |
| 727 this.values = []; |
| 728 this.started = false; |
| 729 } |
| 730 |
| 731 CompoundPathObserver.prototype = createObject({ |
| 732 __proto__: PathObserver.prototype, |
| 733 |
| 734 addPath: function(object, path) { |
| 735 if (this.started) |
| 736 throw Error('Cannot add more paths once started.'); |
| 737 |
| 738 var path = path instanceof Path ? path : getPath(path); |
| 739 var value = path ? path.getValueFrom(object) : undefined; |
| 740 |
| 741 this.observed.push(object, path); |
| 742 this.values.push(value); |
| 743 }, |
| 744 |
| 745 start: function() { |
| 746 this.connect(); |
| 747 this.sync(true); |
| 748 }, |
| 749 |
| 750 getValues: function() { |
| 751 if (this.observedSet) |
| 752 this.observedSet.reset(); |
| 753 |
| 754 var anyChanged = false; |
| 755 for (var i = 0; i < this.observed.length; i = i+2) { |
| 756 var path = this.observed[i+1]; |
| 757 if (!path) |
| 758 continue; |
| 759 var object = this.observed[i]; |
| 760 var value = path.getValueFrom(object, this.observedSet); |
| 761 var oldValue = this.values[i/2]; |
| 762 if (!areSameValue(value, oldValue)) { |
| 763 this.values[i/2] = value; |
| 764 anyChanged = true; |
| 765 } |
| 766 } |
| 767 |
| 768 if (this.observedSet) |
| 769 this.observedSet.cleanup(); |
| 770 |
| 771 return anyChanged; |
| 772 }, |
| 773 |
| 774 check: function() { |
| 775 if (!this.getValues()) |
| 776 return; |
| 777 |
| 778 this.value = this.valueFn(this.values); |
| 779 |
| 780 if (areSameValue(this.value, this.oldValue)) |
| 781 return false; |
| 782 |
| 783 this.reportArgs = [this.value, this.oldValue]; |
| 784 return true; |
| 785 }, |
| 786 |
| 787 sync: function(hard) { |
| 788 if (hard) { |
| 789 this.getValues(); |
| 790 this.value = this.valueFn(this.values); |
| 791 } |
| 792 |
| 793 this.oldValue = this.value; |
| 794 }, |
| 795 |
| 796 close: function() { |
| 797 if (this.observed) { |
| 798 for (var i = 0; i < this.observed.length; i = i + 2) { |
| 799 var object = this.observed[i]; |
| 800 if (object && typeof object.close === 'function') |
| 801 object.close(); |
| 802 } |
| 803 this.observed = undefined; |
| 804 this.values = undefined; |
| 805 } |
| 806 |
| 807 Observer.prototype.close.call(this); |
| 808 } |
| 809 }); |
| 810 |
| 811 var knownRecordTypes = { |
| 812 'new': true, |
| 813 'updated': true, |
| 814 'deleted': true |
| 815 }; |
| 816 |
| 817 function notifyFunction(object, name) { |
| 818 if (typeof Object.observe !== 'function') |
| 819 return; |
| 820 |
| 821 var notifier = Object.getNotifier(object); |
| 822 return function(type, oldValue) { |
| 823 var changeRecord = { |
| 824 object: object, |
| 825 type: type, |
| 826 name: name |
| 827 }; |
| 828 if (arguments.length === 2) |
| 829 changeRecord.oldValue = oldValue; |
| 830 notifier.notify(changeRecord); |
| 831 } |
| 832 } |
| 833 |
| 834 // TODO(rafaelw): It should be possible for the Object.observe case to have |
| 835 // every PathObserver used by defineProperty share a single Object.observe |
| 836 // callback, and thus get() can simply call observer.deliver() and any changes |
| 837 // to any dependent value will be observed. |
| 838 PathObserver.defineProperty = function(object, name, descriptor) { |
| 839 // TODO(rafaelw): Validate errors |
| 840 var obj = descriptor.object; |
| 841 var path = getPath(descriptor.path); |
| 842 var notify = notifyFunction(object, name); |
| 843 |
| 844 var observer = new PathObserver(obj, descriptor.path, |
| 845 function(newValue, oldValue) { |
| 846 if (notify) |
| 847 notify('updated', oldValue); |
| 848 } |
| 849 ); |
| 850 |
| 851 Object.defineProperty(object, name, { |
| 852 get: function() { |
| 853 return path.getValueFrom(obj); |
| 854 }, |
| 855 set: function(newValue) { |
| 856 path.setValueFrom(obj, newValue); |
| 857 }, |
| 858 configurable: true |
| 859 }); |
| 860 |
| 861 return { |
| 862 close: function() { |
| 863 var oldValue = path.getValueFrom(obj); |
| 864 if (notify) |
| 865 observer.deliver(); |
| 866 observer.close(); |
| 867 Object.defineProperty(object, name, { |
| 868 value: oldValue, |
| 869 writable: true, |
| 870 configurable: true |
| 871 }); |
| 872 } |
| 873 }; |
| 874 } |
| 875 |
| 876 function diffObjectFromChangeRecords(object, changeRecords, oldValues) { |
| 877 var added = {}; |
| 878 var removed = {}; |
| 879 |
| 880 for (var i = 0; i < changeRecords.length; i++) { |
| 881 var record = changeRecords[i]; |
| 882 if (!knownRecordTypes[record.type]) { |
| 883 console.error('Unknown changeRecord type: ' + record.type); |
| 884 console.error(record); |
| 885 continue; |
| 886 } |
| 887 |
| 888 if (!(record.name in oldValues)) |
| 889 oldValues[record.name] = record.oldValue; |
| 890 |
| 891 if (record.type == 'updated') |
| 892 continue; |
| 893 |
| 894 if (record.type == 'new') { |
| 895 if (record.name in removed) |
| 896 delete removed[record.name]; |
| 897 else |
| 898 added[record.name] = true; |
| 899 |
| 900 continue; |
| 901 } |
| 902 |
| 903 // type = 'deleted' |
| 904 if (record.name in added) { |
| 905 delete added[record.name]; |
| 906 delete oldValues[record.name]; |
| 907 } else { |
| 908 removed[record.name] = true; |
| 909 } |
| 910 } |
| 911 |
| 912 for (var prop in added) |
| 913 added[prop] = object[prop]; |
| 914 |
| 915 for (var prop in removed) |
| 916 removed[prop] = undefined; |
| 917 |
| 918 var changed = {}; |
| 919 for (var prop in oldValues) { |
| 920 if (prop in added || prop in removed) |
| 921 continue; |
| 922 |
| 923 var newValue = object[prop]; |
| 924 if (oldValues[prop] !== newValue) |
| 925 changed[prop] = newValue; |
| 926 } |
| 927 |
| 928 return { |
| 929 added: added, |
| 930 removed: removed, |
| 931 changed: changed |
| 932 }; |
| 933 } |
| 934 |
| 935 function newSplice(index, removed, addedCount) { |
| 936 return { |
| 937 index: index, |
| 938 removed: removed, |
| 939 addedCount: addedCount |
| 940 }; |
| 941 } |
| 942 |
| 943 var EDIT_LEAVE = 0; |
| 944 var EDIT_UPDATE = 1; |
| 945 var EDIT_ADD = 2; |
| 946 var EDIT_DELETE = 3; |
| 947 |
| 948 function ArraySplice() {} |
| 949 |
| 950 ArraySplice.prototype = { |
| 951 |
| 952 // Note: This function is *based* on the computation of the Levenshtein |
| 953 // "edit" distance. The one change is that "updates" are treated as two |
| 954 // edits - not one. With Array splices, an update is really a delete |
| 955 // followed by an add. By retaining this, we optimize for "keeping" the |
| 956 // maximum array items in the original array. For example: |
| 957 // |
| 958 // 'xxxx123' -> '123yyyy' |
| 959 // |
| 960 // With 1-edit updates, the shortest path would be just to update all seven |
| 961 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This |
| 962 // leaves the substring '123' intact. |
| 963 calcEditDistances: function(current, currentStart, currentEnd, |
| 964 old, oldStart, oldEnd) { |
| 965 // "Deletion" columns |
| 966 var rowCount = oldEnd - oldStart + 1; |
| 967 var columnCount = currentEnd - currentStart + 1; |
| 968 var distances = new Array(rowCount); |
| 969 |
| 970 // "Addition" rows. Initialize null column. |
| 971 for (var i = 0; i < rowCount; i++) { |
| 972 distances[i] = new Array(columnCount); |
| 973 distances[i][0] = i; |
| 974 } |
| 975 |
| 976 // Initialize null row |
| 977 for (var j = 0; j < columnCount; j++) |
| 978 distances[0][j] = j; |
| 979 |
| 980 for (var i = 1; i < rowCount; i++) { |
| 981 for (var j = 1; j < columnCount; j++) { |
| 982 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) |
| 983 distances[i][j] = distances[i - 1][j - 1]; |
| 984 else { |
| 985 var north = distances[i - 1][j] + 1; |
| 986 var west = distances[i][j - 1] + 1; |
| 987 distances[i][j] = north < west ? north : west; |
| 988 } |
| 989 } |
| 990 } |
| 991 |
| 992 return distances; |
| 993 }, |
| 994 |
| 995 // This starts at the final weight, and walks "backward" by finding |
| 996 // the minimum previous weight recursively until the origin of the weight |
| 997 // matrix. |
| 998 spliceOperationsFromEditDistances: function(distances) { |
| 999 var i = distances.length - 1; |
| 1000 var j = distances[0].length - 1; |
| 1001 var current = distances[i][j]; |
| 1002 var edits = []; |
| 1003 while (i > 0 || j > 0) { |
| 1004 if (i == 0) { |
| 1005 edits.push(EDIT_ADD); |
| 1006 j--; |
| 1007 continue; |
| 1008 } |
| 1009 if (j == 0) { |
| 1010 edits.push(EDIT_DELETE); |
| 1011 i--; |
| 1012 continue; |
| 1013 } |
| 1014 var northWest = distances[i - 1][j - 1]; |
| 1015 var west = distances[i - 1][j]; |
| 1016 var north = distances[i][j - 1]; |
| 1017 |
| 1018 var min; |
| 1019 if (west < north) |
| 1020 min = west < northWest ? west : northWest; |
| 1021 else |
| 1022 min = north < northWest ? north : northWest; |
| 1023 |
| 1024 if (min == northWest) { |
| 1025 if (northWest == current) { |
| 1026 edits.push(EDIT_LEAVE); |
| 1027 } else { |
| 1028 edits.push(EDIT_UPDATE); |
| 1029 current = northWest; |
| 1030 } |
| 1031 i--; |
| 1032 j--; |
| 1033 } else if (min == west) { |
| 1034 edits.push(EDIT_DELETE); |
| 1035 i--; |
| 1036 current = west; |
| 1037 } else { |
| 1038 edits.push(EDIT_ADD); |
| 1039 j--; |
| 1040 current = north; |
| 1041 } |
| 1042 } |
| 1043 |
| 1044 edits.reverse(); |
| 1045 return edits; |
| 1046 }, |
| 1047 |
| 1048 /** |
| 1049 * Splice Projection functions: |
| 1050 * |
| 1051 * A splice map is a representation of how a previous array of items |
| 1052 * was transformed into a new array of items. Conceptually it is a list of |
| 1053 * tuples of |
| 1054 * |
| 1055 * <index, removed, addedCount> |
| 1056 * |
| 1057 * which are kept in ascending index order of. The tuple represents that at |
| 1058 * the |index|, |removed| sequence of items were removed, and counting forwa
rd |
| 1059 * from |index|, |addedCount| items were added. |
| 1060 */ |
| 1061 |
| 1062 /** |
| 1063 * Lacking individual splice mutation information, the minimal set of |
| 1064 * splices can be synthesized given the previous state and final state of an |
| 1065 * array. The basic approach is to calculate the edit distance matrix and |
| 1066 * choose the shortest path through it. |
| 1067 * |
| 1068 * Complexity: O(l * p) |
| 1069 * l: The length of the current array |
| 1070 * p: The length of the old array |
| 1071 */ |
| 1072 calcSplices: function(current, currentStart, currentEnd, |
| 1073 old, oldStart, oldEnd) { |
| 1074 var prefixCount = 0; |
| 1075 var suffixCount = 0; |
| 1076 |
| 1077 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); |
| 1078 if (currentStart == 0 && oldStart == 0) |
| 1079 prefixCount = this.sharedPrefix(current, old, minLength); |
| 1080 |
| 1081 if (currentEnd == current.length && oldEnd == old.length) |
| 1082 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount); |
| 1083 |
| 1084 currentStart += prefixCount; |
| 1085 oldStart += prefixCount; |
| 1086 currentEnd -= suffixCount; |
| 1087 oldEnd -= suffixCount; |
| 1088 |
| 1089 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) |
| 1090 return []; |
| 1091 |
| 1092 if (currentStart == currentEnd) { |
| 1093 var splice = newSplice(currentStart, [], 0); |
| 1094 while (oldStart < oldEnd) |
| 1095 splice.removed.push(old[oldStart++]); |
| 1096 |
| 1097 return [ splice ]; |
| 1098 } else if (oldStart == oldEnd) |
| 1099 return [ newSplice(currentStart, [], currentEnd - currentStart) ]; |
| 1100 |
| 1101 var ops = this.spliceOperationsFromEditDistances( |
| 1102 this.calcEditDistances(current, currentStart, currentEnd, |
| 1103 old, oldStart, oldEnd)); |
| 1104 |
| 1105 var splice = undefined; |
| 1106 var splices = []; |
| 1107 var index = currentStart; |
| 1108 var oldIndex = oldStart; |
| 1109 for (var i = 0; i < ops.length; i++) { |
| 1110 switch(ops[i]) { |
| 1111 case EDIT_LEAVE: |
| 1112 if (splice) { |
| 1113 splices.push(splice); |
| 1114 splice = undefined; |
| 1115 } |
| 1116 |
| 1117 index++; |
| 1118 oldIndex++; |
| 1119 break; |
| 1120 case EDIT_UPDATE: |
| 1121 if (!splice) |
| 1122 splice = newSplice(index, [], 0); |
| 1123 |
| 1124 splice.addedCount++; |
| 1125 index++; |
| 1126 |
| 1127 splice.removed.push(old[oldIndex]); |
| 1128 oldIndex++; |
| 1129 break; |
| 1130 case EDIT_ADD: |
| 1131 if (!splice) |
| 1132 splice = newSplice(index, [], 0); |
| 1133 |
| 1134 splice.addedCount++; |
| 1135 index++; |
| 1136 break; |
| 1137 case EDIT_DELETE: |
| 1138 if (!splice) |
| 1139 splice = newSplice(index, [], 0); |
| 1140 |
| 1141 splice.removed.push(old[oldIndex]); |
| 1142 oldIndex++; |
| 1143 break; |
| 1144 } |
| 1145 } |
| 1146 |
| 1147 if (splice) { |
| 1148 splices.push(splice); |
| 1149 } |
| 1150 return splices; |
| 1151 }, |
| 1152 |
| 1153 sharedPrefix: function(current, old, searchLength) { |
| 1154 for (var i = 0; i < searchLength; i++) |
| 1155 if (!this.equals(current[i], old[i])) |
| 1156 return i; |
| 1157 return searchLength; |
| 1158 }, |
| 1159 |
| 1160 sharedSuffix: function(current, old, searchLength) { |
| 1161 var index1 = current.length; |
| 1162 var index2 = old.length; |
| 1163 var count = 0; |
| 1164 while (count < searchLength && this.equals(current[--index1], old[--index2
])) |
| 1165 count++; |
| 1166 |
| 1167 return count; |
| 1168 }, |
| 1169 |
| 1170 calculateSplices: function(current, previous) { |
| 1171 return this.calcSplices(current, 0, current.length, previous, 0, |
| 1172 previous.length); |
| 1173 }, |
| 1174 |
| 1175 equals: function(currentValue, previousValue) { |
| 1176 return currentValue === previousValue; |
| 1177 } |
| 1178 }; |
| 1179 |
| 1180 var arraySplice = new ArraySplice(); |
| 1181 |
| 1182 function calcSplices(current, currentStart, currentEnd, |
| 1183 old, oldStart, oldEnd) { |
| 1184 return arraySplice.calcSplices(current, currentStart, currentEnd, |
| 1185 old, oldStart, oldEnd); |
| 1186 } |
| 1187 |
| 1188 function intersect(start1, end1, start2, end2) { |
| 1189 // Disjoint |
| 1190 if (end1 < start2 || end2 < start1) |
| 1191 return -1; |
| 1192 |
| 1193 // Adjacent |
| 1194 if (end1 == start2 || end2 == start1) |
| 1195 return 0; |
| 1196 |
| 1197 // Non-zero intersect, span1 first |
| 1198 if (start1 < start2) { |
| 1199 if (end1 < end2) |
| 1200 return end1 - start2; // Overlap |
| 1201 else |
| 1202 return end2 - start2; // Contained |
| 1203 } else { |
| 1204 // Non-zero intersect, span2 first |
| 1205 if (end2 < end1) |
| 1206 return end2 - start1; // Overlap |
| 1207 else |
| 1208 return end1 - start1; // Contained |
| 1209 } |
| 1210 } |
| 1211 |
| 1212 function mergeSplice(splices, index, removed, addedCount) { |
| 1213 |
| 1214 var splice = newSplice(index, removed, addedCount); |
| 1215 |
| 1216 var inserted = false; |
| 1217 var insertionOffset = 0; |
| 1218 |
| 1219 for (var i = 0; i < splices.length; i++) { |
| 1220 var current = splices[i]; |
| 1221 current.index += insertionOffset; |
| 1222 |
| 1223 if (inserted) |
| 1224 continue; |
| 1225 |
| 1226 var intersectCount = intersect(splice.index, |
| 1227 splice.index + splice.removed.length, |
| 1228 current.index, |
| 1229 current.index + current.addedCount); |
| 1230 |
| 1231 if (intersectCount >= 0) { |
| 1232 // Merge the two splices |
| 1233 |
| 1234 splices.splice(i, 1); |
| 1235 i--; |
| 1236 |
| 1237 insertionOffset -= current.addedCount - current.removed.length; |
| 1238 |
| 1239 splice.addedCount += current.addedCount - intersectCount; |
| 1240 var deleteCount = splice.removed.length + |
| 1241 current.removed.length - intersectCount; |
| 1242 |
| 1243 if (!splice.addedCount && !deleteCount) { |
| 1244 // merged splice is a noop. discard. |
| 1245 inserted = true; |
| 1246 } else { |
| 1247 var removed = current.removed; |
| 1248 |
| 1249 if (splice.index < current.index) { |
| 1250 // some prefix of splice.removed is prepended to current.removed. |
| 1251 var prepend = splice.removed.slice(0, current.index - splice.index); |
| 1252 Array.prototype.push.apply(prepend, removed); |
| 1253 removed = prepend; |
| 1254 } |
| 1255 |
| 1256 if (splice.index + splice.removed.length > current.index + current.add
edCount) { |
| 1257 // some suffix of splice.removed is appended to current.removed. |
| 1258 var append = splice.removed.slice(current.index + current.addedCount
- splice.index); |
| 1259 Array.prototype.push.apply(removed, append); |
| 1260 } |
| 1261 |
| 1262 splice.removed = removed; |
| 1263 if (current.index < splice.index) { |
| 1264 splice.index = current.index; |
| 1265 } |
| 1266 } |
| 1267 } else if (splice.index < current.index) { |
| 1268 // Insert splice here. |
| 1269 |
| 1270 inserted = true; |
| 1271 |
| 1272 splices.splice(i, 0, splice); |
| 1273 i++; |
| 1274 |
| 1275 var offset = splice.addedCount - splice.removed.length |
| 1276 current.index += offset; |
| 1277 insertionOffset += offset; |
| 1278 } |
| 1279 } |
| 1280 |
| 1281 if (!inserted) |
| 1282 splices.push(splice); |
| 1283 } |
| 1284 |
| 1285 function createInitialSplices(array, changeRecords) { |
| 1286 var splices = []; |
| 1287 |
| 1288 for (var i = 0; i < changeRecords.length; i++) { |
| 1289 var record = changeRecords[i]; |
| 1290 switch(record.type) { |
| 1291 case 'splice': |
| 1292 mergeSplice(splices, record.index, record.removed.slice(), record.adde
dCount); |
| 1293 break; |
| 1294 case 'new': |
| 1295 case 'updated': |
| 1296 case 'deleted': |
| 1297 if (!isIndex(record.name)) |
| 1298 continue; |
| 1299 var index = toNumber(record.name); |
| 1300 if (index < 0) |
| 1301 continue; |
| 1302 mergeSplice(splices, index, [record.oldValue], 1); |
| 1303 break; |
| 1304 default: |
| 1305 console.error('Unexpected record type: ' + JSON.stringify(record)); |
| 1306 break; |
| 1307 } |
| 1308 } |
| 1309 |
| 1310 return splices; |
| 1311 } |
| 1312 |
| 1313 function projectArraySplices(array, changeRecords) { |
| 1314 var splices = []; |
| 1315 |
| 1316 createInitialSplices(array, changeRecords).forEach(function(splice) { |
| 1317 if (splice.addedCount == 1 && splice.removed.length == 1) { |
| 1318 if (splice.removed[0] !== array[splice.index]) |
| 1319 splices.push(splice); |
| 1320 |
| 1321 return |
| 1322 }; |
| 1323 |
| 1324 splices = splices.concat(calcSplices(array, splice.index, splice.index + s
plice.addedCount, |
| 1325 splice.removed, 0, splice.removed.len
gth)); |
| 1326 }); |
| 1327 |
| 1328 return splices; |
| 1329 } |
| 1330 |
| 1331 global.Observer = Observer; |
| 1332 global.Observer.hasObjectObserve = hasObserve; |
| 1333 global.ArrayObserver = ArrayObserver; |
| 1334 global.ArrayObserver.calculateSplices = function(current, previous) { |
| 1335 return arraySplice.calculateSplices(current, previous); |
| 1336 }; |
| 1337 |
| 1338 global.ArraySplice = ArraySplice; |
| 1339 global.ObjectObserver = ObjectObserver; |
| 1340 global.PathObserver = PathObserver; |
| 1341 global.CompoundPathObserver = CompoundPathObserver; |
| 1342 global.Path = Path; |
| 1343 })(typeof global !== 'undefined' && global ? global : this); |
| 1344 |
19 /* | 1345 /* |
20 * Copyright 2012 The Polymer Authors. All rights reserved. | 1346 * Copyright 2012 The Polymer Authors. All rights reserved. |
21 * Use of this source code is goverened by a BSD-style | 1347 * Use of this source code is governed by a BSD-style |
22 * license that can be found in the LICENSE file. | 1348 * license that can be found in the LICENSE file. |
23 */ | 1349 */ |
24 | 1350 |
25 // SideTable is a weak map where possible. If WeakMap is not available the | 1351 // If WeakMap is not available, the association is stored as an expando property
on the "key". |
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 | 1352 // TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox |
29 if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') <
0) { | 1353 if (typeof WeakMap === 'undefined' || navigator.userAgent.indexOf('Firefox/') >
-1) { |
30 SideTable = WeakMap; | |
31 } else { | |
32 (function() { | 1354 (function() { |
33 var defineProperty = Object.defineProperty; | 1355 var defineProperty = Object.defineProperty; |
34 var counter = Date.now() % 1e9; | 1356 var counter = Date.now() % 1e9; |
35 | 1357 |
36 SideTable = function() { | 1358 var WeakMap = function() { |
37 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | 1359 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); |
38 }; | 1360 }; |
39 | 1361 |
40 SideTable.prototype = { | 1362 WeakMap.prototype = { |
41 set: function(key, value) { | 1363 set: function(key, value) { |
42 var entry = key[this.name]; | 1364 var entry = key[this.name]; |
43 if (entry && entry[0] === key) | 1365 if (entry && entry[0] === key) |
44 entry[1] = value; | 1366 entry[1] = value; |
45 else | 1367 else |
46 defineProperty(key, this.name, {value: [key, value], writable: true}); | 1368 defineProperty(key, this.name, {value: [key, value], writable: true}); |
47 }, | 1369 }, |
48 get: function(key) { | 1370 get: function(key) { |
49 var entry; | 1371 var entry; |
50 return (entry = key[this.name]) && entry[0] === key ? | 1372 return (entry = key[this.name]) && entry[0] === key ? |
51 entry[1] : undefined; | 1373 entry[1] : undefined; |
52 }, | 1374 }, |
53 delete: function(key) { | 1375 delete: function(key) { |
54 this.set(key, undefined); | 1376 this.set(key, undefined); |
55 } | 1377 } |
56 } | 1378 }; |
| 1379 |
| 1380 window.WeakMap = WeakMap; |
57 })(); | 1381 })(); |
58 } | 1382 } |
59 | 1383 |
60 // Copyright 2012 The Polymer Authors. All rights reserved. | 1384 // Copyright 2012 The Polymer Authors. All rights reserved. |
61 // Use of this source code is goverened by a BSD-style | 1385 // Use of this source code is goverened by a BSD-style |
62 // license that can be found in the LICENSE file. | 1386 // license that can be found in the LICENSE file. |
63 | 1387 |
64 var ShadowDOMPolyfill = {}; | 1388 var ShadowDOMPolyfill = {}; |
65 | 1389 |
66 (function(scope) { | 1390 (function(scope) { |
67 'use strict'; | 1391 'use strict'; |
68 | 1392 |
69 var wrapperTable = new SideTable(); | 1393 var constructorTable = new WeakMap(); |
70 var constructorTable = new SideTable(); | 1394 var nativePrototypeTable = new WeakMap(); |
71 var nativePrototypeTable = new SideTable(); | |
72 var wrappers = Object.create(null); | 1395 var wrappers = Object.create(null); |
73 | 1396 |
| 1397 // Don't test for eval if document has CSP securityPolicy object and we can |
| 1398 // see that eval is not supported. This avoids an error message in console |
| 1399 // even when the exception is caught |
| 1400 var hasEval = !('securityPolicy' in document) || |
| 1401 document.securityPolicy.allowsEval; |
| 1402 if (hasEval) { |
| 1403 try { |
| 1404 var f = new Function('', 'return true;'); |
| 1405 hasEval = f(); |
| 1406 } catch (ex) { |
| 1407 } |
| 1408 } |
| 1409 |
74 function assert(b) { | 1410 function assert(b) { |
75 if (!b) | 1411 if (!b) |
76 throw new Error('Assertion failed'); | 1412 throw new Error('Assertion failed'); |
77 }; | 1413 }; |
78 | 1414 |
79 function mixin(to, from) { | 1415 function mixin(to, from) { |
80 Object.getOwnPropertyNames(from).forEach(function(name) { | 1416 Object.getOwnPropertyNames(from).forEach(function(name) { |
81 Object.defineProperty(to, name, | 1417 Object.defineProperty(to, name, |
82 Object.getOwnPropertyDescriptor(from, name)); | 1418 Object.getOwnPropertyDescriptor(from, name)); |
83 }); | 1419 }); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 get: function() {}, | 1479 get: function() {}, |
144 set: function(v) {}, | 1480 set: function(v) {}, |
145 configurable: true, | 1481 configurable: true, |
146 enumerable: true | 1482 enumerable: true |
147 }; | 1483 }; |
148 | 1484 |
149 function isEventHandlerName(name) { | 1485 function isEventHandlerName(name) { |
150 return /^on[a-z]+$/.test(name); | 1486 return /^on[a-z]+$/.test(name); |
151 } | 1487 } |
152 | 1488 |
| 1489 function getGetter(name) { |
| 1490 return hasEval ? |
| 1491 new Function('return this.impl.' + name) : |
| 1492 function() { return this.impl[name]; }; |
| 1493 } |
| 1494 |
| 1495 function getSetter(name) { |
| 1496 return hasEval ? |
| 1497 new Function('v', 'this.impl.' + name + ' = v') : |
| 1498 function(v) { this.impl[name] = v; }; |
| 1499 } |
| 1500 |
| 1501 function getMethod(name) { |
| 1502 return hasEval ? |
| 1503 new Function('return this.impl.' + name + |
| 1504 '.apply(this.impl, arguments)') : |
| 1505 function() { return this.impl[name].apply(this.impl, arguments); }; |
| 1506 } |
| 1507 |
153 function installProperty(source, target, allowMethod) { | 1508 function installProperty(source, target, allowMethod) { |
154 Object.getOwnPropertyNames(source).forEach(function(name) { | 1509 Object.getOwnPropertyNames(source).forEach(function(name) { |
155 if (name in target) | 1510 if (name in target) |
156 return; | 1511 return; |
157 | 1512 |
158 if (isFirefox) { | 1513 if (isFirefox) { |
159 // Tickle Firefox's old bindings. | 1514 // Tickle Firefox's old bindings. |
160 source.__lookupGetter__(name); | 1515 source.__lookupGetter__(name); |
161 } | 1516 } |
162 var descriptor; | 1517 var descriptor; |
163 try { | 1518 try { |
164 descriptor = Object.getOwnPropertyDescriptor(source, name); | 1519 descriptor = Object.getOwnPropertyDescriptor(source, name); |
165 } catch (ex) { | 1520 } catch (ex) { |
166 // JSC and V8 both use data properties instead of accessors which can | 1521 // JSC and V8 both use data properties instead of accessors which can |
167 // cause getting the property desciptor to throw an exception. | 1522 // cause getting the property desciptor to throw an exception. |
168 // https://bugs.webkit.org/show_bug.cgi?id=49739 | 1523 // https://bugs.webkit.org/show_bug.cgi?id=49739 |
169 descriptor = dummyDescriptor; | 1524 descriptor = dummyDescriptor; |
170 } | 1525 } |
171 var getter, setter; | 1526 var getter, setter; |
172 if (allowMethod && typeof descriptor.value === 'function') { | 1527 if (allowMethod && typeof descriptor.value === 'function') { |
173 target[name] = function() { | 1528 target[name] = getMethod(name); |
174 return this.impl[name].apply(this.impl, arguments); | |
175 }; | |
176 return; | 1529 return; |
177 } | 1530 } |
178 | 1531 |
179 var isEvent = isEventHandlerName(name); | 1532 var isEvent = isEventHandlerName(name); |
180 if (isEvent) { | 1533 if (isEvent) |
181 getter = scope.getEventHandlerGetter(name); | 1534 getter = scope.getEventHandlerGetter(name); |
182 } else { | 1535 else |
183 getter = function() { | 1536 getter = getGetter(name); |
184 return this.impl[name]; | |
185 }; | |
186 } | |
187 | 1537 |
188 if (descriptor.writable || descriptor.set) { | 1538 if (descriptor.writable || descriptor.set) { |
189 if (isEvent) { | 1539 if (isEvent) |
190 setter = scope.getEventHandlerSetter(name); | 1540 setter = scope.getEventHandlerSetter(name); |
191 } else { | 1541 else |
192 setter = function(value) { | 1542 setter = getSetter(name); |
193 this.impl[name] = value; | |
194 }; | |
195 } | |
196 } | 1543 } |
197 | 1544 |
198 Object.defineProperty(target, name, { | 1545 Object.defineProperty(target, name, { |
199 get: getter, | 1546 get: getter, |
200 set: setter, | 1547 set: setter, |
201 configurable: descriptor.configurable, | 1548 configurable: descriptor.configurable, |
202 enumerable: descriptor.enumerable | 1549 enumerable: descriptor.enumerable |
203 }); | 1550 }); |
204 }); | 1551 }); |
205 } | 1552 } |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 * Wraps a node in a WrapperNode. If there already exists a wrapper for the | 1636 * Wraps a node in a WrapperNode. If there already exists a wrapper for the |
290 * |node| that wrapper is returned instead. | 1637 * |node| that wrapper is returned instead. |
291 * @param {Node} node | 1638 * @param {Node} node |
292 * @return {WrapperNode} | 1639 * @return {WrapperNode} |
293 */ | 1640 */ |
294 function wrap(impl) { | 1641 function wrap(impl) { |
295 if (impl === null) | 1642 if (impl === null) |
296 return null; | 1643 return null; |
297 | 1644 |
298 assert(isNative(impl)); | 1645 assert(isNative(impl)); |
299 var wrapper = wrapperTable.get(impl); | 1646 return impl.polymerWrapper_ || |
300 if (!wrapper) | 1647 (impl.polymerWrapper_ = new (getWrapperConstructor(impl))(impl)); |
301 wrapperTable.set(impl, wrapper = new (getWrapperConstructor(impl))(impl)); | |
302 return wrapper; | |
303 } | 1648 } |
304 | 1649 |
305 /** | 1650 /** |
306 * Unwraps a wrapper and returns the node it is wrapping. | 1651 * Unwraps a wrapper and returns the node it is wrapping. |
307 * @param {WrapperNode} wrapper | 1652 * @param {WrapperNode} wrapper |
308 * @return {Node} | 1653 * @return {Node} |
309 */ | 1654 */ |
310 function unwrap(wrapper) { | 1655 function unwrap(wrapper) { |
311 if (wrapper === null) | 1656 if (wrapper === null) |
312 return null; | 1657 return null; |
(...skipping 23 matching lines...) Expand all Loading... |
336 * Overrides the current wrapper (if any) for node. | 1681 * Overrides the current wrapper (if any) for node. |
337 * @param {Node} node | 1682 * @param {Node} node |
338 * @param {WrapperNode=} wrapper If left out the wrapper will be created as | 1683 * @param {WrapperNode=} wrapper If left out the wrapper will be created as |
339 * needed next time someone wraps the node. | 1684 * needed next time someone wraps the node. |
340 */ | 1685 */ |
341 function rewrap(node, wrapper) { | 1686 function rewrap(node, wrapper) { |
342 if (wrapper === null) | 1687 if (wrapper === null) |
343 return; | 1688 return; |
344 assert(isNative(node)); | 1689 assert(isNative(node)); |
345 assert(wrapper === undefined || isWrapper(wrapper)); | 1690 assert(wrapper === undefined || isWrapper(wrapper)); |
346 wrapperTable.set(node, wrapper); | 1691 node.polymerWrapper_ = wrapper; |
347 } | 1692 } |
348 | 1693 |
349 function defineGetter(constructor, name, getter) { | 1694 function defineGetter(constructor, name, getter) { |
350 Object.defineProperty(constructor.prototype, name, { | 1695 Object.defineProperty(constructor.prototype, name, { |
351 get: getter, | 1696 get: getter, |
352 configurable: true, | 1697 configurable: true, |
353 enumerable: true | 1698 enumerable: true |
354 }); | 1699 }); |
355 } | 1700 } |
356 | 1701 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 (function(scope) { | 1749 (function(scope) { |
405 'use strict'; | 1750 'use strict'; |
406 | 1751 |
407 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | 1752 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; |
408 var mixin = scope.mixin; | 1753 var mixin = scope.mixin; |
409 var registerWrapper = scope.registerWrapper; | 1754 var registerWrapper = scope.registerWrapper; |
410 var unwrap = scope.unwrap; | 1755 var unwrap = scope.unwrap; |
411 var wrap = scope.wrap; | 1756 var wrap = scope.wrap; |
412 var wrappers = scope.wrappers; | 1757 var wrappers = scope.wrappers; |
413 | 1758 |
414 var wrappedFuns = new SideTable(); | 1759 var wrappedFuns = new WeakMap(); |
415 var listenersTable = new SideTable(); | 1760 var listenersTable = new WeakMap(); |
416 var handledEventsTable = new SideTable(); | 1761 var handledEventsTable = new WeakMap(); |
417 var targetTable = new SideTable(); | 1762 var targetTable = new WeakMap(); |
418 var currentTargetTable = new SideTable(); | 1763 var currentTargetTable = new WeakMap(); |
419 var relatedTargetTable = new SideTable(); | 1764 var relatedTargetTable = new WeakMap(); |
420 var eventPhaseTable = new SideTable(); | 1765 var eventPhaseTable = new WeakMap(); |
421 var stopPropagationTable = new SideTable(); | 1766 var stopPropagationTable = new WeakMap(); |
422 var stopImmediatePropagationTable = new SideTable(); | 1767 var stopImmediatePropagationTable = new WeakMap(); |
423 var eventHandlersTable = new SideTable(); | 1768 var eventHandlersTable = new WeakMap(); |
424 var eventPathTable = new SideTable(); | 1769 var eventPathTable = new WeakMap(); |
425 | 1770 |
426 function isShadowRoot(node) { | 1771 function isShadowRoot(node) { |
427 return node instanceof wrappers.ShadowRoot; | 1772 return node instanceof wrappers.ShadowRoot; |
428 } | 1773 } |
429 | 1774 |
430 function isInsertionPoint(node) { | 1775 function isInsertionPoint(node) { |
431 var localName = node.localName; | 1776 var localName = node.localName; |
432 return localName === 'content' || localName === 'shadow'; | 1777 return localName === 'content' || localName === 'shadow'; |
433 } | 1778 } |
434 | 1779 |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 } | 1916 } |
572 | 1917 |
573 function enclosedBy(a, b) { | 1918 function enclosedBy(a, b) { |
574 if (a === b) | 1919 if (a === b) |
575 return true; | 1920 return true; |
576 if (a instanceof wrappers.ShadowRoot) { | 1921 if (a instanceof wrappers.ShadowRoot) { |
577 var host = scope.getHostForShadowRoot(a); | 1922 var host = scope.getHostForShadowRoot(a); |
578 return enclosedBy(rootOfNode(host), b); | 1923 return enclosedBy(rootOfNode(host), b); |
579 } | 1924 } |
580 return false; | 1925 return false; |
581 | |
582 } | 1926 } |
583 | 1927 |
584 function isMutationEvent(type) { | 1928 function isMutationEvent(type) { |
585 switch (type) { | 1929 switch (type) { |
586 case 'DOMAttrModified': | 1930 case 'DOMAttrModified': |
587 case 'DOMAttributeNameChanged': | 1931 case 'DOMAttributeNameChanged': |
588 case 'DOMCharacterDataModified': | 1932 case 'DOMCharacterDataModified': |
589 case 'DOMElementNameChanged': | 1933 case 'DOMElementNameChanged': |
590 case 'DOMNodeInserted': | 1934 case 'DOMNodeInserted': |
591 case 'DOMNodeInsertedIntoDocument': | 1935 case 'DOMNodeInsertedIntoDocument': |
(...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1035 } | 2379 } |
1036 } | 2380 } |
1037 } | 2381 } |
1038 | 2382 |
1039 if (found && count === 1) { | 2383 if (found && count === 1) { |
1040 var target = getTargetToListenAt(this); | 2384 var target = getTargetToListenAt(this); |
1041 target.removeEventListener_(type, dispatchOriginalEvent, true); | 2385 target.removeEventListener_(type, dispatchOriginalEvent, true); |
1042 } | 2386 } |
1043 }, | 2387 }, |
1044 dispatchEvent: function(event) { | 2388 dispatchEvent: function(event) { |
1045 scope.renderAllPending(); | |
1046 var target = getTargetToListenAt(this); | 2389 var target = getTargetToListenAt(this); |
1047 return target.dispatchEvent_(unwrap(event)); | 2390 return target.dispatchEvent_(unwrap(event)); |
1048 } | 2391 } |
1049 }; | 2392 }; |
1050 | 2393 |
1051 if (OriginalEventTarget) | 2394 if (OriginalEventTarget) |
1052 registerWrapper(OriginalEventTarget, EventTarget); | 2395 registerWrapper(OriginalEventTarget, EventTarget); |
1053 | 2396 |
1054 function wrapEventTargetMethods(constructors) { | 2397 function wrapEventTargetMethods(constructors) { |
1055 forwardMethodsToWrapper(constructors, methodNames); | 2398 forwardMethodsToWrapper(constructors, methodNames); |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1204 assert(node instanceof Node); | 2547 assert(node instanceof Node); |
1205 } | 2548 } |
1206 | 2549 |
1207 /** | 2550 /** |
1208 * Collects nodes from a DocumentFragment or a Node for removal followed | 2551 * Collects nodes from a DocumentFragment or a Node for removal followed |
1209 * by an insertion. | 2552 * by an insertion. |
1210 * | 2553 * |
1211 * This updates the internal pointers for node, previousNode and nextNode. | 2554 * This updates the internal pointers for node, previousNode and nextNode. |
1212 */ | 2555 */ |
1213 function collectNodes(node, parentNode, previousNode, nextNode) { | 2556 function collectNodes(node, parentNode, previousNode, nextNode) { |
1214 if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { | 2557 if (!(node instanceof DocumentFragment)) { |
1215 if (node.parentNode) | 2558 if (node.parentNode) |
1216 node.parentNode.removeChild(node); | 2559 node.parentNode.removeChild(node); |
1217 node.parentNode_ = parentNode; | 2560 node.parentNode_ = parentNode; |
1218 node.previousSibling_ = previousNode; | 2561 node.previousSibling_ = previousNode; |
1219 node.nextSibling_ = nextNode; | 2562 node.nextSibling_ = nextNode; |
1220 if (previousNode) | 2563 if (previousNode) |
1221 previousNode.nextSibling_ = node; | 2564 previousNode.nextSibling_ = node; |
1222 if (nextNode) | 2565 if (nextNode) |
1223 nextNode.previousSibling_ = node; | 2566 nextNode.previousSibling_ = node; |
1224 return [node]; | 2567 return [node]; |
(...skipping 13 matching lines...) Expand all Loading... |
1238 } | 2581 } |
1239 | 2582 |
1240 if (previousNode) | 2583 if (previousNode) |
1241 previousNode.nextSibling_ = nodes[0]; | 2584 previousNode.nextSibling_ = nodes[0]; |
1242 if (nextNode) | 2585 if (nextNode) |
1243 nextNode.previousSibling_ = nodes[nodes.length - 1]; | 2586 nextNode.previousSibling_ = nodes[nodes.length - 1]; |
1244 | 2587 |
1245 return nodes; | 2588 return nodes; |
1246 } | 2589 } |
1247 | 2590 |
| 2591 function collectNodesNoNeedToUpdatePointers(node) { |
| 2592 if (node instanceof DocumentFragment) { |
| 2593 var nodes = []; |
| 2594 var i = 0; |
| 2595 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 2596 nodes[i++] = child; |
| 2597 } |
| 2598 return nodes; |
| 2599 } |
| 2600 return [node]; |
| 2601 } |
| 2602 |
| 2603 function nodesWereAdded(nodes) { |
| 2604 for (var i = 0; i < nodes.length; i++) { |
| 2605 nodes[i].nodeWasAdded_(); |
| 2606 } |
| 2607 } |
| 2608 |
1248 function ensureSameOwnerDocument(parent, child) { | 2609 function ensureSameOwnerDocument(parent, child) { |
1249 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? | 2610 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? |
1250 parent : parent.ownerDocument; | 2611 parent : parent.ownerDocument; |
1251 if (ownerDoc !== child.ownerDocument) | 2612 if (ownerDoc !== child.ownerDocument) |
1252 ownerDoc.adoptNode(child); | 2613 ownerDoc.adoptNode(child); |
1253 } | 2614 } |
1254 | 2615 |
1255 function adoptNodesIfNeeded(owner, nodes) { | 2616 function adoptNodesIfNeeded(owner, nodes) { |
1256 if (!nodes.length) | 2617 if (!nodes.length) |
1257 return; | 2618 return; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1366 var originalReplaceChild = OriginalNode.prototype.replaceChild; | 2727 var originalReplaceChild = OriginalNode.prototype.replaceChild; |
1367 var originalRemoveChild = OriginalNode.prototype.removeChild; | 2728 var originalRemoveChild = OriginalNode.prototype.removeChild; |
1368 var originalCompareDocumentPosition = | 2729 var originalCompareDocumentPosition = |
1369 OriginalNode.prototype.compareDocumentPosition; | 2730 OriginalNode.prototype.compareDocumentPosition; |
1370 | 2731 |
1371 Node.prototype = Object.create(EventTarget.prototype); | 2732 Node.prototype = Object.create(EventTarget.prototype); |
1372 mixin(Node.prototype, { | 2733 mixin(Node.prototype, { |
1373 appendChild: function(childWrapper) { | 2734 appendChild: function(childWrapper) { |
1374 assertIsNodeWrapper(childWrapper); | 2735 assertIsNodeWrapper(childWrapper); |
1375 | 2736 |
| 2737 var nodes; |
| 2738 |
1376 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { | 2739 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { |
1377 var previousNode = this.lastChild; | 2740 var previousNode = this.lastChild; |
1378 var nextNode = null; | 2741 var nextNode = null; |
1379 var nodes = collectNodes(childWrapper, this, previousNode, nextNode); | 2742 nodes = collectNodes(childWrapper, this, previousNode, nextNode); |
1380 | 2743 |
1381 this.lastChild_ = nodes[nodes.length - 1]; | 2744 this.lastChild_ = nodes[nodes.length - 1]; |
1382 if (!previousNode) | 2745 if (!previousNode) |
1383 this.firstChild_ = nodes[0]; | 2746 this.firstChild_ = nodes[0]; |
1384 | 2747 |
1385 originalAppendChild.call(this.impl, unwrapNodesForInsertion(this, nodes)
); | 2748 originalAppendChild.call(this.impl, unwrapNodesForInsertion(this, nodes)
); |
1386 } else { | 2749 } else { |
| 2750 nodes = collectNodesNoNeedToUpdatePointers(childWrapper) |
1387 ensureSameOwnerDocument(this, childWrapper); | 2751 ensureSameOwnerDocument(this, childWrapper); |
1388 originalAppendChild.call(this.impl, unwrap(childWrapper)); | 2752 originalAppendChild.call(this.impl, unwrap(childWrapper)); |
1389 } | 2753 } |
1390 | 2754 |
1391 childWrapper.nodeWasAdded_(); | 2755 nodesWereAdded(nodes); |
1392 | 2756 |
1393 return childWrapper; | 2757 return childWrapper; |
1394 }, | 2758 }, |
1395 | 2759 |
1396 insertBefore: function(childWrapper, refWrapper) { | 2760 insertBefore: function(childWrapper, refWrapper) { |
1397 // TODO(arv): Unify with appendChild | 2761 // TODO(arv): Unify with appendChild |
1398 if (!refWrapper) | 2762 if (!refWrapper) |
1399 return this.appendChild(childWrapper); | 2763 return this.appendChild(childWrapper); |
1400 | 2764 |
1401 assertIsNodeWrapper(childWrapper); | 2765 assertIsNodeWrapper(childWrapper); |
1402 assertIsNodeWrapper(refWrapper); | 2766 assertIsNodeWrapper(refWrapper); |
1403 assert(refWrapper.parentNode === this); | 2767 assert(refWrapper.parentNode === this); |
1404 | 2768 |
| 2769 var nodes; |
| 2770 |
1405 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { | 2771 if (this.invalidateShadowRenderer() || invalidateParent(childWrapper)) { |
1406 var previousNode = refWrapper.previousSibling; | 2772 var previousNode = refWrapper.previousSibling; |
1407 var nextNode = refWrapper; | 2773 var nextNode = refWrapper; |
1408 var nodes = collectNodes(childWrapper, this, previousNode, nextNode); | 2774 nodes = collectNodes(childWrapper, this, previousNode, nextNode); |
1409 | 2775 |
1410 if (this.firstChild === refWrapper) | 2776 if (this.firstChild === refWrapper) |
1411 this.firstChild_ = nodes[0]; | 2777 this.firstChild_ = nodes[0]; |
1412 | 2778 |
1413 // insertBefore refWrapper no matter what the parent is? | 2779 // insertBefore refWrapper no matter what the parent is? |
1414 var refNode = unwrap(refWrapper); | 2780 var refNode = unwrap(refWrapper); |
1415 var parentNode = refNode.parentNode; | 2781 var parentNode = refNode.parentNode; |
1416 | 2782 |
1417 if (parentNode) { | 2783 if (parentNode) { |
1418 originalInsertBefore.call( | 2784 originalInsertBefore.call( |
1419 parentNode, | 2785 parentNode, |
1420 unwrapNodesForInsertion(this, nodes), | 2786 unwrapNodesForInsertion(this, nodes), |
1421 refNode); | 2787 refNode); |
1422 } else { | 2788 } else { |
1423 adoptNodesIfNeeded(this, nodes); | 2789 adoptNodesIfNeeded(this, nodes); |
1424 } | 2790 } |
1425 } else { | 2791 } else { |
| 2792 nodes = collectNodesNoNeedToUpdatePointers(childWrapper); |
1426 ensureSameOwnerDocument(this, childWrapper); | 2793 ensureSameOwnerDocument(this, childWrapper); |
1427 originalInsertBefore.call(this.impl, unwrap(childWrapper), | 2794 originalInsertBefore.call(this.impl, unwrap(childWrapper), |
1428 unwrap(refWrapper)); | 2795 unwrap(refWrapper)); |
1429 } | 2796 } |
1430 | 2797 |
1431 childWrapper.nodeWasAdded_(); | 2798 nodesWereAdded(nodes); |
1432 | 2799 |
1433 return childWrapper; | 2800 return childWrapper; |
1434 }, | 2801 }, |
1435 | 2802 |
1436 removeChild: function(childWrapper) { | 2803 removeChild: function(childWrapper) { |
1437 assertIsNodeWrapper(childWrapper); | 2804 assertIsNodeWrapper(childWrapper); |
1438 if (childWrapper.parentNode !== this) { | 2805 if (childWrapper.parentNode !== this) { |
1439 // TODO(arv): DOMException | 2806 // TODO(arv): DOMException |
1440 throw new Error('NotFoundError'); | 2807 throw new Error('NotFoundError'); |
1441 } | 2808 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1478 replaceChild: function(newChildWrapper, oldChildWrapper) { | 2845 replaceChild: function(newChildWrapper, oldChildWrapper) { |
1479 assertIsNodeWrapper(newChildWrapper); | 2846 assertIsNodeWrapper(newChildWrapper); |
1480 assertIsNodeWrapper(oldChildWrapper); | 2847 assertIsNodeWrapper(oldChildWrapper); |
1481 | 2848 |
1482 if (oldChildWrapper.parentNode !== this) { | 2849 if (oldChildWrapper.parentNode !== this) { |
1483 // TODO(arv): DOMException | 2850 // TODO(arv): DOMException |
1484 throw new Error('NotFoundError'); | 2851 throw new Error('NotFoundError'); |
1485 } | 2852 } |
1486 | 2853 |
1487 var oldChildNode = unwrap(oldChildWrapper); | 2854 var oldChildNode = unwrap(oldChildWrapper); |
| 2855 var nodes; |
1488 | 2856 |
1489 if (this.invalidateShadowRenderer() || | 2857 if (this.invalidateShadowRenderer() || |
1490 invalidateParent(newChildWrapper)) { | 2858 invalidateParent(newChildWrapper)) { |
1491 var previousNode = oldChildWrapper.previousSibling; | 2859 var previousNode = oldChildWrapper.previousSibling; |
1492 var nextNode = oldChildWrapper.nextSibling; | 2860 var nextNode = oldChildWrapper.nextSibling; |
1493 if (nextNode === newChildWrapper) | 2861 if (nextNode === newChildWrapper) |
1494 nextNode = newChildWrapper.nextSibling; | 2862 nextNode = newChildWrapper.nextSibling; |
1495 var nodes = collectNodes(newChildWrapper, this, | 2863 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode); |
1496 previousNode, nextNode); | |
1497 | 2864 |
1498 if (this.firstChild === oldChildWrapper) | 2865 if (this.firstChild === oldChildWrapper) |
1499 this.firstChild_ = nodes[0]; | 2866 this.firstChild_ = nodes[0]; |
1500 if (this.lastChild === oldChildWrapper) | 2867 if (this.lastChild === oldChildWrapper) |
1501 this.lastChild_ = nodes[nodes.length - 1]; | 2868 this.lastChild_ = nodes[nodes.length - 1]; |
1502 | 2869 |
1503 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = | 2870 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = |
1504 oldChildWrapper.parentNode_ = undefined; | 2871 oldChildWrapper.parentNode_ = undefined; |
1505 | 2872 |
1506 // replaceChild no matter what the parent is? | 2873 // replaceChild no matter what the parent is? |
1507 if (oldChildNode.parentNode) { | 2874 if (oldChildNode.parentNode) { |
1508 originalReplaceChild.call( | 2875 originalReplaceChild.call( |
1509 oldChildNode.parentNode, | 2876 oldChildNode.parentNode, |
1510 unwrapNodesForInsertion(this, nodes), | 2877 unwrapNodesForInsertion(this, nodes), |
1511 oldChildNode); | 2878 oldChildNode); |
1512 } | 2879 } |
1513 } else { | 2880 } else { |
| 2881 nodes = collectNodesNoNeedToUpdatePointers(newChildWrapper); |
1514 ensureSameOwnerDocument(this, newChildWrapper); | 2882 ensureSameOwnerDocument(this, newChildWrapper); |
1515 originalReplaceChild.call(this.impl, unwrap(newChildWrapper), | 2883 originalReplaceChild.call(this.impl, unwrap(newChildWrapper), |
1516 oldChildNode); | 2884 oldChildNode); |
1517 } | 2885 } |
1518 | 2886 |
1519 newChildWrapper.nodeWasAdded_(); | 2887 nodesWereAdded(nodes); |
1520 | 2888 |
1521 return oldChildWrapper; | 2889 return oldChildWrapper; |
1522 }, | 2890 }, |
1523 | 2891 |
1524 /** | 2892 /** |
1525 * Called after a node was added. Subclasses override this to invalidate | 2893 * Called after a node was added. Subclasses override this to invalidate |
1526 * the renderer as needed. | 2894 * the renderer as needed. |
1527 * @private | 2895 * @private |
1528 */ | 2896 */ |
1529 nodeWasAdded_: function() {}, | 2897 nodeWasAdded_: function() {}, |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1628 var parentNode = child.parentNode; | 2996 var parentNode = child.parentNode; |
1629 if (!parentNode) | 2997 if (!parentNode) |
1630 return false; | 2998 return false; |
1631 return this.contains(parentNode); | 2999 return this.contains(parentNode); |
1632 }, | 3000 }, |
1633 | 3001 |
1634 compareDocumentPosition: function(otherNode) { | 3002 compareDocumentPosition: function(otherNode) { |
1635 // This only wraps, it therefore only operates on the composed DOM and not | 3003 // This only wraps, it therefore only operates on the composed DOM and not |
1636 // the logical DOM. | 3004 // the logical DOM. |
1637 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode)); | 3005 return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode)); |
1638 }, | |
1639 | |
1640 // TODO(jmesserly): this is a workaround for | |
1641 // https://github.com/Polymer/ShadowDOM/issues/200 | |
1642 get ownerDocument() { | |
1643 scope.renderAllPending(); | |
1644 return wrap(this.impl.ownerDocument); | |
1645 } | 3006 } |
1646 }); | 3007 }); |
1647 | 3008 |
1648 // TODO(jmesserly): this is commented out to workaround: | 3009 defineWrapGetter(Node, 'ownerDocument'); |
1649 // https://github.com/Polymer/ShadowDOM/issues/200 | |
1650 //defineWrapGetter(Node, 'ownerDocument'); | |
1651 | 3010 |
1652 // We use a DocumentFragment as a base and then delete the properties of | 3011 // We use a DocumentFragment as a base and then delete the properties of |
1653 // DocumentFragment.prototype from the wrapper Node. Since delete makes | 3012 // DocumentFragment.prototype from the wrapper Node. Since delete makes |
1654 // objects slow in some JS engines we recreate the prototype object. | 3013 // objects slow in some JS engines we recreate the prototype object. |
1655 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); | 3014 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); |
1656 delete Node.prototype.querySelector; | 3015 delete Node.prototype.querySelector; |
1657 delete Node.prototype.querySelectorAll; | 3016 delete Node.prototype.querySelectorAll; |
1658 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); | 3017 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); |
1659 | 3018 |
1660 scope.wrappers.Node = Node; | 3019 scope.wrappers.Node = Node; |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1851 var GetElementsByInterface = scope.GetElementsByInterface; | 3210 var GetElementsByInterface = scope.GetElementsByInterface; |
1852 var Node = scope.wrappers.Node; | 3211 var Node = scope.wrappers.Node; |
1853 var ParentNodeInterface = scope.ParentNodeInterface; | 3212 var ParentNodeInterface = scope.ParentNodeInterface; |
1854 var SelectorsInterface = scope.SelectorsInterface; | 3213 var SelectorsInterface = scope.SelectorsInterface; |
1855 var addWrapNodeListMethod = scope.addWrapNodeListMethod; | 3214 var addWrapNodeListMethod = scope.addWrapNodeListMethod; |
1856 var mixin = scope.mixin; | 3215 var mixin = scope.mixin; |
1857 var oneOf = scope.oneOf; | 3216 var oneOf = scope.oneOf; |
1858 var registerWrapper = scope.registerWrapper; | 3217 var registerWrapper = scope.registerWrapper; |
1859 var wrappers = scope.wrappers; | 3218 var wrappers = scope.wrappers; |
1860 | 3219 |
1861 var shadowRootTable = new SideTable(); | 3220 var shadowRootTable = new WeakMap(); |
1862 var OriginalElement = window.Element; | 3221 var OriginalElement = window.Element; |
1863 | 3222 |
1864 | 3223 |
1865 var matchesName = oneOf(OriginalElement.prototype, [ | 3224 var matchesName = oneOf(OriginalElement.prototype, [ |
1866 'matches', | 3225 'matches', |
1867 'mozMatchesSelector', | 3226 'mozMatchesSelector', |
1868 'msMatchesSelector', | 3227 'msMatchesSelector', |
1869 'webkitMatchesSelector', | 3228 'webkitMatchesSelector', |
1870 ]); | 3229 ]); |
1871 | 3230 |
(...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2217 'use strict'; | 3576 'use strict'; |
2218 | 3577 |
2219 var HTMLElement = scope.wrappers.HTMLElement; | 3578 var HTMLElement = scope.wrappers.HTMLElement; |
2220 var getInnerHTML = scope.getInnerHTML; | 3579 var getInnerHTML = scope.getInnerHTML; |
2221 var mixin = scope.mixin; | 3580 var mixin = scope.mixin; |
2222 var registerWrapper = scope.registerWrapper; | 3581 var registerWrapper = scope.registerWrapper; |
2223 var setInnerHTML = scope.setInnerHTML; | 3582 var setInnerHTML = scope.setInnerHTML; |
2224 var unwrap = scope.unwrap; | 3583 var unwrap = scope.unwrap; |
2225 var wrap = scope.wrap; | 3584 var wrap = scope.wrap; |
2226 | 3585 |
2227 var contentTable = new SideTable(); | 3586 var contentTable = new WeakMap(); |
2228 var templateContentsOwnerTable = new SideTable(); | 3587 var templateContentsOwnerTable = new WeakMap(); |
2229 | 3588 |
2230 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner | 3589 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner |
2231 function getTemplateContentsOwner(doc) { | 3590 function getTemplateContentsOwner(doc) { |
2232 if (!doc.defaultView) | 3591 if (!doc.defaultView) |
2233 return doc; | 3592 return doc; |
2234 var d = templateContentsOwnerTable.get(doc); | 3593 var d = templateContentsOwnerTable.get(doc); |
2235 if (!d) { | 3594 if (!d) { |
2236 // TODO(arv): This should either be a Document or HTMLDocument depending | 3595 // TODO(arv): This should either be a Document or HTMLDocument depending |
2237 // on doc. | 3596 // on doc. |
2238 d = doc.implementation.createHTMLDocument(''); | 3597 d = doc.implementation.createHTMLDocument(''); |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2355 'use strict'; | 3714 'use strict'; |
2356 | 3715 |
2357 var DocumentFragment = scope.wrappers.DocumentFragment; | 3716 var DocumentFragment = scope.wrappers.DocumentFragment; |
2358 var elementFromPoint = scope.elementFromPoint; | 3717 var elementFromPoint = scope.elementFromPoint; |
2359 var getInnerHTML = scope.getInnerHTML; | 3718 var getInnerHTML = scope.getInnerHTML; |
2360 var mixin = scope.mixin; | 3719 var mixin = scope.mixin; |
2361 var rewrap = scope.rewrap; | 3720 var rewrap = scope.rewrap; |
2362 var setInnerHTML = scope.setInnerHTML; | 3721 var setInnerHTML = scope.setInnerHTML; |
2363 var unwrap = scope.unwrap; | 3722 var unwrap = scope.unwrap; |
2364 | 3723 |
2365 var shadowHostTable = new SideTable(); | 3724 var shadowHostTable = new WeakMap(); |
2366 var nextOlderShadowTreeTable = new SideTable(); | 3725 var nextOlderShadowTreeTable = new WeakMap(); |
2367 | 3726 |
2368 function ShadowRoot(hostWrapper) { | 3727 function ShadowRoot(hostWrapper) { |
2369 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); | 3728 var node = unwrap(hostWrapper.impl.ownerDocument.createDocumentFragment()); |
2370 DocumentFragment.call(this, node); | 3729 DocumentFragment.call(this, node); |
2371 | 3730 |
2372 // createDocumentFragment associates the node with a wrapper | 3731 // createDocumentFragment associates the node with a wrapper |
2373 // DocumentFragment instance. Override that. | 3732 // DocumentFragment instance. Override that. |
2374 rewrap(node, this); | 3733 rewrap(node, this); |
2375 | 3734 |
2376 var oldShadowRoot = hostWrapper.shadowRoot; | 3735 var oldShadowRoot = hostWrapper.shadowRoot; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2410 return shadowHostTable.get(node); | 3769 return shadowHostTable.get(node); |
2411 }; | 3770 }; |
2412 })(this.ShadowDOMPolyfill); | 3771 })(this.ShadowDOMPolyfill); |
2413 // Copyright 2013 The Polymer Authors. All rights reserved. | 3772 // Copyright 2013 The Polymer Authors. All rights reserved. |
2414 // Use of this source code is governed by a BSD-style | 3773 // Use of this source code is governed by a BSD-style |
2415 // license that can be found in the LICENSE file. | 3774 // license that can be found in the LICENSE file. |
2416 | 3775 |
2417 (function(scope) { | 3776 (function(scope) { |
2418 'use strict'; | 3777 'use strict'; |
2419 | 3778 |
| 3779 var Element = scope.wrappers.Element; |
2420 var HTMLContentElement = scope.wrappers.HTMLContentElement; | 3780 var HTMLContentElement = scope.wrappers.HTMLContentElement; |
2421 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | 3781 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; |
2422 var Node = scope.wrappers.Node; | 3782 var Node = scope.wrappers.Node; |
2423 var ShadowRoot = scope.wrappers.ShadowRoot; | 3783 var ShadowRoot = scope.wrappers.ShadowRoot; |
2424 var assert = scope.assert; | 3784 var assert = scope.assert; |
2425 var getHostForShadowRoot = scope.getHostForShadowRoot; | 3785 var getHostForShadowRoot = scope.getHostForShadowRoot; |
2426 var mixin = scope.mixin; | 3786 var mixin = scope.mixin; |
2427 var oneOf = scope.oneOf; | 3787 var oneOf = scope.oneOf; |
2428 var unwrap = scope.unwrap; | 3788 var unwrap = scope.unwrap; |
2429 var wrap = scope.wrap; | 3789 var wrap = scope.wrap; |
(...skipping 23 matching lines...) Expand all Loading... |
2453 function updateAllChildNodes(parentNodeWrapper) { | 3813 function updateAllChildNodes(parentNodeWrapper) { |
2454 assert(parentNodeWrapper instanceof Node); | 3814 assert(parentNodeWrapper instanceof Node); |
2455 for (var childWrapper = parentNodeWrapper.firstChild; | 3815 for (var childWrapper = parentNodeWrapper.firstChild; |
2456 childWrapper; | 3816 childWrapper; |
2457 childWrapper = childWrapper.nextSibling) { | 3817 childWrapper = childWrapper.nextSibling) { |
2458 updateWrapperUpAndSideways(childWrapper); | 3818 updateWrapperUpAndSideways(childWrapper); |
2459 } | 3819 } |
2460 updateWrapperDown(parentNodeWrapper); | 3820 updateWrapperDown(parentNodeWrapper); |
2461 } | 3821 } |
2462 | 3822 |
2463 // This object groups DOM operations. This is supposed to be the DOM as the | 3823 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) { |
2464 // browser/render tree sees it. | |
2465 // When changes are done to the visual DOM the logical DOM needs to be updated | |
2466 // to reflect the correct tree. | |
2467 function removeAllChildNodes(parentNodeWrapper) { | |
2468 var parentNode = unwrap(parentNodeWrapper); | 3824 var parentNode = unwrap(parentNodeWrapper); |
2469 updateAllChildNodes(parentNodeWrapper); | 3825 var newChild = unwrap(newChildWrapper); |
2470 if (parentNode.firstChild) | 3826 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null; |
2471 parentNode.textContent = ''; | |
2472 } | |
2473 | 3827 |
2474 function appendChild(parentNodeWrapper, childWrapper) { | 3828 remove(newChildWrapper); |
2475 var parentNode = unwrap(parentNodeWrapper); | 3829 updateWrapperUpAndSideways(newChildWrapper); |
2476 var child = unwrap(childWrapper); | |
2477 if (child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | |
2478 updateAllChildNodes(childWrapper); | |
2479 | 3830 |
| 3831 if (!refChildWrapper) { |
| 3832 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; |
| 3833 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) |
| 3834 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild; |
| 3835 |
| 3836 var lastChildWrapper = wrap(parentNode.lastChild); |
| 3837 if (lastChildWrapper) |
| 3838 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling; |
2480 } else { | 3839 } else { |
2481 remove(childWrapper); | 3840 if (parentNodeWrapper.firstChild === refChildWrapper) |
2482 updateWrapperUpAndSideways(childWrapper); | 3841 parentNodeWrapper.firstChild_ = refChildWrapper; |
| 3842 |
| 3843 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling; |
2483 } | 3844 } |
2484 | 3845 |
2485 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; | 3846 parentNode.insertBefore(newChild, refChild); |
2486 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) | |
2487 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild; | |
2488 | |
2489 var lastChildWrapper = wrap(parentNode.lastChild); | |
2490 if (lastChildWrapper) { | |
2491 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling; | |
2492 } | |
2493 | |
2494 parentNode.appendChild(child); | |
2495 } | |
2496 | |
2497 function removeChild(parentNodeWrapper, childWrapper) { | |
2498 var parentNode = unwrap(parentNodeWrapper); | |
2499 var child = unwrap(childWrapper); | |
2500 | |
2501 updateWrapperUpAndSideways(childWrapper); | |
2502 | |
2503 if (childWrapper.previousSibling) | |
2504 childWrapper.previousSibling.nextSibling_ = childWrapper; | |
2505 if (childWrapper.nextSibling) | |
2506 childWrapper.nextSibling.previousSibling_ = childWrapper; | |
2507 | |
2508 if (parentNodeWrapper.lastChild === childWrapper) | |
2509 parentNodeWrapper.lastChild_ = childWrapper; | |
2510 if (parentNodeWrapper.firstChild === childWrapper) | |
2511 parentNodeWrapper.firstChild_ = childWrapper; | |
2512 | |
2513 parentNode.removeChild(child); | |
2514 } | 3847 } |
2515 | 3848 |
2516 function remove(nodeWrapper) { | 3849 function remove(nodeWrapper) { |
2517 var node = unwrap(nodeWrapper) | 3850 var node = unwrap(nodeWrapper) |
2518 var parentNode = node.parentNode; | 3851 var parentNode = node.parentNode; |
2519 if (parentNode) | 3852 if (!parentNode) |
2520 removeChild(wrap(parentNode), nodeWrapper); | 3853 return; |
| 3854 |
| 3855 var parentNodeWrapper = wrap(parentNode); |
| 3856 updateWrapperUpAndSideways(nodeWrapper); |
| 3857 |
| 3858 if (nodeWrapper.previousSibling) |
| 3859 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper; |
| 3860 if (nodeWrapper.nextSibling) |
| 3861 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper; |
| 3862 |
| 3863 if (parentNodeWrapper.lastChild === nodeWrapper) |
| 3864 parentNodeWrapper.lastChild_ = nodeWrapper; |
| 3865 if (parentNodeWrapper.firstChild === nodeWrapper) |
| 3866 parentNodeWrapper.firstChild_ = nodeWrapper; |
| 3867 |
| 3868 parentNode.removeChild(node); |
2521 } | 3869 } |
2522 | 3870 |
2523 var distributedChildNodesTable = new SideTable(); | 3871 var distributedChildNodesTable = new WeakMap(); |
2524 var eventParentsTable = new SideTable(); | 3872 var eventParentsTable = new WeakMap(); |
2525 var insertionParentTable = new SideTable(); | 3873 var insertionParentTable = new WeakMap(); |
2526 var rendererForHostTable = new SideTable(); | 3874 var rendererForHostTable = new WeakMap(); |
2527 var shadowDOMRendererTable = new SideTable(); | 3875 var shadowDOMRendererTable = new WeakMap(); |
2528 | |
2529 var reprCounter = 0; | |
2530 | |
2531 function repr(node) { | |
2532 if (!node.displayName) | |
2533 node.displayName = node.nodeName + '-' + ++reprCounter; | |
2534 return node.displayName; | |
2535 } | |
2536 | 3876 |
2537 function distributeChildToInsertionPoint(child, insertionPoint) { | 3877 function distributeChildToInsertionPoint(child, insertionPoint) { |
2538 getDistributedChildNodes(insertionPoint).push(child); | 3878 getDistributedChildNodes(insertionPoint).push(child); |
2539 assignToInsertionPoint(child, insertionPoint); | 3879 assignToInsertionPoint(child, insertionPoint); |
2540 | 3880 |
2541 var eventParents = eventParentsTable.get(child); | 3881 var eventParents = eventParentsTable.get(child); |
2542 if (!eventParents) | 3882 if (!eventParents) |
2543 eventParentsTable.set(child, eventParents = []); | 3883 eventParentsTable.set(child, eventParents = []); |
2544 eventParents.push(insertionPoint); | 3884 eventParents.push(insertionPoint); |
2545 } | 3885 } |
(...skipping 16 matching lines...) Expand all Loading... |
2562 | 3902 |
2563 /** | 3903 /** |
2564 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor| | 3904 * Visits all nodes in the tree that fulfils the |predicate|. If the |visitor| |
2565 * function returns |false| the traversal is aborted. | 3905 * function returns |false| the traversal is aborted. |
2566 * @param {!Node} tree | 3906 * @param {!Node} tree |
2567 * @param {function(!Node) : boolean} predicate | 3907 * @param {function(!Node) : boolean} predicate |
2568 * @param {function(!Node) : *} visitor | 3908 * @param {function(!Node) : *} visitor |
2569 */ | 3909 */ |
2570 function visit(tree, predicate, visitor) { | 3910 function visit(tree, predicate, visitor) { |
2571 // This operates on logical DOM. | 3911 // This operates on logical DOM. |
2572 var nodes = getChildNodesSnapshot(tree); | 3912 for (var node = tree.firstChild; node; node = node.nextSibling) { |
2573 for (var i = 0; i < nodes.length; i++) { | |
2574 var node = nodes[i]; | |
2575 if (predicate(node)) { | 3913 if (predicate(node)) { |
2576 if (visitor(node) === false) | 3914 if (visitor(node) === false) |
2577 return; | 3915 return; |
2578 } else { | 3916 } else { |
2579 visit(node, predicate, visitor); | 3917 visit(node, predicate, visitor); |
2580 } | 3918 } |
2581 } | 3919 } |
2582 } | 3920 } |
2583 | 3921 |
2584 // Matching Insertion Points | 3922 // Matching Insertion Points |
(...skipping 30 matching lines...) Expand all Loading... |
2615 function matchesCriteria(node, point) { | 3953 function matchesCriteria(node, point) { |
2616 var select = point.getAttribute('select'); | 3954 var select = point.getAttribute('select'); |
2617 if (!select) | 3955 if (!select) |
2618 return true; | 3956 return true; |
2619 | 3957 |
2620 // Here we know the select attribute is a non empty string. | 3958 // Here we know the select attribute is a non empty string. |
2621 select = select.trim(); | 3959 select = select.trim(); |
2622 if (!select) | 3960 if (!select) |
2623 return true; | 3961 return true; |
2624 | 3962 |
2625 if (node.nodeType !== Node.ELEMENT_NODE) | 3963 if (!(node instanceof Element)) |
2626 return false; | 3964 return false; |
2627 | 3965 |
2628 // TODO(arv): This does not seem right. Need to check for a simple selector. | 3966 // TODO(arv): This does not seem right. Need to check for a simple selector. |
2629 if (!selectorMatchRegExp.test(select)) | 3967 if (!selectorMatchRegExp.test(select)) |
2630 return false; | 3968 return false; |
2631 | 3969 |
2632 if (select[0] === ':' &&!allowedPseudoRegExp.test(select)) | 3970 if (select[0] === ':' && !allowedPseudoRegExp.test(select)) |
2633 return false; | 3971 return false; |
2634 | 3972 |
2635 try { | 3973 try { |
2636 return node.matches(select); | 3974 return node.matches(select); |
2637 } catch (ex) { | 3975 } catch (ex) { |
2638 // Invalid selector. | 3976 // Invalid selector. |
2639 return false; | 3977 return false; |
2640 } | 3978 } |
2641 } | 3979 } |
2642 | 3980 |
2643 var request = oneOf(window, [ | 3981 var request = oneOf(window, [ |
2644 'requestAnimationFrame', | 3982 'requestAnimationFrame', |
2645 'mozRequestAnimationFrame', | 3983 'mozRequestAnimationFrame', |
2646 'webkitRequestAnimationFrame', | 3984 'webkitRequestAnimationFrame', |
2647 'setTimeout' | 3985 'setTimeout' |
2648 ]); | 3986 ]); |
2649 | 3987 |
2650 var pendingDirtyRenderers = []; | 3988 var pendingDirtyRenderers = []; |
2651 var renderTimer; | 3989 var renderTimer; |
2652 | 3990 |
2653 function renderAllPending() { | 3991 function renderAllPending() { |
2654 renderTimer = null; | 3992 for (var i = 0; i < pendingDirtyRenderers.length; i++) { |
2655 pendingDirtyRenderers.forEach(function(owner) { | 3993 pendingDirtyRenderers[i].render(); |
2656 owner.render(); | 3994 } |
2657 }); | |
2658 pendingDirtyRenderers = []; | 3995 pendingDirtyRenderers = []; |
2659 } | 3996 } |
2660 | 3997 |
2661 function ShadowRenderer(host) { | 3998 function handleRequestAnimationFrame() { |
2662 this.host = host; | 3999 renderTimer = null; |
2663 this.dirty = false; | 4000 renderAllPending(); |
2664 this.invalidateAttributes(); | |
2665 this.associateNode(host); | |
2666 } | 4001 } |
2667 | 4002 |
2668 /** | 4003 /** |
2669 * Returns existing shadow renderer for a host or creates it if it is needed. | 4004 * Returns existing shadow renderer for a host or creates it if it is needed. |
2670 * @params {!Element} host | 4005 * @params {!Element} host |
2671 * @return {!ShadowRenderer} | 4006 * @return {!ShadowRenderer} |
2672 */ | 4007 */ |
2673 function getRendererForHost(host) { | 4008 function getRendererForHost(host) { |
2674 var renderer = rendererForHostTable.get(host); | 4009 var renderer = rendererForHostTable.get(host); |
2675 if (!renderer) { | 4010 if (!renderer) { |
2676 renderer = new ShadowRenderer(host); | 4011 renderer = new ShadowRenderer(host); |
2677 rendererForHostTable.set(host, renderer); | 4012 rendererForHostTable.set(host, renderer); |
2678 } | 4013 } |
2679 return renderer; | 4014 return renderer; |
2680 } | 4015 } |
2681 | 4016 |
2682 function getShadowRootAncestor(node) { | 4017 function getShadowRootAncestor(node) { |
2683 for (; node; node = node.parentNode) { | 4018 for (; node; node = node.parentNode) { |
2684 if (node instanceof ShadowRoot) | 4019 if (node instanceof ShadowRoot) |
2685 return node; | 4020 return node; |
2686 } | 4021 } |
2687 return null; | 4022 return null; |
2688 } | 4023 } |
2689 | 4024 |
2690 function getRendererForShadowRoot(shadowRoot) { | 4025 function getRendererForShadowRoot(shadowRoot) { |
2691 return getRendererForHost(getHostForShadowRoot(shadowRoot)); | 4026 return getRendererForHost(getHostForShadowRoot(shadowRoot)); |
2692 } | 4027 } |
2693 | 4028 |
| 4029 var spliceDiff = new ArraySplice(); |
| 4030 spliceDiff.equals = function(renderNode, rawNode) { |
| 4031 return unwrap(renderNode.node) === rawNode; |
| 4032 }; |
| 4033 |
| 4034 /** |
| 4035 * RenderNode is used as an in memory "render tree". When we render the |
| 4036 * composed tree we create a tree of RenderNodes, then we diff this against |
| 4037 * the real DOM tree and make minimal changes as needed. |
| 4038 */ |
| 4039 function RenderNode(node) { |
| 4040 this.skip = false; |
| 4041 this.node = node; |
| 4042 this.childNodes = []; |
| 4043 } |
| 4044 |
| 4045 RenderNode.prototype = { |
| 4046 append: function(node) { |
| 4047 var rv = new RenderNode(node); |
| 4048 this.childNodes.push(rv); |
| 4049 return rv; |
| 4050 }, |
| 4051 |
| 4052 sync: function(opt_added) { |
| 4053 if (this.skip) |
| 4054 return; |
| 4055 |
| 4056 var nodeWrapper = this.node; |
| 4057 // plain array of RenderNodes |
| 4058 var newChildren = this.childNodes; |
| 4059 // plain array of real nodes. |
| 4060 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper)); |
| 4061 var added = opt_added || new WeakMap(); |
| 4062 |
| 4063 var splices = spliceDiff.calculateSplices(newChildren, oldChildren); |
| 4064 |
| 4065 var newIndex = 0, oldIndex = 0; |
| 4066 var lastIndex = 0; |
| 4067 for (var i = 0; i < splices.length; i++) { |
| 4068 var splice = splices[i]; |
| 4069 for (; lastIndex < splice.index; lastIndex++) { |
| 4070 oldIndex++; |
| 4071 newChildren[newIndex++].sync(added); |
| 4072 } |
| 4073 |
| 4074 var removedCount = splice.removed.length; |
| 4075 for (var j = 0; j < removedCount; j++) { |
| 4076 var wrapper = wrap(oldChildren[oldIndex++]); |
| 4077 if (!added.get(wrapper)) |
| 4078 remove(wrapper); |
| 4079 } |
| 4080 |
| 4081 var addedCount = splice.addedCount; |
| 4082 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]); |
| 4083 for (var j = 0; j < addedCount; j++) { |
| 4084 var newChildRenderNode = newChildren[newIndex++]; |
| 4085 var newChildWrapper = newChildRenderNode.node; |
| 4086 insertBefore(nodeWrapper, newChildWrapper, refNode); |
| 4087 |
| 4088 // Keep track of added so that we do not remove the node after it |
| 4089 // has been added. |
| 4090 added.set(newChildWrapper, true); |
| 4091 |
| 4092 newChildRenderNode.sync(added); |
| 4093 } |
| 4094 |
| 4095 lastIndex += addedCount; |
| 4096 } |
| 4097 |
| 4098 for (var i = lastIndex; i < newChildren.length; i++) { |
| 4099 newChildren[i++].sync(added); |
| 4100 } |
| 4101 } |
| 4102 }; |
| 4103 |
| 4104 function ShadowRenderer(host) { |
| 4105 this.host = host; |
| 4106 this.dirty = false; |
| 4107 this.invalidateAttributes(); |
| 4108 this.associateNode(host); |
| 4109 } |
| 4110 |
2694 ShadowRenderer.prototype = { | 4111 ShadowRenderer.prototype = { |
2695 | 4112 |
2696 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees | 4113 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees |
2697 render: function() { | 4114 render: function(opt_renderNode) { |
2698 if (!this.dirty) | 4115 if (!this.dirty) |
2699 return; | 4116 return; |
2700 | 4117 |
2701 this.invalidateAttributes(); | 4118 this.invalidateAttributes(); |
2702 this.treeComposition(); | 4119 this.treeComposition(); |
2703 | 4120 |
2704 var host = this.host; | 4121 var host = this.host; |
2705 var shadowDOM = host.shadowRoot; | 4122 var shadowRoot = host.shadowRoot; |
2706 | 4123 |
2707 this.removeAllChildNodes(this.host); | 4124 this.associateNode(host); |
| 4125 var topMostRenderer = !renderNode; |
| 4126 var renderNode = opt_renderNode || new RenderNode(host); |
2708 | 4127 |
2709 var shadowDOMChildNodes = getChildNodesSnapshot(shadowDOM); | 4128 for (var node = shadowRoot.firstChild; node; node = node.nextSibling) { |
2710 shadowDOMChildNodes.forEach(function(node) { | 4129 this.renderNode(shadowRoot, renderNode, node, false); |
2711 this.renderNode(host, shadowDOM, node, false); | 4130 } |
2712 }, this); | 4131 |
| 4132 if (topMostRenderer) |
| 4133 renderNode.sync(); |
2713 | 4134 |
2714 this.dirty = false; | 4135 this.dirty = false; |
2715 }, | 4136 }, |
2716 | 4137 |
2717 invalidate: function() { | 4138 invalidate: function() { |
2718 if (!this.dirty) { | 4139 if (!this.dirty) { |
2719 this.dirty = true; | 4140 this.dirty = true; |
2720 pendingDirtyRenderers.push(this); | 4141 pendingDirtyRenderers.push(this); |
2721 if (renderTimer) | 4142 if (renderTimer) |
2722 return; | 4143 return; |
2723 renderTimer = window[request](renderAllPending, 0); | 4144 renderTimer = window[request](handleRequestAnimationFrame, 0); |
2724 } | 4145 } |
2725 }, | 4146 }, |
2726 | 4147 |
2727 renderNode: function(visualParent, tree, node, isNested) { | 4148 renderNode: function(shadowRoot, renderNode, node, isNested) { |
2728 if (isShadowHost(node)) { | 4149 if (isShadowHost(node)) { |
2729 this.appendChild(visualParent, node); | 4150 renderNode = renderNode.append(node); |
2730 var renderer = getRendererForHost(node); | 4151 var renderer = getRendererForHost(node); |
2731 renderer.dirty = true; // Need to rerender due to reprojection. | 4152 renderer.dirty = true; // Need to rerender due to reprojection. |
2732 renderer.render(); | 4153 renderer.render(renderNode); |
2733 } else if (isInsertionPoint(node)) { | 4154 } else if (isInsertionPoint(node)) { |
2734 this.renderInsertionPoint(visualParent, tree, node, isNested); | 4155 this.renderInsertionPoint(shadowRoot, renderNode, node, isNested); |
2735 } else if (isShadowInsertionPoint(node)) { | 4156 } else if (isShadowInsertionPoint(node)) { |
2736 this.renderShadowInsertionPoint(visualParent, tree, node); | 4157 this.renderShadowInsertionPoint(shadowRoot, renderNode, node); |
2737 } else { | 4158 } else { |
2738 this.renderAsAnyDomTree(visualParent, tree, node, isNested); | 4159 this.renderAsAnyDomTree(shadowRoot, renderNode, node, isNested); |
2739 } | 4160 } |
2740 }, | 4161 }, |
2741 | 4162 |
2742 renderAsAnyDomTree: function(visualParent, tree, node, isNested) { | 4163 renderAsAnyDomTree: function(shadowRoot, renderNode, node, isNested) { |
2743 this.appendChild(visualParent, node); | 4164 renderNode = renderNode.append(node); |
2744 | 4165 |
2745 if (isShadowHost(node)) { | 4166 if (isShadowHost(node)) { |
2746 render(node); | 4167 var renderer = getRendererForHost(node); |
| 4168 renderNode.skip = !renderer.dirty; |
| 4169 renderer.render(renderNode); |
2747 } else { | 4170 } else { |
2748 var parent = node; | 4171 for (var child = node.firstChild; child; child = child.nextSibling) { |
2749 var logicalChildNodes = getChildNodesSnapshot(parent); | 4172 this.renderNode(shadowRoot, renderNode, child, isNested); |
2750 // We associate the parent of a content/shadow with the renderer | 4173 } |
2751 // because we may need to remove stale childNodes. | |
2752 if (shadowDOMRendererTable.get(parent)) | |
2753 this.removeAllChildNodes(parent); | |
2754 logicalChildNodes.forEach(function(node) { | |
2755 this.renderNode(parent, tree, node, isNested); | |
2756 }, this); | |
2757 } | 4174 } |
2758 }, | 4175 }, |
2759 | 4176 |
2760 renderInsertionPoint: function(visualParent, tree, insertionPoint, isNested)
{ | 4177 renderInsertionPoint: function(shadowRoot, renderNode, insertionPoint, |
| 4178 isNested) { |
2761 var distributedChildNodes = getDistributedChildNodes(insertionPoint); | 4179 var distributedChildNodes = getDistributedChildNodes(insertionPoint); |
2762 if (distributedChildNodes.length) { | 4180 if (distributedChildNodes.length) { |
2763 this.removeAllChildNodes(insertionPoint); | 4181 this.associateNode(insertionPoint); |
2764 | 4182 |
2765 distributedChildNodes.forEach(function(child) { | 4183 for (var i = 0; i < distributedChildNodes.length; i++) { |
| 4184 var child = distributedChildNodes[i]; |
2766 if (isInsertionPoint(child) && isNested) | 4185 if (isInsertionPoint(child) && isNested) |
2767 this.renderInsertionPoint(visualParent, tree, child, isNested); | 4186 this.renderInsertionPoint(shadowRoot, renderNode, child, isNested); |
2768 else | 4187 else |
2769 this.renderAsAnyDomTree(visualParent, tree, child, isNested); | 4188 this.renderAsAnyDomTree(shadowRoot, renderNode, child, isNested); |
2770 }, this); | 4189 } |
2771 } else { | 4190 } else { |
2772 this.renderFallbackContent(visualParent, insertionPoint); | 4191 this.renderFallbackContent(shadowRoot, renderNode, insertionPoint); |
2773 } | 4192 } |
2774 this.remove(insertionPoint); | 4193 this.associateNode(insertionPoint.parentNode); |
2775 }, | 4194 }, |
2776 | 4195 |
2777 renderShadowInsertionPoint: function(visualParent, tree, shadowInsertionPoin
t) { | 4196 renderShadowInsertionPoint: function(shadowRoot, renderNode, |
2778 var nextOlderTree = tree.olderShadowRoot; | 4197 shadowInsertionPoint) { |
| 4198 var nextOlderTree = shadowRoot.olderShadowRoot; |
2779 if (nextOlderTree) { | 4199 if (nextOlderTree) { |
2780 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint); | 4200 assignToInsertionPoint(nextOlderTree, shadowInsertionPoint); |
2781 this.remove(shadowInsertionPoint); | 4201 this.associateNode(shadowInsertionPoint.parentNode); |
2782 var shadowDOMChildNodes = getChildNodesSnapshot(nextOlderTree); | 4202 for (var node = nextOlderTree.firstChild; |
2783 shadowDOMChildNodes.forEach(function(node) { | 4203 node; |
2784 this.renderNode(visualParent, nextOlderTree, node, true); | 4204 node = node.nextSibling) { |
2785 }, this); | 4205 this.renderNode(nextOlderTree, renderNode, node, true); |
| 4206 } |
2786 } else { | 4207 } else { |
2787 this.renderFallbackContent(visualParent, shadowInsertionPoint); | 4208 this.renderFallbackContent(shadowRoot, renderNode, |
| 4209 shadowInsertionPoint); |
2788 } | 4210 } |
2789 }, | 4211 }, |
2790 | 4212 |
2791 renderFallbackContent: function (visualParent, fallbackHost) { | 4213 renderFallbackContent: function(shadowRoot, renderNode, fallbackHost) { |
2792 var logicalChildNodes = getChildNodesSnapshot(fallbackHost); | |
2793 this.associateNode(fallbackHost); | 4214 this.associateNode(fallbackHost); |
2794 this.remove(fallbackHost); | 4215 this.associateNode(fallbackHost.parentNode); |
2795 logicalChildNodes.forEach(function(node) { | 4216 for (var node = fallbackHost.firstChild; node; node = node.nextSibling) { |
2796 this.appendChild(visualParent, node); | 4217 this.renderAsAnyDomTree(shadowRoot, renderNode, node, false); |
2797 }, this); | 4218 } |
2798 }, | 4219 }, |
2799 | 4220 |
2800 /** | 4221 /** |
2801 * Invalidates the attributes used to keep track of which attributes may | 4222 * Invalidates the attributes used to keep track of which attributes may |
2802 * cause the renderer to be invalidated. | 4223 * cause the renderer to be invalidated. |
2803 */ | 4224 */ |
2804 invalidateAttributes: function() { | 4225 invalidateAttributes: function() { |
2805 this.attributes = Object.create(null); | 4226 this.attributes = Object.create(null); |
2806 }, | 4227 }, |
2807 | 4228 |
(...skipping 22 matching lines...) Expand all Loading... |
2830 | 4251 |
2831 // Pseudo selectors have been removed from the spec. | 4252 // Pseudo selectors have been removed from the spec. |
2832 }, | 4253 }, |
2833 | 4254 |
2834 dependsOnAttribute: function(name) { | 4255 dependsOnAttribute: function(name) { |
2835 return this.attributes[name]; | 4256 return this.attributes[name]; |
2836 }, | 4257 }, |
2837 | 4258 |
2838 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-distribution-algorithm | 4259 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-distribution-algorithm |
2839 distribute: function(tree, pool) { | 4260 distribute: function(tree, pool) { |
2840 var anyRemoved = false; | |
2841 var self = this; | 4261 var self = this; |
2842 | 4262 |
2843 visit(tree, isActiveInsertionPoint, | 4263 visit(tree, isActiveInsertionPoint, |
2844 function(insertionPoint) { | 4264 function(insertionPoint) { |
2845 resetDistributedChildNodes(insertionPoint); | 4265 resetDistributedChildNodes(insertionPoint); |
2846 self.updateDependentAttributes( | 4266 self.updateDependentAttributes( |
2847 insertionPoint.getAttribute('select')); | 4267 insertionPoint.getAttribute('select')); |
2848 | 4268 |
2849 for (var i = 0; i < pool.length; i++) { // 1.2 | 4269 for (var i = 0; i < pool.length; i++) { // 1.2 |
2850 var node = pool[i]; // 1.2.1 | 4270 var node = pool[i]; // 1.2.1 |
2851 if (node === undefined) // removed | 4271 if (node === undefined) // removed |
2852 continue; | 4272 continue; |
2853 if (matchesCriteria(node, insertionPoint)) { // 1.2.2 | 4273 if (matchesCriteria(node, insertionPoint)) { // 1.2.2 |
2854 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2
.1 | 4274 distributeChildToInsertionPoint(node, insertionPoint); // 1.2.2
.1 |
2855 pool[i] = undefined; // 1.2.2.2 | 4275 pool[i] = undefined; // 1.2.2.2 |
2856 anyRemoved = true; | |
2857 } | 4276 } |
2858 } | 4277 } |
2859 }); | 4278 }); |
2860 | |
2861 if (!anyRemoved) | |
2862 return pool; | |
2863 | |
2864 return pool.filter(function(item) { | |
2865 return item !== undefined; | |
2866 }); | |
2867 }, | 4279 }, |
2868 | 4280 |
2869 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-tree-composition | 4281 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#d
fn-tree-composition |
2870 treeComposition: function () { | 4282 treeComposition: function () { |
2871 var shadowHost = this.host; | 4283 var shadowHost = this.host; |
2872 var tree = shadowHost.shadowRoot; // 1. | 4284 var tree = shadowHost.shadowRoot; // 1. |
2873 var pool = []; // 2. | 4285 var pool = []; // 2. |
2874 var shadowHostChildNodes = getChildNodesSnapshot(shadowHost); | 4286 |
2875 shadowHostChildNodes.forEach(function(child) { // 3. | 4287 for (var child = shadowHost.firstChild; |
| 4288 child; |
| 4289 child = child.nextSibling) { // 3. |
2876 if (isInsertionPoint(child)) { // 3.2. | 4290 if (isInsertionPoint(child)) { // 3.2. |
2877 var reprojected = getDistributedChildNodes(child); // 3.2.1. | 4291 var reprojected = getDistributedChildNodes(child); // 3.2.1. |
2878 // if reprojected is undef... reset it? | 4292 // if reprojected is undef... reset it? |
2879 if (!reprojected || !reprojected.length) // 3.2.2. | 4293 if (!reprojected || !reprojected.length) // 3.2.2. |
2880 reprojected = getChildNodesSnapshot(child); | 4294 reprojected = getChildNodesSnapshot(child); |
2881 pool.push.apply(pool, reprojected); // 3.2.3. | 4295 pool.push.apply(pool, reprojected); // 3.2.3. |
2882 } else { | 4296 } else { |
2883 pool.push(child); // 3.3. | 4297 pool.push(child); // 3.3. |
2884 } | 4298 } |
2885 }); | 4299 } |
2886 | 4300 |
2887 var shadowInsertionPoint, point; | 4301 var shadowInsertionPoint, point; |
2888 while (tree) { // 4. | 4302 while (tree) { // 4. |
2889 // 4.1. | 4303 // 4.1. |
2890 shadowInsertionPoint = undefined; // Reset every iteration. | 4304 shadowInsertionPoint = undefined; // Reset every iteration. |
2891 visit(tree, isActiveShadowInsertionPoint, function(point) { | 4305 visit(tree, isActiveShadowInsertionPoint, function(point) { |
2892 shadowInsertionPoint = point; | 4306 shadowInsertionPoint = point; |
2893 return false; | 4307 return false; |
2894 }); | 4308 }); |
2895 point = shadowInsertionPoint; | 4309 point = shadowInsertionPoint; |
2896 | 4310 |
2897 pool = this.distribute(tree, pool); // 4.2. | 4311 this.distribute(tree, pool); // 4.2. |
2898 if (point) { // 4.3. | 4312 if (point) { // 4.3. |
2899 var nextOlderTree = tree.olderShadowRoot; // 4.3.1. | 4313 var nextOlderTree = tree.olderShadowRoot; // 4.3.1. |
2900 if (!nextOlderTree) { | 4314 if (!nextOlderTree) { |
2901 break; // 4.3.1.1. | 4315 break; // 4.3.1.1. |
2902 } else { | 4316 } else { |
2903 tree = nextOlderTree; // 4.3.2.2. | 4317 tree = nextOlderTree; // 4.3.2.2. |
2904 assignToInsertionPoint(tree, point); // 4.3.2.2. | 4318 assignToInsertionPoint(tree, point); // 4.3.2.2. |
2905 continue; // 4.3.2.3. | 4319 continue; // 4.3.2.3. |
2906 } | 4320 } |
2907 } else { | 4321 } else { |
2908 break; // 4.4. | 4322 break; // 4.4. |
2909 } | 4323 } |
2910 } | 4324 } |
2911 }, | 4325 }, |
2912 | 4326 |
2913 appendChild: function(parent, child) { | |
2914 // this.associateNode(child); | |
2915 this.associateNode(parent); | |
2916 appendChild(parent, child); | |
2917 }, | |
2918 | |
2919 remove: function(node) { | |
2920 // this.associateNode(node); | |
2921 this.associateNode(node.parentNode); | |
2922 remove(node); | |
2923 }, | |
2924 | |
2925 removeAllChildNodes: function(parent) { | |
2926 this.associateNode(parent); | |
2927 removeAllChildNodes(parent); | |
2928 }, | |
2929 | |
2930 associateNode: function(node) { | 4327 associateNode: function(node) { |
2931 shadowDOMRendererTable.set(node, this); | 4328 shadowDOMRendererTable.set(node, this); |
2932 } | 4329 } |
2933 }; | 4330 }; |
2934 | 4331 |
2935 function isInsertionPoint(node) { | 4332 function isInsertionPoint(node) { |
2936 // Should this include <shadow>? | 4333 // Should this include <shadow>? |
2937 return node.localName === 'content'; | 4334 return node instanceof HTMLContentElement; |
2938 } | 4335 } |
2939 | 4336 |
2940 function isActiveInsertionPoint(node) { | 4337 function isActiveInsertionPoint(node) { |
2941 // <content> inside another <content> or <shadow> is considered inactive. | 4338 // <content> inside another <content> or <shadow> is considered inactive. |
2942 return node.localName === 'content'; | 4339 return node instanceof HTMLContentElement; |
2943 } | 4340 } |
2944 | 4341 |
2945 function isShadowInsertionPoint(node) { | 4342 function isShadowInsertionPoint(node) { |
2946 return node.localName === 'shadow'; | 4343 return node instanceof HTMLShadowElement; |
2947 } | 4344 } |
2948 | 4345 |
2949 function isActiveShadowInsertionPoint(node) { | 4346 function isActiveShadowInsertionPoint(node) { |
2950 // <shadow> inside another <content> or <shadow> is considered inactive. | 4347 // <shadow> inside another <content> or <shadow> is considered inactive. |
2951 return node.localName === 'shadow'; | 4348 return node instanceof HTMLShadowElement; |
2952 } | 4349 } |
2953 | 4350 |
2954 function isShadowHost(shadowHost) { | 4351 function isShadowHost(shadowHost) { |
2955 return shadowHost.shadowRoot; | 4352 return shadowHost.shadowRoot; |
2956 } | 4353 } |
2957 | 4354 |
2958 function getShadowTrees(host) { | 4355 function getShadowTrees(host) { |
2959 var trees = []; | 4356 var trees = []; |
2960 | 4357 |
2961 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { | 4358 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3018 }; | 4415 }; |
3019 | 4416 |
3020 scope.eventParentsTable = eventParentsTable; | 4417 scope.eventParentsTable = eventParentsTable; |
3021 scope.getRendererForHost = getRendererForHost; | 4418 scope.getRendererForHost = getRendererForHost; |
3022 scope.getShadowTrees = getShadowTrees; | 4419 scope.getShadowTrees = getShadowTrees; |
3023 scope.insertionParentTable = insertionParentTable; | 4420 scope.insertionParentTable = insertionParentTable; |
3024 scope.renderAllPending = renderAllPending; | 4421 scope.renderAllPending = renderAllPending; |
3025 | 4422 |
3026 // Exposed for testing | 4423 // Exposed for testing |
3027 scope.visual = { | 4424 scope.visual = { |
3028 removeAllChildNodes: removeAllChildNodes, | 4425 insertBefore: insertBefore, |
3029 appendChild: appendChild, | 4426 remove: remove, |
3030 removeChild: removeChild | |
3031 }; | 4427 }; |
3032 | 4428 |
3033 })(this.ShadowDOMPolyfill); | 4429 })(this.ShadowDOMPolyfill); |
3034 // Copyright 2013 The Polymer Authors. All rights reserved. | 4430 // Copyright 2013 The Polymer Authors. All rights reserved. |
3035 // Use of this source code is goverened by a BSD-style | 4431 // Use of this source code is goverened by a BSD-style |
3036 // license that can be found in the LICENSE file. | 4432 // license that can be found in the LICENSE file. |
3037 | 4433 |
3038 (function(scope) { | 4434 (function(scope) { |
3039 'use strict'; | 4435 'use strict'; |
3040 | 4436 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3102 var elementFromPoint = scope.elementFromPoint; | 4498 var elementFromPoint = scope.elementFromPoint; |
3103 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | 4499 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; |
3104 var matchesName = scope.matchesName; | 4500 var matchesName = scope.matchesName; |
3105 var mixin = scope.mixin; | 4501 var mixin = scope.mixin; |
3106 var registerWrapper = scope.registerWrapper; | 4502 var registerWrapper = scope.registerWrapper; |
3107 var unwrap = scope.unwrap; | 4503 var unwrap = scope.unwrap; |
3108 var wrap = scope.wrap; | 4504 var wrap = scope.wrap; |
3109 var wrapEventTargetMethods = scope.wrapEventTargetMethods; | 4505 var wrapEventTargetMethods = scope.wrapEventTargetMethods; |
3110 var wrapNodeList = scope.wrapNodeList; | 4506 var wrapNodeList = scope.wrapNodeList; |
3111 | 4507 |
3112 var implementationTable = new SideTable(); | 4508 var implementationTable = new WeakMap(); |
3113 | 4509 |
3114 function Document(node) { | 4510 function Document(node) { |
3115 Node.call(this, node); | 4511 Node.call(this, node); |
3116 } | 4512 } |
3117 Document.prototype = Object.create(Node.prototype); | 4513 Document.prototype = Object.create(Node.prototype); |
3118 | 4514 |
3119 defineWrapGetter(Document, 'documentElement'); | 4515 defineWrapGetter(Document, 'documentElement'); |
3120 | 4516 |
3121 // Conceptually both body and head can be in a shadow but suporting that seems | 4517 // Conceptually both body and head can be in a shadow but suporting that seems |
3122 // overkill at this point. | 4518 // overkill at this point. |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3377 | 4773 |
3378 (function(scope) { | 4774 (function(scope) { |
3379 'use strict'; | 4775 'use strict'; |
3380 | 4776 |
3381 var EventTarget = scope.wrappers.EventTarget; | 4777 var EventTarget = scope.wrappers.EventTarget; |
3382 var mixin = scope.mixin; | 4778 var mixin = scope.mixin; |
3383 var registerWrapper = scope.registerWrapper; | 4779 var registerWrapper = scope.registerWrapper; |
3384 var unwrap = scope.unwrap; | 4780 var unwrap = scope.unwrap; |
3385 var unwrapIfNeeded = scope.unwrapIfNeeded; | 4781 var unwrapIfNeeded = scope.unwrapIfNeeded; |
3386 var wrap = scope.wrap; | 4782 var wrap = scope.wrap; |
| 4783 var renderAllPending = scope.renderAllPending; |
3387 | 4784 |
3388 var OriginalWindow = window.Window; | 4785 var OriginalWindow = window.Window; |
3389 | 4786 |
3390 function Window(impl) { | 4787 function Window(impl) { |
3391 EventTarget.call(this, impl); | 4788 EventTarget.call(this, impl); |
3392 } | 4789 } |
3393 Window.prototype = Object.create(EventTarget.prototype); | 4790 Window.prototype = Object.create(EventTarget.prototype); |
3394 | 4791 |
3395 var originalGetComputedStyle = window.getComputedStyle; | 4792 var originalGetComputedStyle = window.getComputedStyle; |
3396 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { | 4793 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { |
| 4794 renderAllPending(); |
3397 return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el), | 4795 return originalGetComputedStyle.call(this || window, unwrapIfNeeded(el), |
3398 pseudo); | 4796 pseudo); |
3399 }; | 4797 }; |
3400 | 4798 |
3401 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( | 4799 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( |
3402 function(name) { | 4800 function(name) { |
3403 OriginalWindow.prototype[name] = function() { | 4801 OriginalWindow.prototype[name] = function() { |
3404 var w = wrap(this || window); | 4802 var w = wrap(this || window); |
3405 return w[name].apply(w, arguments); | 4803 return w[name].apply(w, arguments); |
3406 }; | 4804 }; |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3738 if (obj instanceof NodeList) return 'NodeList'; | 5136 if (obj instanceof NodeList) return 'NodeList'; |
3739 if (obj instanceof ShadowRoot) return 'ShadowRoot'; | 5137 if (obj instanceof ShadowRoot) return 'ShadowRoot'; |
3740 if (obj instanceof MutationRecord) return 'MutationRecord'; | 5138 if (obj instanceof MutationRecord) return 'MutationRecord'; |
3741 if (obj instanceof MutationObserver) return 'MutationObserver'; | 5139 if (obj instanceof MutationObserver) return 'MutationObserver'; |
3742 | 5140 |
3743 var unwrapped = unwrapIfNeeded(obj); | 5141 var unwrapped = unwrapIfNeeded(obj); |
3744 if (obj !== unwrapped) { | 5142 if (obj !== unwrapped) { |
3745 // Fix up class names for Firefox. | 5143 // Fix up class names for Firefox. |
3746 // For some of them (like HTMLFormElement and HTMLInputElement), | 5144 // For some of them (like HTMLFormElement and HTMLInputElement), |
3747 // the "constructor" property of the unwrapped nodes points at the | 5145 // the "constructor" property of the unwrapped nodes points at the |
3748 // wrapper. | 5146 // same constructor as the wrapper. |
3749 // Note: it is safe to check for the GeneratedWrapper string because | 5147 var ctor = obj.constructor |
3750 // we know it is some kind of Shadow DOM wrapper object. | 5148 if (ctor === unwrapped.constructor) { |
3751 var ctor = obj.constructor; | |
3752 if (ctor && ctor.name == 'GeneratedWrapper') { | |
3753 var name = ctor._ShadowDOMPolyfill$cacheTag_; | 5149 var name = ctor._ShadowDOMPolyfill$cacheTag_; |
3754 if (!name) { | 5150 if (!name) { |
3755 name = Object.prototype.toString.call(unwrapped); | 5151 name = Object.prototype.toString.call(unwrapped); |
3756 name = name.substring(8, name.length - 1); | 5152 name = name.substring(8, name.length - 1); |
3757 ctor._ShadowDOMPolyfill$cacheTag_ = name; | 5153 ctor._ShadowDOMPolyfill$cacheTag_ = name; |
3758 } | 5154 } |
3759 return name; | 5155 return name; |
3760 } | 5156 } |
3761 | 5157 |
3762 obj = unwrapped; | 5158 obj = unwrapped; |
(...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4286 css.push(cssRules[i].cssText); | 5682 css.push(cssRules[i].cssText); |
4287 } | 5683 } |
4288 return css.join('\n\n'); | 5684 return css.join('\n\n'); |
4289 } | 5685 } |
4290 | 5686 |
4291 // exports | 5687 // exports |
4292 scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2; | 5688 scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2; |
4293 })(window.Platform); | 5689 })(window.Platform); |
4294 | 5690 |
4295 } | 5691 } |
OLD | NEW |