| OLD | NEW |
| (Empty) |
| 1 /** | |
| 2 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 3 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 4 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 5 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 6 * Code distributed by Google as part of the polymer project is also | |
| 7 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 8 */ | |
| 9 | |
| 10 window.Platform = window.Platform || {}; | |
| 11 // prepopulate window.logFlags if necessary | |
| 12 window.logFlags = window.logFlags || {}; | |
| 13 // process flags | |
| 14 (function(scope){ | |
| 15 // import | |
| 16 var flags = scope.flags || {}; | |
| 17 // populate flags from location | |
| 18 location.search.slice(1).split('&').forEach(function(o) { | |
| 19 o = o.split('='); | |
| 20 o[0] && (flags[o[0]] = o[1] || true); | |
| 21 }); | |
| 22 var entryPoint = document.currentScript || | |
| 23 document.querySelector('script[src*="platform.js"]'); | |
| 24 if (entryPoint) { | |
| 25 var a = entryPoint.attributes; | |
| 26 for (var i = 0, n; i < a.length; i++) { | |
| 27 n = a[i]; | |
| 28 if (n.name !== 'src') { | |
| 29 flags[n.name] = n.value || true; | |
| 30 } | |
| 31 } | |
| 32 } | |
| 33 if (flags.log) { | |
| 34 flags.log.split(',').forEach(function(f) { | |
| 35 window.logFlags[f] = true; | |
| 36 }); | |
| 37 } | |
| 38 // If any of these flags match 'native', then force native ShadowDOM; any | |
| 39 // other truthy value, or failure to detect native | |
| 40 // ShadowDOM, results in polyfill | |
| 41 flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; | |
| 42 if (flags.shadow === 'native') { | |
| 43 flags.shadow = false; | |
| 44 } else { | |
| 45 flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; | |
| 46 } | |
| 47 | |
| 48 if (flags.shadow && document.querySelectorAll('script').length > 1) { | |
| 49 console.log('Warning: platform.js is not the first script on the page. ' + | |
| 50 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + | |
| 51 'for details.'); | |
| 52 } | |
| 53 | |
| 54 // CustomElements polyfill flag | |
| 55 if (flags.register) { | |
| 56 window.CustomElements = window.CustomElements || {flags: {}}; | |
| 57 window.CustomElements.flags.register = flags.register; | |
| 58 } | |
| 59 | |
| 60 if (flags.imports) { | |
| 61 window.HTMLImports = window.HTMLImports || {flags: {}}; | |
| 62 window.HTMLImports.flags.imports = flags.imports; | |
| 63 } | |
| 64 | |
| 65 // export | |
| 66 scope.flags = flags; | |
| 67 })(Platform); | |
| 68 | |
| 69 /* | |
| 70 * Copyright 2012 The Polymer Authors. All rights reserved. | |
| 71 * Use of this source code is governed by a BSD-style | |
| 72 * license that can be found in the LICENSE file. | |
| 73 */ | |
| 74 | |
| 75 if (typeof WeakMap === 'undefined') { | |
| 76 (function() { | |
| 77 var defineProperty = Object.defineProperty; | |
| 78 var counter = Date.now() % 1e9; | |
| 79 | |
| 80 var WeakMap = function() { | |
| 81 this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); | |
| 82 }; | |
| 83 | |
| 84 WeakMap.prototype = { | |
| 85 set: function(key, value) { | |
| 86 var entry = key[this.name]; | |
| 87 if (entry && entry[0] === key) | |
| 88 entry[1] = value; | |
| 89 else | |
| 90 defineProperty(key, this.name, {value: [key, value], writable: true}); | |
| 91 return this; | |
| 92 }, | |
| 93 get: function(key) { | |
| 94 var entry; | |
| 95 return (entry = key[this.name]) && entry[0] === key ? | |
| 96 entry[1] : undefined; | |
| 97 }, | |
| 98 delete: function(key) { | |
| 99 var entry = key[this.name]; | |
| 100 if (!entry) return false; | |
| 101 var hasValue = entry[0] === key; | |
| 102 entry[0] = entry[1] = undefined; | |
| 103 return hasValue; | |
| 104 }, | |
| 105 has: function(key) { | |
| 106 var entry = key[this.name]; | |
| 107 if (!entry) return false; | |
| 108 return entry[0] === key; | |
| 109 } | |
| 110 }; | |
| 111 | |
| 112 window.WeakMap = WeakMap; | |
| 113 })(); | |
| 114 } | |
| 115 | |
| 116 // select ShadowDOM impl | |
| 117 if (Platform.flags.shadow) { | |
| 118 | |
| 119 /* | |
| 120 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 121 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 122 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 123 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 124 * Code distributed by Google as part of the polymer project is also | |
| 125 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 126 */ | |
| 127 | |
| 128 (function(global) { | |
| 129 'use strict'; | |
| 130 | |
| 131 var testingExposeCycleCount = global.testingExposeCycleCount; | |
| 132 | |
| 133 // Detect and do basic sanity checking on Object/Array.observe. | |
| 134 function detectObjectObserve() { | |
| 135 if (typeof Object.observe !== 'function' || | |
| 136 typeof Array.observe !== 'function') { | |
| 137 return false; | |
| 138 } | |
| 139 | |
| 140 var records = []; | |
| 141 | |
| 142 function callback(recs) { | |
| 143 records = recs; | |
| 144 } | |
| 145 | |
| 146 var test = {}; | |
| 147 var arr = []; | |
| 148 Object.observe(test, callback); | |
| 149 Array.observe(arr, callback); | |
| 150 test.id = 1; | |
| 151 test.id = 2; | |
| 152 delete test.id; | |
| 153 arr.push(1, 2); | |
| 154 arr.length = 0; | |
| 155 | |
| 156 Object.deliverChangeRecords(callback); | |
| 157 if (records.length !== 5) | |
| 158 return false; | |
| 159 | |
| 160 if (records[0].type != 'add' || | |
| 161 records[1].type != 'update' || | |
| 162 records[2].type != 'delete' || | |
| 163 records[3].type != 'splice' || | |
| 164 records[4].type != 'splice') { | |
| 165 return false; | |
| 166 } | |
| 167 | |
| 168 Object.unobserve(test, callback); | |
| 169 Array.unobserve(arr, callback); | |
| 170 | |
| 171 return true; | |
| 172 } | |
| 173 | |
| 174 var hasObserve = detectObjectObserve(); | |
| 175 | |
| 176 function detectEval() { | |
| 177 // Don't test for eval if we're running in a Chrome App environment. | |
| 178 // We check for APIs set that only exist in a Chrome App context. | |
| 179 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { | |
| 180 return false; | |
| 181 } | |
| 182 | |
| 183 // Firefox OS Apps do not allow eval. This feature detection is very hacky | |
| 184 // but even if some other platform adds support for this function this code | |
| 185 // will continue to work. | |
| 186 if (typeof navigator != 'undefined' && navigator.getDeviceStorage) { | |
| 187 return false; | |
| 188 } | |
| 189 | |
| 190 try { | |
| 191 var f = new Function('', 'return true;'); | |
| 192 return f(); | |
| 193 } catch (ex) { | |
| 194 return false; | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 var hasEval = detectEval(); | |
| 199 | |
| 200 function isIndex(s) { | |
| 201 return +s === s >>> 0 && s !== ''; | |
| 202 } | |
| 203 | |
| 204 function toNumber(s) { | |
| 205 return +s; | |
| 206 } | |
| 207 | |
| 208 function isObject(obj) { | |
| 209 return obj === Object(obj); | |
| 210 } | |
| 211 | |
| 212 var numberIsNaN = global.Number.isNaN || function(value) { | |
| 213 return typeof value === 'number' && global.isNaN(value); | |
| 214 } | |
| 215 | |
| 216 function areSameValue(left, right) { | |
| 217 if (left === right) | |
| 218 return left !== 0 || 1 / left === 1 / right; | |
| 219 if (numberIsNaN(left) && numberIsNaN(right)) | |
| 220 return true; | |
| 221 | |
| 222 return left !== left && right !== right; | |
| 223 } | |
| 224 | |
| 225 var createObject = ('__proto__' in {}) ? | |
| 226 function(obj) { return obj; } : | |
| 227 function(obj) { | |
| 228 var proto = obj.__proto__; | |
| 229 if (!proto) | |
| 230 return obj; | |
| 231 var newObject = Object.create(proto); | |
| 232 Object.getOwnPropertyNames(obj).forEach(function(name) { | |
| 233 Object.defineProperty(newObject, name, | |
| 234 Object.getOwnPropertyDescriptor(obj, name)); | |
| 235 }); | |
| 236 return newObject; | |
| 237 }; | |
| 238 | |
| 239 var identStart = '[\$_a-zA-Z]'; | |
| 240 var identPart = '[\$_a-zA-Z0-9]'; | |
| 241 var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$'); | |
| 242 | |
| 243 function getPathCharType(char) { | |
| 244 if (char === undefined) | |
| 245 return 'eof'; | |
| 246 | |
| 247 var code = char.charCodeAt(0); | |
| 248 | |
| 249 switch(code) { | |
| 250 case 0x5B: // [ | |
| 251 case 0x5D: // ] | |
| 252 case 0x2E: // . | |
| 253 case 0x22: // " | |
| 254 case 0x27: // ' | |
| 255 case 0x30: // 0 | |
| 256 return char; | |
| 257 | |
| 258 case 0x5F: // _ | |
| 259 case 0x24: // $ | |
| 260 return 'ident'; | |
| 261 | |
| 262 case 0x20: // Space | |
| 263 case 0x09: // Tab | |
| 264 case 0x0A: // Newline | |
| 265 case 0x0D: // Return | |
| 266 case 0xA0: // No-break space | |
| 267 case 0xFEFF: // Byte Order Mark | |
| 268 case 0x2028: // Line Separator | |
| 269 case 0x2029: // Paragraph Separator | |
| 270 return 'ws'; | |
| 271 } | |
| 272 | |
| 273 // a-z, A-Z | |
| 274 if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A)) | |
| 275 return 'ident'; | |
| 276 | |
| 277 // 1-9 | |
| 278 if (0x31 <= code && code <= 0x39) | |
| 279 return 'number'; | |
| 280 | |
| 281 return 'else'; | |
| 282 } | |
| 283 | |
| 284 var pathStateMachine = { | |
| 285 'beforePath': { | |
| 286 'ws': ['beforePath'], | |
| 287 'ident': ['inIdent', 'append'], | |
| 288 '[': ['beforeElement'], | |
| 289 'eof': ['afterPath'] | |
| 290 }, | |
| 291 | |
| 292 'inPath': { | |
| 293 'ws': ['inPath'], | |
| 294 '.': ['beforeIdent'], | |
| 295 '[': ['beforeElement'], | |
| 296 'eof': ['afterPath'] | |
| 297 }, | |
| 298 | |
| 299 'beforeIdent': { | |
| 300 'ws': ['beforeIdent'], | |
| 301 'ident': ['inIdent', 'append'] | |
| 302 }, | |
| 303 | |
| 304 'inIdent': { | |
| 305 'ident': ['inIdent', 'append'], | |
| 306 '0': ['inIdent', 'append'], | |
| 307 'number': ['inIdent', 'append'], | |
| 308 'ws': ['inPath', 'push'], | |
| 309 '.': ['beforeIdent', 'push'], | |
| 310 '[': ['beforeElement', 'push'], | |
| 311 'eof': ['afterPath', 'push'] | |
| 312 }, | |
| 313 | |
| 314 'beforeElement': { | |
| 315 'ws': ['beforeElement'], | |
| 316 '0': ['afterZero', 'append'], | |
| 317 'number': ['inIndex', 'append'], | |
| 318 "'": ['inSingleQuote', 'append', ''], | |
| 319 '"': ['inDoubleQuote', 'append', ''] | |
| 320 }, | |
| 321 | |
| 322 'afterZero': { | |
| 323 'ws': ['afterElement', 'push'], | |
| 324 ']': ['inPath', 'push'] | |
| 325 }, | |
| 326 | |
| 327 'inIndex': { | |
| 328 '0': ['inIndex', 'append'], | |
| 329 'number': ['inIndex', 'append'], | |
| 330 'ws': ['afterElement'], | |
| 331 ']': ['inPath', 'push'] | |
| 332 }, | |
| 333 | |
| 334 'inSingleQuote': { | |
| 335 "'": ['afterElement'], | |
| 336 'eof': ['error'], | |
| 337 'else': ['inSingleQuote', 'append'] | |
| 338 }, | |
| 339 | |
| 340 'inDoubleQuote': { | |
| 341 '"': ['afterElement'], | |
| 342 'eof': ['error'], | |
| 343 'else': ['inDoubleQuote', 'append'] | |
| 344 }, | |
| 345 | |
| 346 'afterElement': { | |
| 347 'ws': ['afterElement'], | |
| 348 ']': ['inPath', 'push'] | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 function noop() {} | |
| 353 | |
| 354 function parsePath(path) { | |
| 355 var keys = []; | |
| 356 var index = -1; | |
| 357 var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath'; | |
| 358 | |
| 359 var actions = { | |
| 360 push: function() { | |
| 361 if (key === undefined) | |
| 362 return; | |
| 363 | |
| 364 keys.push(key); | |
| 365 key = undefined; | |
| 366 }, | |
| 367 | |
| 368 append: function() { | |
| 369 if (key === undefined) | |
| 370 key = newChar | |
| 371 else | |
| 372 key += newChar; | |
| 373 } | |
| 374 }; | |
| 375 | |
| 376 function maybeUnescapeQuote() { | |
| 377 if (index >= path.length) | |
| 378 return; | |
| 379 | |
| 380 var nextChar = path[index + 1]; | |
| 381 if ((mode == 'inSingleQuote' && nextChar == "'") || | |
| 382 (mode == 'inDoubleQuote' && nextChar == '"')) { | |
| 383 index++; | |
| 384 newChar = nextChar; | |
| 385 actions.append(); | |
| 386 return true; | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 while (mode) { | |
| 391 index++; | |
| 392 c = path[index]; | |
| 393 | |
| 394 if (c == '\\' && maybeUnescapeQuote(mode)) | |
| 395 continue; | |
| 396 | |
| 397 type = getPathCharType(c); | |
| 398 typeMap = pathStateMachine[mode]; | |
| 399 transition = typeMap[type] || typeMap['else'] || 'error'; | |
| 400 | |
| 401 if (transition == 'error') | |
| 402 return; // parse error; | |
| 403 | |
| 404 mode = transition[0]; | |
| 405 action = actions[transition[1]] || noop; | |
| 406 newChar = transition[2] === undefined ? c : transition[2]; | |
| 407 action(); | |
| 408 | |
| 409 if (mode === 'afterPath') { | |
| 410 return keys; | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 return; // parse error | |
| 415 } | |
| 416 | |
| 417 function isIdent(s) { | |
| 418 return identRegExp.test(s); | |
| 419 } | |
| 420 | |
| 421 var constructorIsPrivate = {}; | |
| 422 | |
| 423 function Path(parts, privateToken) { | |
| 424 if (privateToken !== constructorIsPrivate) | |
| 425 throw Error('Use Path.get to retrieve path objects'); | |
| 426 | |
| 427 for (var i = 0; i < parts.length; i++) { | |
| 428 this.push(String(parts[i])); | |
| 429 } | |
| 430 | |
| 431 if (hasEval && this.length) { | |
| 432 this.getValueFrom = this.compiledGetValueFromFn(); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 // TODO(rafaelw): Make simple LRU cache | |
| 437 var pathCache = {}; | |
| 438 | |
| 439 function getPath(pathString) { | |
| 440 if (pathString instanceof Path) | |
| 441 return pathString; | |
| 442 | |
| 443 if (pathString == null || pathString.length == 0) | |
| 444 pathString = ''; | |
| 445 | |
| 446 if (typeof pathString != 'string') { | |
| 447 if (isIndex(pathString.length)) { | |
| 448 // Constructed with array-like (pre-parsed) keys | |
| 449 return new Path(pathString, constructorIsPrivate); | |
| 450 } | |
| 451 | |
| 452 pathString = String(pathString); | |
| 453 } | |
| 454 | |
| 455 var path = pathCache[pathString]; | |
| 456 if (path) | |
| 457 return path; | |
| 458 | |
| 459 var parts = parsePath(pathString); | |
| 460 if (!parts) | |
| 461 return invalidPath; | |
| 462 | |
| 463 var path = new Path(parts, constructorIsPrivate); | |
| 464 pathCache[pathString] = path; | |
| 465 return path; | |
| 466 } | |
| 467 | |
| 468 Path.get = getPath; | |
| 469 | |
| 470 function formatAccessor(key) { | |
| 471 if (isIndex(key)) { | |
| 472 return '[' + key + ']'; | |
| 473 } else { | |
| 474 return '["' + key.replace(/"/g, '\\"') + '"]'; | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 Path.prototype = createObject({ | |
| 479 __proto__: [], | |
| 480 valid: true, | |
| 481 | |
| 482 toString: function() { | |
| 483 var pathString = ''; | |
| 484 for (var i = 0; i < this.length; i++) { | |
| 485 var key = this[i]; | |
| 486 if (isIdent(key)) { | |
| 487 pathString += i ? '.' + key : key; | |
| 488 } else { | |
| 489 pathString += formatAccessor(key); | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 return pathString; | |
| 494 }, | |
| 495 | |
| 496 getValueFrom: function(obj, directObserver) { | |
| 497 for (var i = 0; i < this.length; i++) { | |
| 498 if (obj == null) | |
| 499 return; | |
| 500 obj = obj[this[i]]; | |
| 501 } | |
| 502 return obj; | |
| 503 }, | |
| 504 | |
| 505 iterateObjects: function(obj, observe) { | |
| 506 for (var i = 0; i < this.length; i++) { | |
| 507 if (i) | |
| 508 obj = obj[this[i - 1]]; | |
| 509 if (!isObject(obj)) | |
| 510 return; | |
| 511 observe(obj, this[0]); | |
| 512 } | |
| 513 }, | |
| 514 | |
| 515 compiledGetValueFromFn: function() { | |
| 516 var str = ''; | |
| 517 var pathString = 'obj'; | |
| 518 str += 'if (obj != null'; | |
| 519 var i = 0; | |
| 520 var key; | |
| 521 for (; i < (this.length - 1); i++) { | |
| 522 key = this[i]; | |
| 523 pathString += isIdent(key) ? '.' + key : formatAccessor(key); | |
| 524 str += ' &&\n ' + pathString + ' != null'; | |
| 525 } | |
| 526 str += ')\n'; | |
| 527 | |
| 528 var key = this[i]; | |
| 529 pathString += isIdent(key) ? '.' + key : formatAccessor(key); | |
| 530 | |
| 531 str += ' return ' + pathString + ';\nelse\n return undefined;'; | |
| 532 return new Function('obj', str); | |
| 533 }, | |
| 534 | |
| 535 setValueFrom: function(obj, value) { | |
| 536 if (!this.length) | |
| 537 return false; | |
| 538 | |
| 539 for (var i = 0; i < this.length - 1; i++) { | |
| 540 if (!isObject(obj)) | |
| 541 return false; | |
| 542 obj = obj[this[i]]; | |
| 543 } | |
| 544 | |
| 545 if (!isObject(obj)) | |
| 546 return false; | |
| 547 | |
| 548 obj[this[i]] = value; | |
| 549 return true; | |
| 550 } | |
| 551 }); | |
| 552 | |
| 553 var invalidPath = new Path('', constructorIsPrivate); | |
| 554 invalidPath.valid = false; | |
| 555 invalidPath.getValueFrom = invalidPath.setValueFrom = function() {}; | |
| 556 | |
| 557 var MAX_DIRTY_CHECK_CYCLES = 1000; | |
| 558 | |
| 559 function dirtyCheck(observer) { | |
| 560 var cycles = 0; | |
| 561 while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) { | |
| 562 cycles++; | |
| 563 } | |
| 564 if (testingExposeCycleCount) | |
| 565 global.dirtyCheckCycleCount = cycles; | |
| 566 | |
| 567 return cycles > 0; | |
| 568 } | |
| 569 | |
| 570 function objectIsEmpty(object) { | |
| 571 for (var prop in object) | |
| 572 return false; | |
| 573 return true; | |
| 574 } | |
| 575 | |
| 576 function diffIsEmpty(diff) { | |
| 577 return objectIsEmpty(diff.added) && | |
| 578 objectIsEmpty(diff.removed) && | |
| 579 objectIsEmpty(diff.changed); | |
| 580 } | |
| 581 | |
| 582 function diffObjectFromOldObject(object, oldObject) { | |
| 583 var added = {}; | |
| 584 var removed = {}; | |
| 585 var changed = {}; | |
| 586 | |
| 587 for (var prop in oldObject) { | |
| 588 var newValue = object[prop]; | |
| 589 | |
| 590 if (newValue !== undefined && newValue === oldObject[prop]) | |
| 591 continue; | |
| 592 | |
| 593 if (!(prop in object)) { | |
| 594 removed[prop] = undefined; | |
| 595 continue; | |
| 596 } | |
| 597 | |
| 598 if (newValue !== oldObject[prop]) | |
| 599 changed[prop] = newValue; | |
| 600 } | |
| 601 | |
| 602 for (var prop in object) { | |
| 603 if (prop in oldObject) | |
| 604 continue; | |
| 605 | |
| 606 added[prop] = object[prop]; | |
| 607 } | |
| 608 | |
| 609 if (Array.isArray(object) && object.length !== oldObject.length) | |
| 610 changed.length = object.length; | |
| 611 | |
| 612 return { | |
| 613 added: added, | |
| 614 removed: removed, | |
| 615 changed: changed | |
| 616 }; | |
| 617 } | |
| 618 | |
| 619 var eomTasks = []; | |
| 620 function runEOMTasks() { | |
| 621 if (!eomTasks.length) | |
| 622 return false; | |
| 623 | |
| 624 for (var i = 0; i < eomTasks.length; i++) { | |
| 625 eomTasks[i](); | |
| 626 } | |
| 627 eomTasks.length = 0; | |
| 628 return true; | |
| 629 } | |
| 630 | |
| 631 var runEOM = hasObserve ? (function(){ | |
| 632 var eomObj = { pingPong: true }; | |
| 633 var eomRunScheduled = false; | |
| 634 | |
| 635 Object.observe(eomObj, function() { | |
| 636 runEOMTasks(); | |
| 637 eomRunScheduled = false; | |
| 638 }); | |
| 639 | |
| 640 return function(fn) { | |
| 641 eomTasks.push(fn); | |
| 642 if (!eomRunScheduled) { | |
| 643 eomRunScheduled = true; | |
| 644 eomObj.pingPong = !eomObj.pingPong; | |
| 645 } | |
| 646 }; | |
| 647 })() : | |
| 648 (function() { | |
| 649 return function(fn) { | |
| 650 eomTasks.push(fn); | |
| 651 }; | |
| 652 })(); | |
| 653 | |
| 654 var observedObjectCache = []; | |
| 655 | |
| 656 function newObservedObject() { | |
| 657 var observer; | |
| 658 var object; | |
| 659 var discardRecords = false; | |
| 660 var first = true; | |
| 661 | |
| 662 function callback(records) { | |
| 663 if (observer && observer.state_ === OPENED && !discardRecords) | |
| 664 observer.check_(records); | |
| 665 } | |
| 666 | |
| 667 return { | |
| 668 open: function(obs) { | |
| 669 if (observer) | |
| 670 throw Error('ObservedObject in use'); | |
| 671 | |
| 672 if (!first) | |
| 673 Object.deliverChangeRecords(callback); | |
| 674 | |
| 675 observer = obs; | |
| 676 first = false; | |
| 677 }, | |
| 678 observe: function(obj, arrayObserve) { | |
| 679 object = obj; | |
| 680 if (arrayObserve) | |
| 681 Array.observe(object, callback); | |
| 682 else | |
| 683 Object.observe(object, callback); | |
| 684 }, | |
| 685 deliver: function(discard) { | |
| 686 discardRecords = discard; | |
| 687 Object.deliverChangeRecords(callback); | |
| 688 discardRecords = false; | |
| 689 }, | |
| 690 close: function() { | |
| 691 observer = undefined; | |
| 692 Object.unobserve(object, callback); | |
| 693 observedObjectCache.push(this); | |
| 694 } | |
| 695 }; | |
| 696 } | |
| 697 | |
| 698 /* | |
| 699 * The observedSet abstraction is a perf optimization which reduces the total | |
| 700 * number of Object.observe observations of a set of objects. The idea is that | |
| 701 * groups of Observers will have some object dependencies in common and this | |
| 702 * observed set ensures that each object in the transitive closure of | |
| 703 * dependencies is only observed once. The observedSet acts as a write barrier | |
| 704 * such that whenever any change comes through, all Observers are checked for | |
| 705 * changed values. | |
| 706 * | |
| 707 * Note that this optimization is explicitly moving work from setup-time to | |
| 708 * change-time. | |
| 709 * | |
| 710 * TODO(rafaelw): Implement "garbage collection". In order to move work off | |
| 711 * the critical path, when Observers are closed, their observed objects are | |
| 712 * not Object.unobserve(d). As a result, it's possible that if the observedSet | |
| 713 * is kept open, but some Observers have been closed, it could cause "leaks" | |
| 714 * (prevent otherwise collectable objects from being collected). At some | |
| 715 * point, we should implement incremental "gc" which keeps a list of | |
| 716 * observedSets which may need clean-up and does small amounts of cleanup on a | |
| 717 * timeout until all is clean. | |
| 718 */ | |
| 719 | |
| 720 function getObservedObject(observer, object, arrayObserve) { | |
| 721 var dir = observedObjectCache.pop() || newObservedObject(); | |
| 722 dir.open(observer); | |
| 723 dir.observe(object, arrayObserve); | |
| 724 return dir; | |
| 725 } | |
| 726 | |
| 727 var observedSetCache = []; | |
| 728 | |
| 729 function newObservedSet() { | |
| 730 var observerCount = 0; | |
| 731 var observers = []; | |
| 732 var objects = []; | |
| 733 var rootObj; | |
| 734 var rootObjProps; | |
| 735 | |
| 736 function observe(obj, prop) { | |
| 737 if (!obj) | |
| 738 return; | |
| 739 | |
| 740 if (obj === rootObj) | |
| 741 rootObjProps[prop] = true; | |
| 742 | |
| 743 if (objects.indexOf(obj) < 0) { | |
| 744 objects.push(obj); | |
| 745 Object.observe(obj, callback); | |
| 746 } | |
| 747 | |
| 748 observe(Object.getPrototypeOf(obj), prop); | |
| 749 } | |
| 750 | |
| 751 function allRootObjNonObservedProps(recs) { | |
| 752 for (var i = 0; i < recs.length; i++) { | |
| 753 var rec = recs[i]; | |
| 754 if (rec.object !== rootObj || | |
| 755 rootObjProps[rec.name] || | |
| 756 rec.type === 'setPrototype') { | |
| 757 return false; | |
| 758 } | |
| 759 } | |
| 760 return true; | |
| 761 } | |
| 762 | |
| 763 function callback(recs) { | |
| 764 if (allRootObjNonObservedProps(recs)) | |
| 765 return; | |
| 766 | |
| 767 var observer; | |
| 768 for (var i = 0; i < observers.length; i++) { | |
| 769 observer = observers[i]; | |
| 770 if (observer.state_ == OPENED) { | |
| 771 observer.iterateObjects_(observe); | |
| 772 } | |
| 773 } | |
| 774 | |
| 775 for (var i = 0; i < observers.length; i++) { | |
| 776 observer = observers[i]; | |
| 777 if (observer.state_ == OPENED) { | |
| 778 observer.check_(); | |
| 779 } | |
| 780 } | |
| 781 } | |
| 782 | |
| 783 var record = { | |
| 784 object: undefined, | |
| 785 objects: objects, | |
| 786 open: function(obs, object) { | |
| 787 if (!rootObj) { | |
| 788 rootObj = object; | |
| 789 rootObjProps = {}; | |
| 790 } | |
| 791 | |
| 792 observers.push(obs); | |
| 793 observerCount++; | |
| 794 obs.iterateObjects_(observe); | |
| 795 }, | |
| 796 close: function(obs) { | |
| 797 observerCount--; | |
| 798 if (observerCount > 0) { | |
| 799 return; | |
| 800 } | |
| 801 | |
| 802 for (var i = 0; i < objects.length; i++) { | |
| 803 Object.unobserve(objects[i], callback); | |
| 804 Observer.unobservedCount++; | |
| 805 } | |
| 806 | |
| 807 observers.length = 0; | |
| 808 objects.length = 0; | |
| 809 rootObj = undefined; | |
| 810 rootObjProps = undefined; | |
| 811 observedSetCache.push(this); | |
| 812 } | |
| 813 }; | |
| 814 | |
| 815 return record; | |
| 816 } | |
| 817 | |
| 818 var lastObservedSet; | |
| 819 | |
| 820 function getObservedSet(observer, obj) { | |
| 821 if (!lastObservedSet || lastObservedSet.object !== obj) { | |
| 822 lastObservedSet = observedSetCache.pop() || newObservedSet(); | |
| 823 lastObservedSet.object = obj; | |
| 824 } | |
| 825 lastObservedSet.open(observer, obj); | |
| 826 return lastObservedSet; | |
| 827 } | |
| 828 | |
| 829 var UNOPENED = 0; | |
| 830 var OPENED = 1; | |
| 831 var CLOSED = 2; | |
| 832 var RESETTING = 3; | |
| 833 | |
| 834 var nextObserverId = 1; | |
| 835 | |
| 836 function Observer() { | |
| 837 this.state_ = UNOPENED; | |
| 838 this.callback_ = undefined; | |
| 839 this.target_ = undefined; // TODO(rafaelw): Should be WeakRef | |
| 840 this.directObserver_ = undefined; | |
| 841 this.value_ = undefined; | |
| 842 this.id_ = nextObserverId++; | |
| 843 } | |
| 844 | |
| 845 Observer.prototype = { | |
| 846 open: function(callback, target) { | |
| 847 if (this.state_ != UNOPENED) | |
| 848 throw Error('Observer has already been opened.'); | |
| 849 | |
| 850 addToAll(this); | |
| 851 this.callback_ = callback; | |
| 852 this.target_ = target; | |
| 853 this.connect_(); | |
| 854 this.state_ = OPENED; | |
| 855 return this.value_; | |
| 856 }, | |
| 857 | |
| 858 close: function() { | |
| 859 if (this.state_ != OPENED) | |
| 860 return; | |
| 861 | |
| 862 removeFromAll(this); | |
| 863 this.disconnect_(); | |
| 864 this.value_ = undefined; | |
| 865 this.callback_ = undefined; | |
| 866 this.target_ = undefined; | |
| 867 this.state_ = CLOSED; | |
| 868 }, | |
| 869 | |
| 870 deliver: function() { | |
| 871 if (this.state_ != OPENED) | |
| 872 return; | |
| 873 | |
| 874 dirtyCheck(this); | |
| 875 }, | |
| 876 | |
| 877 report_: function(changes) { | |
| 878 try { | |
| 879 this.callback_.apply(this.target_, changes); | |
| 880 } catch (ex) { | |
| 881 Observer._errorThrownDuringCallback = true; | |
| 882 console.error('Exception caught during observer callback: ' + | |
| 883 (ex.stack || ex)); | |
| 884 } | |
| 885 }, | |
| 886 | |
| 887 discardChanges: function() { | |
| 888 this.check_(undefined, true); | |
| 889 return this.value_; | |
| 890 } | |
| 891 } | |
| 892 | |
| 893 var collectObservers = !hasObserve; | |
| 894 var allObservers; | |
| 895 Observer._allObserversCount = 0; | |
| 896 | |
| 897 if (collectObservers) { | |
| 898 allObservers = []; | |
| 899 } | |
| 900 | |
| 901 function addToAll(observer) { | |
| 902 Observer._allObserversCount++; | |
| 903 if (!collectObservers) | |
| 904 return; | |
| 905 | |
| 906 allObservers.push(observer); | |
| 907 } | |
| 908 | |
| 909 function removeFromAll(observer) { | |
| 910 Observer._allObserversCount--; | |
| 911 } | |
| 912 | |
| 913 var runningMicrotaskCheckpoint = false; | |
| 914 | |
| 915 global.Platform = global.Platform || {}; | |
| 916 | |
| 917 global.Platform.performMicrotaskCheckpoint = function() { | |
| 918 if (runningMicrotaskCheckpoint) | |
| 919 return; | |
| 920 | |
| 921 if (!collectObservers) | |
| 922 return; | |
| 923 | |
| 924 runningMicrotaskCheckpoint = true; | |
| 925 | |
| 926 var cycles = 0; | |
| 927 var anyChanged, toCheck; | |
| 928 | |
| 929 do { | |
| 930 cycles++; | |
| 931 toCheck = allObservers; | |
| 932 allObservers = []; | |
| 933 anyChanged = false; | |
| 934 | |
| 935 for (var i = 0; i < toCheck.length; i++) { | |
| 936 var observer = toCheck[i]; | |
| 937 if (observer.state_ != OPENED) | |
| 938 continue; | |
| 939 | |
| 940 if (observer.check_()) | |
| 941 anyChanged = true; | |
| 942 | |
| 943 allObservers.push(observer); | |
| 944 } | |
| 945 if (runEOMTasks()) | |
| 946 anyChanged = true; | |
| 947 } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged); | |
| 948 | |
| 949 if (testingExposeCycleCount) | |
| 950 global.dirtyCheckCycleCount = cycles; | |
| 951 | |
| 952 runningMicrotaskCheckpoint = false; | |
| 953 }; | |
| 954 | |
| 955 if (collectObservers) { | |
| 956 global.Platform.clearObservers = function() { | |
| 957 allObservers = []; | |
| 958 }; | |
| 959 } | |
| 960 | |
| 961 function ObjectObserver(object) { | |
| 962 Observer.call(this); | |
| 963 this.value_ = object; | |
| 964 this.oldObject_ = undefined; | |
| 965 } | |
| 966 | |
| 967 ObjectObserver.prototype = createObject({ | |
| 968 __proto__: Observer.prototype, | |
| 969 | |
| 970 arrayObserve: false, | |
| 971 | |
| 972 connect_: function(callback, target) { | |
| 973 if (hasObserve) { | |
| 974 this.directObserver_ = getObservedObject(this, this.value_, | |
| 975 this.arrayObserve); | |
| 976 } else { | |
| 977 this.oldObject_ = this.copyObject(this.value_); | |
| 978 } | |
| 979 | |
| 980 }, | |
| 981 | |
| 982 copyObject: function(object) { | |
| 983 var copy = Array.isArray(object) ? [] : {}; | |
| 984 for (var prop in object) { | |
| 985 copy[prop] = object[prop]; | |
| 986 }; | |
| 987 if (Array.isArray(object)) | |
| 988 copy.length = object.length; | |
| 989 return copy; | |
| 990 }, | |
| 991 | |
| 992 check_: function(changeRecords, skipChanges) { | |
| 993 var diff; | |
| 994 var oldValues; | |
| 995 if (hasObserve) { | |
| 996 if (!changeRecords) | |
| 997 return false; | |
| 998 | |
| 999 oldValues = {}; | |
| 1000 diff = diffObjectFromChangeRecords(this.value_, changeRecords, | |
| 1001 oldValues); | |
| 1002 } else { | |
| 1003 oldValues = this.oldObject_; | |
| 1004 diff = diffObjectFromOldObject(this.value_, this.oldObject_); | |
| 1005 } | |
| 1006 | |
| 1007 if (diffIsEmpty(diff)) | |
| 1008 return false; | |
| 1009 | |
| 1010 if (!hasObserve) | |
| 1011 this.oldObject_ = this.copyObject(this.value_); | |
| 1012 | |
| 1013 this.report_([ | |
| 1014 diff.added || {}, | |
| 1015 diff.removed || {}, | |
| 1016 diff.changed || {}, | |
| 1017 function(property) { | |
| 1018 return oldValues[property]; | |
| 1019 } | |
| 1020 ]); | |
| 1021 | |
| 1022 return true; | |
| 1023 }, | |
| 1024 | |
| 1025 disconnect_: function() { | |
| 1026 if (hasObserve) { | |
| 1027 this.directObserver_.close(); | |
| 1028 this.directObserver_ = undefined; | |
| 1029 } else { | |
| 1030 this.oldObject_ = undefined; | |
| 1031 } | |
| 1032 }, | |
| 1033 | |
| 1034 deliver: function() { | |
| 1035 if (this.state_ != OPENED) | |
| 1036 return; | |
| 1037 | |
| 1038 if (hasObserve) | |
| 1039 this.directObserver_.deliver(false); | |
| 1040 else | |
| 1041 dirtyCheck(this); | |
| 1042 }, | |
| 1043 | |
| 1044 discardChanges: function() { | |
| 1045 if (this.directObserver_) | |
| 1046 this.directObserver_.deliver(true); | |
| 1047 else | |
| 1048 this.oldObject_ = this.copyObject(this.value_); | |
| 1049 | |
| 1050 return this.value_; | |
| 1051 } | |
| 1052 }); | |
| 1053 | |
| 1054 function ArrayObserver(array) { | |
| 1055 if (!Array.isArray(array)) | |
| 1056 throw Error('Provided object is not an Array'); | |
| 1057 ObjectObserver.call(this, array); | |
| 1058 } | |
| 1059 | |
| 1060 ArrayObserver.prototype = createObject({ | |
| 1061 | |
| 1062 __proto__: ObjectObserver.prototype, | |
| 1063 | |
| 1064 arrayObserve: true, | |
| 1065 | |
| 1066 copyObject: function(arr) { | |
| 1067 return arr.slice(); | |
| 1068 }, | |
| 1069 | |
| 1070 check_: function(changeRecords) { | |
| 1071 var splices; | |
| 1072 if (hasObserve) { | |
| 1073 if (!changeRecords) | |
| 1074 return false; | |
| 1075 splices = projectArraySplices(this.value_, changeRecords); | |
| 1076 } else { | |
| 1077 splices = calcSplices(this.value_, 0, this.value_.length, | |
| 1078 this.oldObject_, 0, this.oldObject_.length); | |
| 1079 } | |
| 1080 | |
| 1081 if (!splices || !splices.length) | |
| 1082 return false; | |
| 1083 | |
| 1084 if (!hasObserve) | |
| 1085 this.oldObject_ = this.copyObject(this.value_); | |
| 1086 | |
| 1087 this.report_([splices]); | |
| 1088 return true; | |
| 1089 } | |
| 1090 }); | |
| 1091 | |
| 1092 ArrayObserver.applySplices = function(previous, current, splices) { | |
| 1093 splices.forEach(function(splice) { | |
| 1094 var spliceArgs = [splice.index, splice.removed.length]; | |
| 1095 var addIndex = splice.index; | |
| 1096 while (addIndex < splice.index + splice.addedCount) { | |
| 1097 spliceArgs.push(current[addIndex]); | |
| 1098 addIndex++; | |
| 1099 } | |
| 1100 | |
| 1101 Array.prototype.splice.apply(previous, spliceArgs); | |
| 1102 }); | |
| 1103 }; | |
| 1104 | |
| 1105 function PathObserver(object, path) { | |
| 1106 Observer.call(this); | |
| 1107 | |
| 1108 this.object_ = object; | |
| 1109 this.path_ = getPath(path); | |
| 1110 this.directObserver_ = undefined; | |
| 1111 } | |
| 1112 | |
| 1113 PathObserver.prototype = createObject({ | |
| 1114 __proto__: Observer.prototype, | |
| 1115 | |
| 1116 get path() { | |
| 1117 return this.path_; | |
| 1118 }, | |
| 1119 | |
| 1120 connect_: function() { | |
| 1121 if (hasObserve) | |
| 1122 this.directObserver_ = getObservedSet(this, this.object_); | |
| 1123 | |
| 1124 this.check_(undefined, true); | |
| 1125 }, | |
| 1126 | |
| 1127 disconnect_: function() { | |
| 1128 this.value_ = undefined; | |
| 1129 | |
| 1130 if (this.directObserver_) { | |
| 1131 this.directObserver_.close(this); | |
| 1132 this.directObserver_ = undefined; | |
| 1133 } | |
| 1134 }, | |
| 1135 | |
| 1136 iterateObjects_: function(observe) { | |
| 1137 this.path_.iterateObjects(this.object_, observe); | |
| 1138 }, | |
| 1139 | |
| 1140 check_: function(changeRecords, skipChanges) { | |
| 1141 var oldValue = this.value_; | |
| 1142 this.value_ = this.path_.getValueFrom(this.object_); | |
| 1143 if (skipChanges || areSameValue(this.value_, oldValue)) | |
| 1144 return false; | |
| 1145 | |
| 1146 this.report_([this.value_, oldValue, this]); | |
| 1147 return true; | |
| 1148 }, | |
| 1149 | |
| 1150 setValue: function(newValue) { | |
| 1151 if (this.path_) | |
| 1152 this.path_.setValueFrom(this.object_, newValue); | |
| 1153 } | |
| 1154 }); | |
| 1155 | |
| 1156 function CompoundObserver(reportChangesOnOpen) { | |
| 1157 Observer.call(this); | |
| 1158 | |
| 1159 this.reportChangesOnOpen_ = reportChangesOnOpen; | |
| 1160 this.value_ = []; | |
| 1161 this.directObserver_ = undefined; | |
| 1162 this.observed_ = []; | |
| 1163 } | |
| 1164 | |
| 1165 var observerSentinel = {}; | |
| 1166 | |
| 1167 CompoundObserver.prototype = createObject({ | |
| 1168 __proto__: Observer.prototype, | |
| 1169 | |
| 1170 connect_: function() { | |
| 1171 if (hasObserve) { | |
| 1172 var object; | |
| 1173 var needsDirectObserver = false; | |
| 1174 for (var i = 0; i < this.observed_.length; i += 2) { | |
| 1175 object = this.observed_[i] | |
| 1176 if (object !== observerSentinel) { | |
| 1177 needsDirectObserver = true; | |
| 1178 break; | |
| 1179 } | |
| 1180 } | |
| 1181 | |
| 1182 if (needsDirectObserver) | |
| 1183 this.directObserver_ = getObservedSet(this, object); | |
| 1184 } | |
| 1185 | |
| 1186 this.check_(undefined, !this.reportChangesOnOpen_); | |
| 1187 }, | |
| 1188 | |
| 1189 disconnect_: function() { | |
| 1190 for (var i = 0; i < this.observed_.length; i += 2) { | |
| 1191 if (this.observed_[i] === observerSentinel) | |
| 1192 this.observed_[i + 1].close(); | |
| 1193 } | |
| 1194 this.observed_.length = 0; | |
| 1195 this.value_.length = 0; | |
| 1196 | |
| 1197 if (this.directObserver_) { | |
| 1198 this.directObserver_.close(this); | |
| 1199 this.directObserver_ = undefined; | |
| 1200 } | |
| 1201 }, | |
| 1202 | |
| 1203 addPath: function(object, path) { | |
| 1204 if (this.state_ != UNOPENED && this.state_ != RESETTING) | |
| 1205 throw Error('Cannot add paths once started.'); | |
| 1206 | |
| 1207 var path = getPath(path); | |
| 1208 this.observed_.push(object, path); | |
| 1209 if (!this.reportChangesOnOpen_) | |
| 1210 return; | |
| 1211 var index = this.observed_.length / 2 - 1; | |
| 1212 this.value_[index] = path.getValueFrom(object); | |
| 1213 }, | |
| 1214 | |
| 1215 addObserver: function(observer) { | |
| 1216 if (this.state_ != UNOPENED && this.state_ != RESETTING) | |
| 1217 throw Error('Cannot add observers once started.'); | |
| 1218 | |
| 1219 this.observed_.push(observerSentinel, observer); | |
| 1220 if (!this.reportChangesOnOpen_) | |
| 1221 return; | |
| 1222 var index = this.observed_.length / 2 - 1; | |
| 1223 this.value_[index] = observer.open(this.deliver, this); | |
| 1224 }, | |
| 1225 | |
| 1226 startReset: function() { | |
| 1227 if (this.state_ != OPENED) | |
| 1228 throw Error('Can only reset while open'); | |
| 1229 | |
| 1230 this.state_ = RESETTING; | |
| 1231 this.disconnect_(); | |
| 1232 }, | |
| 1233 | |
| 1234 finishReset: function() { | |
| 1235 if (this.state_ != RESETTING) | |
| 1236 throw Error('Can only finishReset after startReset'); | |
| 1237 this.state_ = OPENED; | |
| 1238 this.connect_(); | |
| 1239 | |
| 1240 return this.value_; | |
| 1241 }, | |
| 1242 | |
| 1243 iterateObjects_: function(observe) { | |
| 1244 var object; | |
| 1245 for (var i = 0; i < this.observed_.length; i += 2) { | |
| 1246 object = this.observed_[i] | |
| 1247 if (object !== observerSentinel) | |
| 1248 this.observed_[i + 1].iterateObjects(object, observe) | |
| 1249 } | |
| 1250 }, | |
| 1251 | |
| 1252 check_: function(changeRecords, skipChanges) { | |
| 1253 var oldValues; | |
| 1254 for (var i = 0; i < this.observed_.length; i += 2) { | |
| 1255 var object = this.observed_[i]; | |
| 1256 var path = this.observed_[i+1]; | |
| 1257 var value; | |
| 1258 if (object === observerSentinel) { | |
| 1259 var observable = path; | |
| 1260 value = this.state_ === UNOPENED ? | |
| 1261 observable.open(this.deliver, this) : | |
| 1262 observable.discardChanges(); | |
| 1263 } else { | |
| 1264 value = path.getValueFrom(object); | |
| 1265 } | |
| 1266 | |
| 1267 if (skipChanges) { | |
| 1268 this.value_[i / 2] = value; | |
| 1269 continue; | |
| 1270 } | |
| 1271 | |
| 1272 if (areSameValue(value, this.value_[i / 2])) | |
| 1273 continue; | |
| 1274 | |
| 1275 oldValues = oldValues || []; | |
| 1276 oldValues[i / 2] = this.value_[i / 2]; | |
| 1277 this.value_[i / 2] = value; | |
| 1278 } | |
| 1279 | |
| 1280 if (!oldValues) | |
| 1281 return false; | |
| 1282 | |
| 1283 // TODO(rafaelw): Having observed_ as the third callback arg here is | |
| 1284 // pretty lame API. Fix. | |
| 1285 this.report_([this.value_, oldValues, this.observed_]); | |
| 1286 return true; | |
| 1287 } | |
| 1288 }); | |
| 1289 | |
| 1290 function identFn(value) { return value; } | |
| 1291 | |
| 1292 function ObserverTransform(observable, getValueFn, setValueFn, | |
| 1293 dontPassThroughSet) { | |
| 1294 this.callback_ = undefined; | |
| 1295 this.target_ = undefined; | |
| 1296 this.value_ = undefined; | |
| 1297 this.observable_ = observable; | |
| 1298 this.getValueFn_ = getValueFn || identFn; | |
| 1299 this.setValueFn_ = setValueFn || identFn; | |
| 1300 // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this | |
| 1301 // at the moment because of a bug in it's dependency tracking. | |
| 1302 this.dontPassThroughSet_ = dontPassThroughSet; | |
| 1303 } | |
| 1304 | |
| 1305 ObserverTransform.prototype = { | |
| 1306 open: function(callback, target) { | |
| 1307 this.callback_ = callback; | |
| 1308 this.target_ = target; | |
| 1309 this.value_ = | |
| 1310 this.getValueFn_(this.observable_.open(this.observedCallback_, this)); | |
| 1311 return this.value_; | |
| 1312 }, | |
| 1313 | |
| 1314 observedCallback_: function(value) { | |
| 1315 value = this.getValueFn_(value); | |
| 1316 if (areSameValue(value, this.value_)) | |
| 1317 return; | |
| 1318 var oldValue = this.value_; | |
| 1319 this.value_ = value; | |
| 1320 this.callback_.call(this.target_, this.value_, oldValue); | |
| 1321 }, | |
| 1322 | |
| 1323 discardChanges: function() { | |
| 1324 this.value_ = this.getValueFn_(this.observable_.discardChanges()); | |
| 1325 return this.value_; | |
| 1326 }, | |
| 1327 | |
| 1328 deliver: function() { | |
| 1329 return this.observable_.deliver(); | |
| 1330 }, | |
| 1331 | |
| 1332 setValue: function(value) { | |
| 1333 value = this.setValueFn_(value); | |
| 1334 if (!this.dontPassThroughSet_ && this.observable_.setValue) | |
| 1335 return this.observable_.setValue(value); | |
| 1336 }, | |
| 1337 | |
| 1338 close: function() { | |
| 1339 if (this.observable_) | |
| 1340 this.observable_.close(); | |
| 1341 this.callback_ = undefined; | |
| 1342 this.target_ = undefined; | |
| 1343 this.observable_ = undefined; | |
| 1344 this.value_ = undefined; | |
| 1345 this.getValueFn_ = undefined; | |
| 1346 this.setValueFn_ = undefined; | |
| 1347 } | |
| 1348 } | |
| 1349 | |
| 1350 var expectedRecordTypes = { | |
| 1351 add: true, | |
| 1352 update: true, | |
| 1353 delete: true | |
| 1354 }; | |
| 1355 | |
| 1356 function diffObjectFromChangeRecords(object, changeRecords, oldValues) { | |
| 1357 var added = {}; | |
| 1358 var removed = {}; | |
| 1359 | |
| 1360 for (var i = 0; i < changeRecords.length; i++) { | |
| 1361 var record = changeRecords[i]; | |
| 1362 if (!expectedRecordTypes[record.type]) { | |
| 1363 console.error('Unknown changeRecord type: ' + record.type); | |
| 1364 console.error(record); | |
| 1365 continue; | |
| 1366 } | |
| 1367 | |
| 1368 if (!(record.name in oldValues)) | |
| 1369 oldValues[record.name] = record.oldValue; | |
| 1370 | |
| 1371 if (record.type == 'update') | |
| 1372 continue; | |
| 1373 | |
| 1374 if (record.type == 'add') { | |
| 1375 if (record.name in removed) | |
| 1376 delete removed[record.name]; | |
| 1377 else | |
| 1378 added[record.name] = true; | |
| 1379 | |
| 1380 continue; | |
| 1381 } | |
| 1382 | |
| 1383 // type = 'delete' | |
| 1384 if (record.name in added) { | |
| 1385 delete added[record.name]; | |
| 1386 delete oldValues[record.name]; | |
| 1387 } else { | |
| 1388 removed[record.name] = true; | |
| 1389 } | |
| 1390 } | |
| 1391 | |
| 1392 for (var prop in added) | |
| 1393 added[prop] = object[prop]; | |
| 1394 | |
| 1395 for (var prop in removed) | |
| 1396 removed[prop] = undefined; | |
| 1397 | |
| 1398 var changed = {}; | |
| 1399 for (var prop in oldValues) { | |
| 1400 if (prop in added || prop in removed) | |
| 1401 continue; | |
| 1402 | |
| 1403 var newValue = object[prop]; | |
| 1404 if (oldValues[prop] !== newValue) | |
| 1405 changed[prop] = newValue; | |
| 1406 } | |
| 1407 | |
| 1408 return { | |
| 1409 added: added, | |
| 1410 removed: removed, | |
| 1411 changed: changed | |
| 1412 }; | |
| 1413 } | |
| 1414 | |
| 1415 function newSplice(index, removed, addedCount) { | |
| 1416 return { | |
| 1417 index: index, | |
| 1418 removed: removed, | |
| 1419 addedCount: addedCount | |
| 1420 }; | |
| 1421 } | |
| 1422 | |
| 1423 var EDIT_LEAVE = 0; | |
| 1424 var EDIT_UPDATE = 1; | |
| 1425 var EDIT_ADD = 2; | |
| 1426 var EDIT_DELETE = 3; | |
| 1427 | |
| 1428 function ArraySplice() {} | |
| 1429 | |
| 1430 ArraySplice.prototype = { | |
| 1431 | |
| 1432 // Note: This function is *based* on the computation of the Levenshtein | |
| 1433 // "edit" distance. The one change is that "updates" are treated as two | |
| 1434 // edits - not one. With Array splices, an update is really a delete | |
| 1435 // followed by an add. By retaining this, we optimize for "keeping" the | |
| 1436 // maximum array items in the original array. For example: | |
| 1437 // | |
| 1438 // 'xxxx123' -> '123yyyy' | |
| 1439 // | |
| 1440 // With 1-edit updates, the shortest path would be just to update all seven | |
| 1441 // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This | |
| 1442 // leaves the substring '123' intact. | |
| 1443 calcEditDistances: function(current, currentStart, currentEnd, | |
| 1444 old, oldStart, oldEnd) { | |
| 1445 // "Deletion" columns | |
| 1446 var rowCount = oldEnd - oldStart + 1; | |
| 1447 var columnCount = currentEnd - currentStart + 1; | |
| 1448 var distances = new Array(rowCount); | |
| 1449 | |
| 1450 // "Addition" rows. Initialize null column. | |
| 1451 for (var i = 0; i < rowCount; i++) { | |
| 1452 distances[i] = new Array(columnCount); | |
| 1453 distances[i][0] = i; | |
| 1454 } | |
| 1455 | |
| 1456 // Initialize null row | |
| 1457 for (var j = 0; j < columnCount; j++) | |
| 1458 distances[0][j] = j; | |
| 1459 | |
| 1460 for (var i = 1; i < rowCount; i++) { | |
| 1461 for (var j = 1; j < columnCount; j++) { | |
| 1462 if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) | |
| 1463 distances[i][j] = distances[i - 1][j - 1]; | |
| 1464 else { | |
| 1465 var north = distances[i - 1][j] + 1; | |
| 1466 var west = distances[i][j - 1] + 1; | |
| 1467 distances[i][j] = north < west ? north : west; | |
| 1468 } | |
| 1469 } | |
| 1470 } | |
| 1471 | |
| 1472 return distances; | |
| 1473 }, | |
| 1474 | |
| 1475 // This starts at the final weight, and walks "backward" by finding | |
| 1476 // the minimum previous weight recursively until the origin of the weight | |
| 1477 // matrix. | |
| 1478 spliceOperationsFromEditDistances: function(distances) { | |
| 1479 var i = distances.length - 1; | |
| 1480 var j = distances[0].length - 1; | |
| 1481 var current = distances[i][j]; | |
| 1482 var edits = []; | |
| 1483 while (i > 0 || j > 0) { | |
| 1484 if (i == 0) { | |
| 1485 edits.push(EDIT_ADD); | |
| 1486 j--; | |
| 1487 continue; | |
| 1488 } | |
| 1489 if (j == 0) { | |
| 1490 edits.push(EDIT_DELETE); | |
| 1491 i--; | |
| 1492 continue; | |
| 1493 } | |
| 1494 var northWest = distances[i - 1][j - 1]; | |
| 1495 var west = distances[i - 1][j]; | |
| 1496 var north = distances[i][j - 1]; | |
| 1497 | |
| 1498 var min; | |
| 1499 if (west < north) | |
| 1500 min = west < northWest ? west : northWest; | |
| 1501 else | |
| 1502 min = north < northWest ? north : northWest; | |
| 1503 | |
| 1504 if (min == northWest) { | |
| 1505 if (northWest == current) { | |
| 1506 edits.push(EDIT_LEAVE); | |
| 1507 } else { | |
| 1508 edits.push(EDIT_UPDATE); | |
| 1509 current = northWest; | |
| 1510 } | |
| 1511 i--; | |
| 1512 j--; | |
| 1513 } else if (min == west) { | |
| 1514 edits.push(EDIT_DELETE); | |
| 1515 i--; | |
| 1516 current = west; | |
| 1517 } else { | |
| 1518 edits.push(EDIT_ADD); | |
| 1519 j--; | |
| 1520 current = north; | |
| 1521 } | |
| 1522 } | |
| 1523 | |
| 1524 edits.reverse(); | |
| 1525 return edits; | |
| 1526 }, | |
| 1527 | |
| 1528 /** | |
| 1529 * Splice Projection functions: | |
| 1530 * | |
| 1531 * A splice map is a representation of how a previous array of items | |
| 1532 * was transformed into a new array of items. Conceptually it is a list of | |
| 1533 * tuples of | |
| 1534 * | |
| 1535 * <index, removed, addedCount> | |
| 1536 * | |
| 1537 * which are kept in ascending index order of. The tuple represents that at | |
| 1538 * the |index|, |removed| sequence of items were removed, and counting forwa
rd | |
| 1539 * from |index|, |addedCount| items were added. | |
| 1540 */ | |
| 1541 | |
| 1542 /** | |
| 1543 * Lacking individual splice mutation information, the minimal set of | |
| 1544 * splices can be synthesized given the previous state and final state of an | |
| 1545 * array. The basic approach is to calculate the edit distance matrix and | |
| 1546 * choose the shortest path through it. | |
| 1547 * | |
| 1548 * Complexity: O(l * p) | |
| 1549 * l: The length of the current array | |
| 1550 * p: The length of the old array | |
| 1551 */ | |
| 1552 calcSplices: function(current, currentStart, currentEnd, | |
| 1553 old, oldStart, oldEnd) { | |
| 1554 var prefixCount = 0; | |
| 1555 var suffixCount = 0; | |
| 1556 | |
| 1557 var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); | |
| 1558 if (currentStart == 0 && oldStart == 0) | |
| 1559 prefixCount = this.sharedPrefix(current, old, minLength); | |
| 1560 | |
| 1561 if (currentEnd == current.length && oldEnd == old.length) | |
| 1562 suffixCount = this.sharedSuffix(current, old, minLength - prefixCount); | |
| 1563 | |
| 1564 currentStart += prefixCount; | |
| 1565 oldStart += prefixCount; | |
| 1566 currentEnd -= suffixCount; | |
| 1567 oldEnd -= suffixCount; | |
| 1568 | |
| 1569 if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) | |
| 1570 return []; | |
| 1571 | |
| 1572 if (currentStart == currentEnd) { | |
| 1573 var splice = newSplice(currentStart, [], 0); | |
| 1574 while (oldStart < oldEnd) | |
| 1575 splice.removed.push(old[oldStart++]); | |
| 1576 | |
| 1577 return [ splice ]; | |
| 1578 } else if (oldStart == oldEnd) | |
| 1579 return [ newSplice(currentStart, [], currentEnd - currentStart) ]; | |
| 1580 | |
| 1581 var ops = this.spliceOperationsFromEditDistances( | |
| 1582 this.calcEditDistances(current, currentStart, currentEnd, | |
| 1583 old, oldStart, oldEnd)); | |
| 1584 | |
| 1585 var splice = undefined; | |
| 1586 var splices = []; | |
| 1587 var index = currentStart; | |
| 1588 var oldIndex = oldStart; | |
| 1589 for (var i = 0; i < ops.length; i++) { | |
| 1590 switch(ops[i]) { | |
| 1591 case EDIT_LEAVE: | |
| 1592 if (splice) { | |
| 1593 splices.push(splice); | |
| 1594 splice = undefined; | |
| 1595 } | |
| 1596 | |
| 1597 index++; | |
| 1598 oldIndex++; | |
| 1599 break; | |
| 1600 case EDIT_UPDATE: | |
| 1601 if (!splice) | |
| 1602 splice = newSplice(index, [], 0); | |
| 1603 | |
| 1604 splice.addedCount++; | |
| 1605 index++; | |
| 1606 | |
| 1607 splice.removed.push(old[oldIndex]); | |
| 1608 oldIndex++; | |
| 1609 break; | |
| 1610 case EDIT_ADD: | |
| 1611 if (!splice) | |
| 1612 splice = newSplice(index, [], 0); | |
| 1613 | |
| 1614 splice.addedCount++; | |
| 1615 index++; | |
| 1616 break; | |
| 1617 case EDIT_DELETE: | |
| 1618 if (!splice) | |
| 1619 splice = newSplice(index, [], 0); | |
| 1620 | |
| 1621 splice.removed.push(old[oldIndex]); | |
| 1622 oldIndex++; | |
| 1623 break; | |
| 1624 } | |
| 1625 } | |
| 1626 | |
| 1627 if (splice) { | |
| 1628 splices.push(splice); | |
| 1629 } | |
| 1630 return splices; | |
| 1631 }, | |
| 1632 | |
| 1633 sharedPrefix: function(current, old, searchLength) { | |
| 1634 for (var i = 0; i < searchLength; i++) | |
| 1635 if (!this.equals(current[i], old[i])) | |
| 1636 return i; | |
| 1637 return searchLength; | |
| 1638 }, | |
| 1639 | |
| 1640 sharedSuffix: function(current, old, searchLength) { | |
| 1641 var index1 = current.length; | |
| 1642 var index2 = old.length; | |
| 1643 var count = 0; | |
| 1644 while (count < searchLength && this.equals(current[--index1], old[--index2
])) | |
| 1645 count++; | |
| 1646 | |
| 1647 return count; | |
| 1648 }, | |
| 1649 | |
| 1650 calculateSplices: function(current, previous) { | |
| 1651 return this.calcSplices(current, 0, current.length, previous, 0, | |
| 1652 previous.length); | |
| 1653 }, | |
| 1654 | |
| 1655 equals: function(currentValue, previousValue) { | |
| 1656 return currentValue === previousValue; | |
| 1657 } | |
| 1658 }; | |
| 1659 | |
| 1660 var arraySplice = new ArraySplice(); | |
| 1661 | |
| 1662 function calcSplices(current, currentStart, currentEnd, | |
| 1663 old, oldStart, oldEnd) { | |
| 1664 return arraySplice.calcSplices(current, currentStart, currentEnd, | |
| 1665 old, oldStart, oldEnd); | |
| 1666 } | |
| 1667 | |
| 1668 function intersect(start1, end1, start2, end2) { | |
| 1669 // Disjoint | |
| 1670 if (end1 < start2 || end2 < start1) | |
| 1671 return -1; | |
| 1672 | |
| 1673 // Adjacent | |
| 1674 if (end1 == start2 || end2 == start1) | |
| 1675 return 0; | |
| 1676 | |
| 1677 // Non-zero intersect, span1 first | |
| 1678 if (start1 < start2) { | |
| 1679 if (end1 < end2) | |
| 1680 return end1 - start2; // Overlap | |
| 1681 else | |
| 1682 return end2 - start2; // Contained | |
| 1683 } else { | |
| 1684 // Non-zero intersect, span2 first | |
| 1685 if (end2 < end1) | |
| 1686 return end2 - start1; // Overlap | |
| 1687 else | |
| 1688 return end1 - start1; // Contained | |
| 1689 } | |
| 1690 } | |
| 1691 | |
| 1692 function mergeSplice(splices, index, removed, addedCount) { | |
| 1693 | |
| 1694 var splice = newSplice(index, removed, addedCount); | |
| 1695 | |
| 1696 var inserted = false; | |
| 1697 var insertionOffset = 0; | |
| 1698 | |
| 1699 for (var i = 0; i < splices.length; i++) { | |
| 1700 var current = splices[i]; | |
| 1701 current.index += insertionOffset; | |
| 1702 | |
| 1703 if (inserted) | |
| 1704 continue; | |
| 1705 | |
| 1706 var intersectCount = intersect(splice.index, | |
| 1707 splice.index + splice.removed.length, | |
| 1708 current.index, | |
| 1709 current.index + current.addedCount); | |
| 1710 | |
| 1711 if (intersectCount >= 0) { | |
| 1712 // Merge the two splices | |
| 1713 | |
| 1714 splices.splice(i, 1); | |
| 1715 i--; | |
| 1716 | |
| 1717 insertionOffset -= current.addedCount - current.removed.length; | |
| 1718 | |
| 1719 splice.addedCount += current.addedCount - intersectCount; | |
| 1720 var deleteCount = splice.removed.length + | |
| 1721 current.removed.length - intersectCount; | |
| 1722 | |
| 1723 if (!splice.addedCount && !deleteCount) { | |
| 1724 // merged splice is a noop. discard. | |
| 1725 inserted = true; | |
| 1726 } else { | |
| 1727 var removed = current.removed; | |
| 1728 | |
| 1729 if (splice.index < current.index) { | |
| 1730 // some prefix of splice.removed is prepended to current.removed. | |
| 1731 var prepend = splice.removed.slice(0, current.index - splice.index); | |
| 1732 Array.prototype.push.apply(prepend, removed); | |
| 1733 removed = prepend; | |
| 1734 } | |
| 1735 | |
| 1736 if (splice.index + splice.removed.length > current.index + current.add
edCount) { | |
| 1737 // some suffix of splice.removed is appended to current.removed. | |
| 1738 var append = splice.removed.slice(current.index + current.addedCount
- splice.index); | |
| 1739 Array.prototype.push.apply(removed, append); | |
| 1740 } | |
| 1741 | |
| 1742 splice.removed = removed; | |
| 1743 if (current.index < splice.index) { | |
| 1744 splice.index = current.index; | |
| 1745 } | |
| 1746 } | |
| 1747 } else if (splice.index < current.index) { | |
| 1748 // Insert splice here. | |
| 1749 | |
| 1750 inserted = true; | |
| 1751 | |
| 1752 splices.splice(i, 0, splice); | |
| 1753 i++; | |
| 1754 | |
| 1755 var offset = splice.addedCount - splice.removed.length | |
| 1756 current.index += offset; | |
| 1757 insertionOffset += offset; | |
| 1758 } | |
| 1759 } | |
| 1760 | |
| 1761 if (!inserted) | |
| 1762 splices.push(splice); | |
| 1763 } | |
| 1764 | |
| 1765 function createInitialSplices(array, changeRecords) { | |
| 1766 var splices = []; | |
| 1767 | |
| 1768 for (var i = 0; i < changeRecords.length; i++) { | |
| 1769 var record = changeRecords[i]; | |
| 1770 switch(record.type) { | |
| 1771 case 'splice': | |
| 1772 mergeSplice(splices, record.index, record.removed.slice(), record.adde
dCount); | |
| 1773 break; | |
| 1774 case 'add': | |
| 1775 case 'update': | |
| 1776 case 'delete': | |
| 1777 if (!isIndex(record.name)) | |
| 1778 continue; | |
| 1779 var index = toNumber(record.name); | |
| 1780 if (index < 0) | |
| 1781 continue; | |
| 1782 mergeSplice(splices, index, [record.oldValue], 1); | |
| 1783 break; | |
| 1784 default: | |
| 1785 console.error('Unexpected record type: ' + JSON.stringify(record)); | |
| 1786 break; | |
| 1787 } | |
| 1788 } | |
| 1789 | |
| 1790 return splices; | |
| 1791 } | |
| 1792 | |
| 1793 function projectArraySplices(array, changeRecords) { | |
| 1794 var splices = []; | |
| 1795 | |
| 1796 createInitialSplices(array, changeRecords).forEach(function(splice) { | |
| 1797 if (splice.addedCount == 1 && splice.removed.length == 1) { | |
| 1798 if (splice.removed[0] !== array[splice.index]) | |
| 1799 splices.push(splice); | |
| 1800 | |
| 1801 return | |
| 1802 }; | |
| 1803 | |
| 1804 splices = splices.concat(calcSplices(array, splice.index, splice.index + s
plice.addedCount, | |
| 1805 splice.removed, 0, splice.removed.len
gth)); | |
| 1806 }); | |
| 1807 | |
| 1808 return splices; | |
| 1809 } | |
| 1810 | |
| 1811 global.Observer = Observer; | |
| 1812 global.Observer.runEOM_ = runEOM; | |
| 1813 global.Observer.observerSentinel_ = observerSentinel; // for testing. | |
| 1814 global.Observer.hasObjectObserve = hasObserve; | |
| 1815 global.ArrayObserver = ArrayObserver; | |
| 1816 global.ArrayObserver.calculateSplices = function(current, previous) { | |
| 1817 return arraySplice.calculateSplices(current, previous); | |
| 1818 }; | |
| 1819 | |
| 1820 global.ArraySplice = ArraySplice; | |
| 1821 global.ObjectObserver = ObjectObserver; | |
| 1822 global.PathObserver = PathObserver; | |
| 1823 global.CompoundObserver = CompoundObserver; | |
| 1824 global.Path = Path; | |
| 1825 global.ObserverTransform = ObserverTransform; | |
| 1826 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m
odule ? global : this || window); | |
| 1827 | |
| 1828 // Copyright 2012 The Polymer Authors. All rights reserved. | |
| 1829 // Use of this source code is goverened by a BSD-style | |
| 1830 // license that can be found in the LICENSE file. | |
| 1831 | |
| 1832 window.ShadowDOMPolyfill = {}; | |
| 1833 | |
| 1834 (function(scope) { | |
| 1835 'use strict'; | |
| 1836 | |
| 1837 var constructorTable = new WeakMap(); | |
| 1838 var nativePrototypeTable = new WeakMap(); | |
| 1839 var wrappers = Object.create(null); | |
| 1840 | |
| 1841 function detectEval() { | |
| 1842 // Don't test for eval if we're running in a Chrome App environment. | |
| 1843 // We check for APIs set that only exist in a Chrome App context. | |
| 1844 if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { | |
| 1845 return false; | |
| 1846 } | |
| 1847 | |
| 1848 // Firefox OS Apps do not allow eval. This feature detection is very hacky | |
| 1849 // but even if some other platform adds support for this function this code | |
| 1850 // will continue to work. | |
| 1851 if (navigator.getDeviceStorage) { | |
| 1852 return false; | |
| 1853 } | |
| 1854 | |
| 1855 try { | |
| 1856 var f = new Function('return true;'); | |
| 1857 return f(); | |
| 1858 } catch (ex) { | |
| 1859 return false; | |
| 1860 } | |
| 1861 } | |
| 1862 | |
| 1863 var hasEval = detectEval(); | |
| 1864 | |
| 1865 function assert(b) { | |
| 1866 if (!b) | |
| 1867 throw new Error('Assertion failed'); | |
| 1868 }; | |
| 1869 | |
| 1870 var defineProperty = Object.defineProperty; | |
| 1871 var getOwnPropertyNames = Object.getOwnPropertyNames; | |
| 1872 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | |
| 1873 | |
| 1874 function mixin(to, from) { | |
| 1875 var names = getOwnPropertyNames(from); | |
| 1876 for (var i = 0; i < names.length; i++) { | |
| 1877 var name = names[i]; | |
| 1878 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
| 1879 } | |
| 1880 return to; | |
| 1881 }; | |
| 1882 | |
| 1883 function mixinStatics(to, from) { | |
| 1884 var names = getOwnPropertyNames(from); | |
| 1885 for (var i = 0; i < names.length; i++) { | |
| 1886 var name = names[i]; | |
| 1887 switch (name) { | |
| 1888 case 'arguments': | |
| 1889 case 'caller': | |
| 1890 case 'length': | |
| 1891 case 'name': | |
| 1892 case 'prototype': | |
| 1893 case 'toString': | |
| 1894 continue; | |
| 1895 } | |
| 1896 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
| 1897 } | |
| 1898 return to; | |
| 1899 }; | |
| 1900 | |
| 1901 function oneOf(object, propertyNames) { | |
| 1902 for (var i = 0; i < propertyNames.length; i++) { | |
| 1903 if (propertyNames[i] in object) | |
| 1904 return propertyNames[i]; | |
| 1905 } | |
| 1906 } | |
| 1907 | |
| 1908 var nonEnumerableDataDescriptor = { | |
| 1909 value: undefined, | |
| 1910 configurable: true, | |
| 1911 enumerable: false, | |
| 1912 writable: true | |
| 1913 }; | |
| 1914 | |
| 1915 function defineNonEnumerableDataProperty(object, name, value) { | |
| 1916 nonEnumerableDataDescriptor.value = value; | |
| 1917 defineProperty(object, name, nonEnumerableDataDescriptor); | |
| 1918 } | |
| 1919 | |
| 1920 // Mozilla's old DOM bindings are bretty busted: | |
| 1921 // https://bugzilla.mozilla.org/show_bug.cgi?id=855844 | |
| 1922 // Make sure they are create before we start modifying things. | |
| 1923 getOwnPropertyNames(window); | |
| 1924 | |
| 1925 function getWrapperConstructor(node) { | |
| 1926 var nativePrototype = node.__proto__ || Object.getPrototypeOf(node); | |
| 1927 var wrapperConstructor = constructorTable.get(nativePrototype); | |
| 1928 if (wrapperConstructor) | |
| 1929 return wrapperConstructor; | |
| 1930 | |
| 1931 var parentWrapperConstructor = getWrapperConstructor(nativePrototype); | |
| 1932 | |
| 1933 var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor); | |
| 1934 registerInternal(nativePrototype, GeneratedWrapper, node); | |
| 1935 | |
| 1936 return GeneratedWrapper; | |
| 1937 } | |
| 1938 | |
| 1939 function addForwardingProperties(nativePrototype, wrapperPrototype) { | |
| 1940 installProperty(nativePrototype, wrapperPrototype, true); | |
| 1941 } | |
| 1942 | |
| 1943 function registerInstanceProperties(wrapperPrototype, instanceObject) { | |
| 1944 installProperty(instanceObject, wrapperPrototype, false); | |
| 1945 } | |
| 1946 | |
| 1947 var isFirefox = /Firefox/.test(navigator.userAgent); | |
| 1948 | |
| 1949 // This is used as a fallback when getting the descriptor fails in | |
| 1950 // installProperty. | |
| 1951 var dummyDescriptor = { | |
| 1952 get: function() {}, | |
| 1953 set: function(v) {}, | |
| 1954 configurable: true, | |
| 1955 enumerable: true | |
| 1956 }; | |
| 1957 | |
| 1958 function isEventHandlerName(name) { | |
| 1959 return /^on[a-z]+$/.test(name); | |
| 1960 } | |
| 1961 | |
| 1962 function isIdentifierName(name) { | |
| 1963 return /^\w[a-zA-Z_0-9]*$/.test(name); | |
| 1964 } | |
| 1965 | |
| 1966 // The name of the implementation property is intentionally hard to | |
| 1967 // remember. Unfortunately, browsers are slower doing obj[expr] than | |
| 1968 // obj.foo so we resort to repeat this ugly name. This ugly name is never | |
| 1969 // used outside of this file though. | |
| 1970 | |
| 1971 function getGetter(name) { | |
| 1972 return hasEval && isIdentifierName(name) ? | |
| 1973 new Function('return this.__impl4cf1e782hg__.' + name) : | |
| 1974 function() { return this.__impl4cf1e782hg__[name]; }; | |
| 1975 } | |
| 1976 | |
| 1977 function getSetter(name) { | |
| 1978 return hasEval && isIdentifierName(name) ? | |
| 1979 new Function('v', 'this.__impl4cf1e782hg__.' + name + ' = v') : | |
| 1980 function(v) { this.__impl4cf1e782hg__[name] = v; }; | |
| 1981 } | |
| 1982 | |
| 1983 function getMethod(name) { | |
| 1984 return hasEval && isIdentifierName(name) ? | |
| 1985 new Function('return this.__impl4cf1e782hg__.' + name + | |
| 1986 '.apply(this.__impl4cf1e782hg__, arguments)') : | |
| 1987 function() { | |
| 1988 return this.__impl4cf1e782hg__[name].apply( | |
| 1989 this.__impl4cf1e782hg__, arguments); | |
| 1990 }; | |
| 1991 } | |
| 1992 | |
| 1993 function getDescriptor(source, name) { | |
| 1994 try { | |
| 1995 return Object.getOwnPropertyDescriptor(source, name); | |
| 1996 } catch (ex) { | |
| 1997 // JSC and V8 both use data properties instead of accessors which can | |
| 1998 // cause getting the property desciptor to throw an exception. | |
| 1999 // https://bugs.webkit.org/show_bug.cgi?id=49739 | |
| 2000 return dummyDescriptor; | |
| 2001 } | |
| 2002 } | |
| 2003 | |
| 2004 // Safari 8 exposes WebIDL attributes as an invalid accessor property. Its | |
| 2005 // descriptor has {get: undefined, set: undefined}. We therefore ignore the | |
| 2006 // shape of the descriptor and make all properties read-write. | |
| 2007 // https://bugs.webkit.org/show_bug.cgi?id=49739 | |
| 2008 var isBrokenSafari = function() { | |
| 2009 var descr = Object.getOwnPropertyDescriptor(Node.prototype, 'nodeType'); | |
| 2010 return !!descr && 'set' in descr; | |
| 2011 }(); | |
| 2012 | |
| 2013 function installProperty(source, target, allowMethod, opt_blacklist) { | |
| 2014 var names = getOwnPropertyNames(source); | |
| 2015 for (var i = 0; i < names.length; i++) { | |
| 2016 var name = names[i]; | |
| 2017 if (name === 'polymerBlackList_') | |
| 2018 continue; | |
| 2019 | |
| 2020 if (name in target) | |
| 2021 continue; | |
| 2022 | |
| 2023 if (source.polymerBlackList_ && source.polymerBlackList_[name]) | |
| 2024 continue; | |
| 2025 | |
| 2026 if (isFirefox) { | |
| 2027 // Tickle Firefox's old bindings. | |
| 2028 source.__lookupGetter__(name); | |
| 2029 } | |
| 2030 var descriptor = getDescriptor(source, name); | |
| 2031 var getter, setter; | |
| 2032 if (allowMethod && typeof descriptor.value === 'function') { | |
| 2033 target[name] = getMethod(name); | |
| 2034 continue; | |
| 2035 } | |
| 2036 | |
| 2037 var isEvent = isEventHandlerName(name); | |
| 2038 if (isEvent) | |
| 2039 getter = scope.getEventHandlerGetter(name); | |
| 2040 else | |
| 2041 getter = getGetter(name); | |
| 2042 | |
| 2043 if (descriptor.writable || descriptor.set || isBrokenSafari) { | |
| 2044 if (isEvent) | |
| 2045 setter = scope.getEventHandlerSetter(name); | |
| 2046 else | |
| 2047 setter = getSetter(name); | |
| 2048 } | |
| 2049 | |
| 2050 defineProperty(target, name, { | |
| 2051 get: getter, | |
| 2052 set: setter, | |
| 2053 configurable: descriptor.configurable, | |
| 2054 enumerable: descriptor.enumerable | |
| 2055 }); | |
| 2056 } | |
| 2057 } | |
| 2058 | |
| 2059 /** | |
| 2060 * @param {Function} nativeConstructor | |
| 2061 * @param {Function} wrapperConstructor | |
| 2062 * @param {Object=} opt_instance If present, this is used to extract | |
| 2063 * properties from an instance object. | |
| 2064 */ | |
| 2065 function register(nativeConstructor, wrapperConstructor, opt_instance) { | |
| 2066 var nativePrototype = nativeConstructor.prototype; | |
| 2067 registerInternal(nativePrototype, wrapperConstructor, opt_instance); | |
| 2068 mixinStatics(wrapperConstructor, nativeConstructor); | |
| 2069 } | |
| 2070 | |
| 2071 function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { | |
| 2072 var wrapperPrototype = wrapperConstructor.prototype; | |
| 2073 assert(constructorTable.get(nativePrototype) === undefined); | |
| 2074 | |
| 2075 constructorTable.set(nativePrototype, wrapperConstructor); | |
| 2076 nativePrototypeTable.set(wrapperPrototype, nativePrototype); | |
| 2077 | |
| 2078 addForwardingProperties(nativePrototype, wrapperPrototype); | |
| 2079 if (opt_instance) | |
| 2080 registerInstanceProperties(wrapperPrototype, opt_instance); | |
| 2081 | |
| 2082 defineNonEnumerableDataProperty( | |
| 2083 wrapperPrototype, 'constructor', wrapperConstructor); | |
| 2084 // Set it again. Some VMs optimizes objects that are used as prototypes. | |
| 2085 wrapperConstructor.prototype = wrapperPrototype; | |
| 2086 } | |
| 2087 | |
| 2088 function isWrapperFor(wrapperConstructor, nativeConstructor) { | |
| 2089 return constructorTable.get(nativeConstructor.prototype) === | |
| 2090 wrapperConstructor; | |
| 2091 } | |
| 2092 | |
| 2093 /** | |
| 2094 * Creates a generic wrapper constructor based on |object| and its | |
| 2095 * constructor. | |
| 2096 * @param {Node} object | |
| 2097 * @return {Function} The generated constructor. | |
| 2098 */ | |
| 2099 function registerObject(object) { | |
| 2100 var nativePrototype = Object.getPrototypeOf(object); | |
| 2101 | |
| 2102 var superWrapperConstructor = getWrapperConstructor(nativePrototype); | |
| 2103 var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor); | |
| 2104 registerInternal(nativePrototype, GeneratedWrapper, object); | |
| 2105 | |
| 2106 return GeneratedWrapper; | |
| 2107 } | |
| 2108 | |
| 2109 function createWrapperConstructor(superWrapperConstructor) { | |
| 2110 function GeneratedWrapper(node) { | |
| 2111 superWrapperConstructor.call(this, node); | |
| 2112 } | |
| 2113 var p = Object.create(superWrapperConstructor.prototype); | |
| 2114 p.constructor = GeneratedWrapper; | |
| 2115 GeneratedWrapper.prototype = p; | |
| 2116 | |
| 2117 return GeneratedWrapper; | |
| 2118 } | |
| 2119 | |
| 2120 function isWrapper(object) { | |
| 2121 return object && object.__impl4cf1e782hg__; | |
| 2122 } | |
| 2123 | |
| 2124 function isNative(object) { | |
| 2125 return !isWrapper(object); | |
| 2126 } | |
| 2127 | |
| 2128 /** | |
| 2129 * Wraps a node in a WrapperNode. If there already exists a wrapper for the | |
| 2130 * |node| that wrapper is returned instead. | |
| 2131 * @param {Node} node | |
| 2132 * @return {WrapperNode} | |
| 2133 */ | |
| 2134 function wrap(impl) { | |
| 2135 if (impl === null) | |
| 2136 return null; | |
| 2137 | |
| 2138 assert(isNative(impl)); | |
| 2139 return impl.__wrapper8e3dd93a60__ || | |
| 2140 (impl.__wrapper8e3dd93a60__ = new (getWrapperConstructor(impl))(impl)); | |
| 2141 } | |
| 2142 | |
| 2143 /** | |
| 2144 * Unwraps a wrapper and returns the node it is wrapping. | |
| 2145 * @param {WrapperNode} wrapper | |
| 2146 * @return {Node} | |
| 2147 */ | |
| 2148 function unwrap(wrapper) { | |
| 2149 if (wrapper === null) | |
| 2150 return null; | |
| 2151 assert(isWrapper(wrapper)); | |
| 2152 return wrapper.__impl4cf1e782hg__; | |
| 2153 } | |
| 2154 | |
| 2155 function unsafeUnwrap(wrapper) { | |
| 2156 return wrapper.__impl4cf1e782hg__; | |
| 2157 } | |
| 2158 | |
| 2159 function setWrapper(impl, wrapper) { | |
| 2160 wrapper.__impl4cf1e782hg__ = impl; | |
| 2161 impl.__wrapper8e3dd93a60__ = wrapper; | |
| 2162 } | |
| 2163 | |
| 2164 /** | |
| 2165 * Unwraps object if it is a wrapper. | |
| 2166 * @param {Object} object | |
| 2167 * @return {Object} The native implementation object. | |
| 2168 */ | |
| 2169 function unwrapIfNeeded(object) { | |
| 2170 return object && isWrapper(object) ? unwrap(object) : object; | |
| 2171 } | |
| 2172 | |
| 2173 /** | |
| 2174 * Wraps object if it is not a wrapper. | |
| 2175 * @param {Object} object | |
| 2176 * @return {Object} The wrapper for object. | |
| 2177 */ | |
| 2178 function wrapIfNeeded(object) { | |
| 2179 return object && !isWrapper(object) ? wrap(object) : object; | |
| 2180 } | |
| 2181 | |
| 2182 /** | |
| 2183 * Overrides the current wrapper (if any) for node. | |
| 2184 * @param {Node} node | |
| 2185 * @param {WrapperNode=} wrapper If left out the wrapper will be created as | |
| 2186 * needed next time someone wraps the node. | |
| 2187 */ | |
| 2188 function rewrap(node, wrapper) { | |
| 2189 if (wrapper === null) | |
| 2190 return; | |
| 2191 assert(isNative(node)); | |
| 2192 assert(wrapper === undefined || isWrapper(wrapper)); | |
| 2193 node.__wrapper8e3dd93a60__ = wrapper; | |
| 2194 } | |
| 2195 | |
| 2196 var getterDescriptor = { | |
| 2197 get: undefined, | |
| 2198 configurable: true, | |
| 2199 enumerable: true | |
| 2200 }; | |
| 2201 | |
| 2202 function defineGetter(constructor, name, getter) { | |
| 2203 getterDescriptor.get = getter; | |
| 2204 defineProperty(constructor.prototype, name, getterDescriptor); | |
| 2205 } | |
| 2206 | |
| 2207 function defineWrapGetter(constructor, name) { | |
| 2208 defineGetter(constructor, name, function() { | |
| 2209 return wrap(this.__impl4cf1e782hg__[name]); | |
| 2210 }); | |
| 2211 } | |
| 2212 | |
| 2213 /** | |
| 2214 * Forwards existing methods on the native object to the wrapper methods. | |
| 2215 * This does not wrap any of the arguments or the return value since the | |
| 2216 * wrapper implementation already takes care of that. | |
| 2217 * @param {Array.<Function>} constructors | |
| 2218 * @parem {Array.<string>} names | |
| 2219 */ | |
| 2220 function forwardMethodsToWrapper(constructors, names) { | |
| 2221 constructors.forEach(function(constructor) { | |
| 2222 names.forEach(function(name) { | |
| 2223 constructor.prototype[name] = function() { | |
| 2224 var w = wrapIfNeeded(this); | |
| 2225 return w[name].apply(w, arguments); | |
| 2226 }; | |
| 2227 }); | |
| 2228 }); | |
| 2229 } | |
| 2230 | |
| 2231 scope.assert = assert; | |
| 2232 scope.constructorTable = constructorTable; | |
| 2233 scope.defineGetter = defineGetter; | |
| 2234 scope.defineWrapGetter = defineWrapGetter; | |
| 2235 scope.forwardMethodsToWrapper = forwardMethodsToWrapper; | |
| 2236 scope.isWrapper = isWrapper; | |
| 2237 scope.isWrapperFor = isWrapperFor; | |
| 2238 scope.mixin = mixin; | |
| 2239 scope.nativePrototypeTable = nativePrototypeTable; | |
| 2240 scope.oneOf = oneOf; | |
| 2241 scope.registerObject = registerObject; | |
| 2242 scope.registerWrapper = register; | |
| 2243 scope.rewrap = rewrap; | |
| 2244 scope.setWrapper = setWrapper; | |
| 2245 scope.unsafeUnwrap = unsafeUnwrap; | |
| 2246 scope.unwrap = unwrap; | |
| 2247 scope.unwrapIfNeeded = unwrapIfNeeded; | |
| 2248 scope.wrap = wrap; | |
| 2249 scope.wrapIfNeeded = wrapIfNeeded; | |
| 2250 scope.wrappers = wrappers; | |
| 2251 | |
| 2252 })(window.ShadowDOMPolyfill); | |
| 2253 | |
| 2254 /* | |
| 2255 * Copyright 2013 The Polymer Authors. All rights reserved. | |
| 2256 * Use of this source code is goverened by a BSD-style | |
| 2257 * license that can be found in the LICENSE file. | |
| 2258 */ | |
| 2259 | |
| 2260 (function(context) { | |
| 2261 'use strict'; | |
| 2262 | |
| 2263 var OriginalMutationObserver = window.MutationObserver; | |
| 2264 var callbacks = []; | |
| 2265 var pending = false; | |
| 2266 var timerFunc; | |
| 2267 | |
| 2268 function handle() { | |
| 2269 pending = false; | |
| 2270 var copies = callbacks.slice(0); | |
| 2271 callbacks = []; | |
| 2272 for (var i = 0; i < copies.length; i++) { | |
| 2273 (0, copies[i])(); | |
| 2274 } | |
| 2275 } | |
| 2276 | |
| 2277 if (OriginalMutationObserver) { | |
| 2278 var counter = 1; | |
| 2279 var observer = new OriginalMutationObserver(handle); | |
| 2280 var textNode = document.createTextNode(counter); | |
| 2281 observer.observe(textNode, {characterData: true}); | |
| 2282 | |
| 2283 timerFunc = function() { | |
| 2284 counter = (counter + 1) % 2; | |
| 2285 textNode.data = counter; | |
| 2286 }; | |
| 2287 | |
| 2288 } else { | |
| 2289 timerFunc = window.setImmediate || window.setTimeout; | |
| 2290 } | |
| 2291 | |
| 2292 function setEndOfMicrotask(func) { | |
| 2293 callbacks.push(func); | |
| 2294 if (pending) | |
| 2295 return; | |
| 2296 pending = true; | |
| 2297 timerFunc(handle, 0); | |
| 2298 } | |
| 2299 | |
| 2300 context.setEndOfMicrotask = setEndOfMicrotask; | |
| 2301 | |
| 2302 })(window.ShadowDOMPolyfill); | |
| 2303 | |
| 2304 /* | |
| 2305 * Copyright 2013 The Polymer Authors. All rights reserved. | |
| 2306 * Use of this source code is goverened by a BSD-style | |
| 2307 * license that can be found in the LICENSE file. | |
| 2308 */ | |
| 2309 | |
| 2310 (function(scope) { | |
| 2311 'use strict'; | |
| 2312 | |
| 2313 var setEndOfMicrotask = scope.setEndOfMicrotask | |
| 2314 var wrapIfNeeded = scope.wrapIfNeeded | |
| 2315 var wrappers = scope.wrappers; | |
| 2316 | |
| 2317 var registrationsTable = new WeakMap(); | |
| 2318 var globalMutationObservers = []; | |
| 2319 var isScheduled = false; | |
| 2320 | |
| 2321 function scheduleCallback(observer) { | |
| 2322 if (observer.scheduled_) | |
| 2323 return; | |
| 2324 | |
| 2325 observer.scheduled_ = true; | |
| 2326 globalMutationObservers.push(observer); | |
| 2327 | |
| 2328 if (isScheduled) | |
| 2329 return; | |
| 2330 setEndOfMicrotask(notifyObservers); | |
| 2331 isScheduled = true; | |
| 2332 } | |
| 2333 | |
| 2334 // http://dom.spec.whatwg.org/#mutation-observers | |
| 2335 function notifyObservers() { | |
| 2336 isScheduled = false; | |
| 2337 | |
| 2338 while (globalMutationObservers.length) { | |
| 2339 var notifyList = globalMutationObservers; | |
| 2340 globalMutationObservers = []; | |
| 2341 | |
| 2342 // Deliver changes in birth order of the MutationObservers. | |
| 2343 notifyList.sort(function(x, y) { return x.uid_ - y.uid_; }); | |
| 2344 | |
| 2345 for (var i = 0; i < notifyList.length; i++) { | |
| 2346 var mo = notifyList[i]; | |
| 2347 mo.scheduled_ = false; | |
| 2348 var queue = mo.takeRecords(); | |
| 2349 removeTransientObserversFor(mo); | |
| 2350 if (queue.length) { | |
| 2351 mo.callback_(queue, mo); | |
| 2352 } | |
| 2353 } | |
| 2354 } | |
| 2355 } | |
| 2356 | |
| 2357 | |
| 2358 /** | |
| 2359 * @param {string} type | |
| 2360 * @param {Node} target | |
| 2361 * @constructor | |
| 2362 */ | |
| 2363 function MutationRecord(type, target) { | |
| 2364 this.type = type; | |
| 2365 this.target = target; | |
| 2366 this.addedNodes = new wrappers.NodeList(); | |
| 2367 this.removedNodes = new wrappers.NodeList(); | |
| 2368 this.previousSibling = null; | |
| 2369 this.nextSibling = null; | |
| 2370 this.attributeName = null; | |
| 2371 this.attributeNamespace = null; | |
| 2372 this.oldValue = null; | |
| 2373 } | |
| 2374 | |
| 2375 /** | |
| 2376 * Registers transient observers to ancestor and its ancesors for the node | |
| 2377 * which was removed. | |
| 2378 * @param {!Node} ancestor | |
| 2379 * @param {!Node} node | |
| 2380 */ | |
| 2381 function registerTransientObservers(ancestor, node) { | |
| 2382 for (; ancestor; ancestor = ancestor.parentNode) { | |
| 2383 var registrations = registrationsTable.get(ancestor); | |
| 2384 if (!registrations) | |
| 2385 continue; | |
| 2386 for (var i = 0; i < registrations.length; i++) { | |
| 2387 var registration = registrations[i]; | |
| 2388 if (registration.options.subtree) | |
| 2389 registration.addTransientObserver(node); | |
| 2390 } | |
| 2391 } | |
| 2392 } | |
| 2393 | |
| 2394 function removeTransientObserversFor(observer) { | |
| 2395 for (var i = 0; i < observer.nodes_.length; i++) { | |
| 2396 var node = observer.nodes_[i]; | |
| 2397 var registrations = registrationsTable.get(node); | |
| 2398 if (!registrations) | |
| 2399 return; | |
| 2400 for (var j = 0; j < registrations.length; j++) { | |
| 2401 var registration = registrations[j]; | |
| 2402 if (registration.observer === observer) | |
| 2403 registration.removeTransientObservers(); | |
| 2404 } | |
| 2405 } | |
| 2406 } | |
| 2407 | |
| 2408 // http://dom.spec.whatwg.org/#queue-a-mutation-record | |
| 2409 function enqueueMutation(target, type, data) { | |
| 2410 // 1. | |
| 2411 var interestedObservers = Object.create(null); | |
| 2412 var associatedStrings = Object.create(null); | |
| 2413 | |
| 2414 // 2. | |
| 2415 for (var node = target; node; node = node.parentNode) { | |
| 2416 // 3. | |
| 2417 var registrations = registrationsTable.get(node); | |
| 2418 if (!registrations) | |
| 2419 continue; | |
| 2420 for (var j = 0; j < registrations.length; j++) { | |
| 2421 var registration = registrations[j]; | |
| 2422 var options = registration.options; | |
| 2423 // 1. | |
| 2424 if (node !== target && !options.subtree) | |
| 2425 continue; | |
| 2426 | |
| 2427 // 2. | |
| 2428 if (type === 'attributes' && !options.attributes) | |
| 2429 continue; | |
| 2430 | |
| 2431 // 3. If type is "attributes", options's attributeFilter is present, and | |
| 2432 // either options's attributeFilter does not contain name or namespace | |
| 2433 // is non-null, continue. | |
| 2434 if (type === 'attributes' && options.attributeFilter && | |
| 2435 (data.namespace !== null || | |
| 2436 options.attributeFilter.indexOf(data.name) === -1)) { | |
| 2437 continue; | |
| 2438 } | |
| 2439 | |
| 2440 // 4. | |
| 2441 if (type === 'characterData' && !options.characterData) | |
| 2442 continue; | |
| 2443 | |
| 2444 // 5. | |
| 2445 if (type === 'childList' && !options.childList) | |
| 2446 continue; | |
| 2447 | |
| 2448 // 6. | |
| 2449 var observer = registration.observer; | |
| 2450 interestedObservers[observer.uid_] = observer; | |
| 2451 | |
| 2452 // 7. If either type is "attributes" and options's attributeOldValue is | |
| 2453 // true, or type is "characterData" and options's characterDataOldValue | |
| 2454 // is true, set the paired string of registered observer's observer in | |
| 2455 // interested observers to oldValue. | |
| 2456 if (type === 'attributes' && options.attributeOldValue || | |
| 2457 type === 'characterData' && options.characterDataOldValue) { | |
| 2458 associatedStrings[observer.uid_] = data.oldValue; | |
| 2459 } | |
| 2460 } | |
| 2461 } | |
| 2462 | |
| 2463 // 4. | |
| 2464 for (var uid in interestedObservers) { | |
| 2465 var observer = interestedObservers[uid]; | |
| 2466 var record = new MutationRecord(type, target); | |
| 2467 | |
| 2468 // 2. | |
| 2469 if ('name' in data && 'namespace' in data) { | |
| 2470 record.attributeName = data.name; | |
| 2471 record.attributeNamespace = data.namespace; | |
| 2472 } | |
| 2473 | |
| 2474 // 3. | |
| 2475 if (data.addedNodes) | |
| 2476 record.addedNodes = data.addedNodes; | |
| 2477 | |
| 2478 // 4. | |
| 2479 if (data.removedNodes) | |
| 2480 record.removedNodes = data.removedNodes; | |
| 2481 | |
| 2482 // 5. | |
| 2483 if (data.previousSibling) | |
| 2484 record.previousSibling = data.previousSibling; | |
| 2485 | |
| 2486 // 6. | |
| 2487 if (data.nextSibling) | |
| 2488 record.nextSibling = data.nextSibling; | |
| 2489 | |
| 2490 // 7. | |
| 2491 if (associatedStrings[uid] !== undefined) | |
| 2492 record.oldValue = associatedStrings[uid]; | |
| 2493 | |
| 2494 // 8. | |
| 2495 scheduleCallback(observer); | |
| 2496 observer.records_.push(record); | |
| 2497 } | |
| 2498 } | |
| 2499 | |
| 2500 var slice = Array.prototype.slice; | |
| 2501 | |
| 2502 /** | |
| 2503 * @param {!Object} options | |
| 2504 * @constructor | |
| 2505 */ | |
| 2506 function MutationObserverOptions(options) { | |
| 2507 this.childList = !!options.childList; | |
| 2508 this.subtree = !!options.subtree; | |
| 2509 | |
| 2510 // 1. If either options' attributeOldValue or attributeFilter is present | |
| 2511 // and options' attributes is omitted, set options' attributes to true. | |
| 2512 if (!('attributes' in options) && | |
| 2513 ('attributeOldValue' in options || 'attributeFilter' in options)) { | |
| 2514 this.attributes = true; | |
| 2515 } else { | |
| 2516 this.attributes = !!options.attributes; | |
| 2517 } | |
| 2518 | |
| 2519 // 2. If options' characterDataOldValue is present and options' | |
| 2520 // characterData is omitted, set options' characterData to true. | |
| 2521 if ('characterDataOldValue' in options && !('characterData' in options)) | |
| 2522 this.characterData = true; | |
| 2523 else | |
| 2524 this.characterData = !!options.characterData; | |
| 2525 | |
| 2526 // 3. & 4. | |
| 2527 if (!this.attributes && | |
| 2528 (options.attributeOldValue || 'attributeFilter' in options) || | |
| 2529 // 5. | |
| 2530 !this.characterData && options.characterDataOldValue) { | |
| 2531 throw new TypeError(); | |
| 2532 } | |
| 2533 | |
| 2534 this.characterData = !!options.characterData; | |
| 2535 this.attributeOldValue = !!options.attributeOldValue; | |
| 2536 this.characterDataOldValue = !!options.characterDataOldValue; | |
| 2537 if ('attributeFilter' in options) { | |
| 2538 if (options.attributeFilter == null || | |
| 2539 typeof options.attributeFilter !== 'object') { | |
| 2540 throw new TypeError(); | |
| 2541 } | |
| 2542 this.attributeFilter = slice.call(options.attributeFilter); | |
| 2543 } else { | |
| 2544 this.attributeFilter = null; | |
| 2545 } | |
| 2546 } | |
| 2547 | |
| 2548 var uidCounter = 0; | |
| 2549 | |
| 2550 /** | |
| 2551 * The class that maps to the DOM MutationObserver interface. | |
| 2552 * @param {Function} callback. | |
| 2553 * @constructor | |
| 2554 */ | |
| 2555 function MutationObserver(callback) { | |
| 2556 this.callback_ = callback; | |
| 2557 this.nodes_ = []; | |
| 2558 this.records_ = []; | |
| 2559 this.uid_ = ++uidCounter; | |
| 2560 this.scheduled_ = false; | |
| 2561 } | |
| 2562 | |
| 2563 MutationObserver.prototype = { | |
| 2564 constructor: MutationObserver, | |
| 2565 | |
| 2566 // http://dom.spec.whatwg.org/#dom-mutationobserver-observe | |
| 2567 observe: function(target, options) { | |
| 2568 target = wrapIfNeeded(target); | |
| 2569 | |
| 2570 var newOptions = new MutationObserverOptions(options); | |
| 2571 | |
| 2572 // 6. | |
| 2573 var registration; | |
| 2574 var registrations = registrationsTable.get(target); | |
| 2575 if (!registrations) | |
| 2576 registrationsTable.set(target, registrations = []); | |
| 2577 | |
| 2578 for (var i = 0; i < registrations.length; i++) { | |
| 2579 if (registrations[i].observer === this) { | |
| 2580 registration = registrations[i]; | |
| 2581 // 6.1. | |
| 2582 registration.removeTransientObservers(); | |
| 2583 // 6.2. | |
| 2584 registration.options = newOptions; | |
| 2585 } | |
| 2586 } | |
| 2587 | |
| 2588 // 7. | |
| 2589 if (!registration) { | |
| 2590 registration = new Registration(this, target, newOptions); | |
| 2591 registrations.push(registration); | |
| 2592 this.nodes_.push(target); | |
| 2593 } | |
| 2594 }, | |
| 2595 | |
| 2596 // http://dom.spec.whatwg.org/#dom-mutationobserver-disconnect | |
| 2597 disconnect: function() { | |
| 2598 this.nodes_.forEach(function(node) { | |
| 2599 var registrations = registrationsTable.get(node); | |
| 2600 for (var i = 0; i < registrations.length; i++) { | |
| 2601 var registration = registrations[i]; | |
| 2602 if (registration.observer === this) { | |
| 2603 registrations.splice(i, 1); | |
| 2604 // Each node can only have one registered observer associated with | |
| 2605 // this observer. | |
| 2606 break; | |
| 2607 } | |
| 2608 } | |
| 2609 }, this); | |
| 2610 this.records_ = []; | |
| 2611 }, | |
| 2612 | |
| 2613 takeRecords: function() { | |
| 2614 var copyOfRecords = this.records_; | |
| 2615 this.records_ = []; | |
| 2616 return copyOfRecords; | |
| 2617 } | |
| 2618 }; | |
| 2619 | |
| 2620 /** | |
| 2621 * Class used to represent a registered observer. | |
| 2622 * @param {MutationObserver} observer | |
| 2623 * @param {Node} target | |
| 2624 * @param {MutationObserverOptions} options | |
| 2625 * @constructor | |
| 2626 */ | |
| 2627 function Registration(observer, target, options) { | |
| 2628 this.observer = observer; | |
| 2629 this.target = target; | |
| 2630 this.options = options; | |
| 2631 this.transientObservedNodes = []; | |
| 2632 } | |
| 2633 | |
| 2634 Registration.prototype = { | |
| 2635 /** | |
| 2636 * Adds a transient observer on node. The transient observer gets removed | |
| 2637 * next time we deliver the change records. | |
| 2638 * @param {Node} node | |
| 2639 */ | |
| 2640 addTransientObserver: function(node) { | |
| 2641 // Don't add transient observers on the target itself. We already have all | |
| 2642 // the required listeners set up on the target. | |
| 2643 if (node === this.target) | |
| 2644 return; | |
| 2645 | |
| 2646 // Make sure we remove transient observers at the end of microtask, even | |
| 2647 // if we didn't get any change records. | |
| 2648 scheduleCallback(this.observer); | |
| 2649 | |
| 2650 this.transientObservedNodes.push(node); | |
| 2651 var registrations = registrationsTable.get(node); | |
| 2652 if (!registrations) | |
| 2653 registrationsTable.set(node, registrations = []); | |
| 2654 | |
| 2655 // We know that registrations does not contain this because we already | |
| 2656 // checked if node === this.target. | |
| 2657 registrations.push(this); | |
| 2658 }, | |
| 2659 | |
| 2660 removeTransientObservers: function() { | |
| 2661 var transientObservedNodes = this.transientObservedNodes; | |
| 2662 this.transientObservedNodes = []; | |
| 2663 | |
| 2664 for (var i = 0; i < transientObservedNodes.length; i++) { | |
| 2665 var node = transientObservedNodes[i]; | |
| 2666 var registrations = registrationsTable.get(node); | |
| 2667 for (var j = 0; j < registrations.length; j++) { | |
| 2668 if (registrations[j] === this) { | |
| 2669 registrations.splice(j, 1); | |
| 2670 // Each node can only have one registered observer associated with | |
| 2671 // this observer. | |
| 2672 break; | |
| 2673 } | |
| 2674 } | |
| 2675 } | |
| 2676 } | |
| 2677 }; | |
| 2678 | |
| 2679 scope.enqueueMutation = enqueueMutation; | |
| 2680 scope.registerTransientObservers = registerTransientObservers; | |
| 2681 scope.wrappers.MutationObserver = MutationObserver; | |
| 2682 scope.wrappers.MutationRecord = MutationRecord; | |
| 2683 | |
| 2684 })(window.ShadowDOMPolyfill); | |
| 2685 | |
| 2686 /** | |
| 2687 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 2688 * Use of this source code is goverened by a BSD-style | |
| 2689 * license that can be found in the LICENSE file. | |
| 2690 */ | |
| 2691 | |
| 2692 (function(scope) { | |
| 2693 'use strict'; | |
| 2694 | |
| 2695 /** | |
| 2696 * A tree scope represents the root of a tree. All nodes in a tree point to | |
| 2697 * the same TreeScope object. The tree scope of a node get set the first time | |
| 2698 * it is accessed or when a node is added or remove to a tree. | |
| 2699 * | |
| 2700 * The root is a Node that has no parent. | |
| 2701 * | |
| 2702 * The parent is another TreeScope. For ShadowRoots, it is the TreeScope of | |
| 2703 * the host of the ShadowRoot. | |
| 2704 * | |
| 2705 * @param {!Node} root | |
| 2706 * @param {TreeScope} parent | |
| 2707 * @constructor | |
| 2708 */ | |
| 2709 function TreeScope(root, parent) { | |
| 2710 /** @type {!Node} */ | |
| 2711 this.root = root; | |
| 2712 | |
| 2713 /** @type {TreeScope} */ | |
| 2714 this.parent = parent; | |
| 2715 } | |
| 2716 | |
| 2717 TreeScope.prototype = { | |
| 2718 get renderer() { | |
| 2719 if (this.root instanceof scope.wrappers.ShadowRoot) { | |
| 2720 return scope.getRendererForHost(this.root.host); | |
| 2721 } | |
| 2722 return null; | |
| 2723 }, | |
| 2724 | |
| 2725 contains: function(treeScope) { | |
| 2726 for (; treeScope; treeScope = treeScope.parent) { | |
| 2727 if (treeScope === this) | |
| 2728 return true; | |
| 2729 } | |
| 2730 return false; | |
| 2731 } | |
| 2732 }; | |
| 2733 | |
| 2734 function setTreeScope(node, treeScope) { | |
| 2735 if (node.treeScope_ !== treeScope) { | |
| 2736 node.treeScope_ = treeScope; | |
| 2737 for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { | |
| 2738 sr.treeScope_.parent = treeScope; | |
| 2739 } | |
| 2740 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 2741 setTreeScope(child, treeScope); | |
| 2742 } | |
| 2743 } | |
| 2744 } | |
| 2745 | |
| 2746 function getTreeScope(node) { | |
| 2747 if (node instanceof scope.wrappers.Window) { | |
| 2748 debugger; | |
| 2749 } | |
| 2750 | |
| 2751 if (node.treeScope_) | |
| 2752 return node.treeScope_; | |
| 2753 var parent = node.parentNode; | |
| 2754 var treeScope; | |
| 2755 if (parent) | |
| 2756 treeScope = getTreeScope(parent); | |
| 2757 else | |
| 2758 treeScope = new TreeScope(node, null); | |
| 2759 return node.treeScope_ = treeScope; | |
| 2760 } | |
| 2761 | |
| 2762 scope.TreeScope = TreeScope; | |
| 2763 scope.getTreeScope = getTreeScope; | |
| 2764 scope.setTreeScope = setTreeScope; | |
| 2765 | |
| 2766 })(window.ShadowDOMPolyfill); | |
| 2767 | |
| 2768 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 2769 // Use of this source code is goverened by a BSD-style | |
| 2770 // license that can be found in the LICENSE file. | |
| 2771 | |
| 2772 (function(scope) { | |
| 2773 'use strict'; | |
| 2774 | |
| 2775 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
| 2776 var getTreeScope = scope.getTreeScope; | |
| 2777 var mixin = scope.mixin; | |
| 2778 var registerWrapper = scope.registerWrapper; | |
| 2779 var setWrapper = scope.setWrapper; | |
| 2780 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 2781 var unwrap = scope.unwrap; | |
| 2782 var wrap = scope.wrap; | |
| 2783 var wrappers = scope.wrappers; | |
| 2784 | |
| 2785 var wrappedFuns = new WeakMap(); | |
| 2786 var listenersTable = new WeakMap(); | |
| 2787 var handledEventsTable = new WeakMap(); | |
| 2788 var currentlyDispatchingEvents = new WeakMap(); | |
| 2789 var targetTable = new WeakMap(); | |
| 2790 var currentTargetTable = new WeakMap(); | |
| 2791 var relatedTargetTable = new WeakMap(); | |
| 2792 var eventPhaseTable = new WeakMap(); | |
| 2793 var stopPropagationTable = new WeakMap(); | |
| 2794 var stopImmediatePropagationTable = new WeakMap(); | |
| 2795 var eventHandlersTable = new WeakMap(); | |
| 2796 var eventPathTable = new WeakMap(); | |
| 2797 | |
| 2798 function isShadowRoot(node) { | |
| 2799 return node instanceof wrappers.ShadowRoot; | |
| 2800 } | |
| 2801 | |
| 2802 function rootOfNode(node) { | |
| 2803 return getTreeScope(node).root; | |
| 2804 } | |
| 2805 | |
| 2806 // http://w3c.github.io/webcomponents/spec/shadow/#event-paths | |
| 2807 function getEventPath(node, event) { | |
| 2808 var path = []; | |
| 2809 var current = node; | |
| 2810 path.push(current); | |
| 2811 while (current) { | |
| 2812 // 4.1. | |
| 2813 var destinationInsertionPoints = getDestinationInsertionPoints(current); | |
| 2814 if (destinationInsertionPoints && destinationInsertionPoints.length > 0) { | |
| 2815 // 4.1.1 | |
| 2816 for (var i = 0; i < destinationInsertionPoints.length; i++) { | |
| 2817 var insertionPoint = destinationInsertionPoints[i]; | |
| 2818 // 4.1.1.1 | |
| 2819 if (isShadowInsertionPoint(insertionPoint)) { | |
| 2820 var shadowRoot = rootOfNode(insertionPoint); | |
| 2821 // 4.1.1.1.2 | |
| 2822 var olderShadowRoot = shadowRoot.olderShadowRoot; | |
| 2823 if (olderShadowRoot) | |
| 2824 path.push(olderShadowRoot); | |
| 2825 } | |
| 2826 | |
| 2827 // 4.1.1.2 | |
| 2828 path.push(insertionPoint); | |
| 2829 } | |
| 2830 | |
| 2831 // 4.1.2 | |
| 2832 current = destinationInsertionPoints[ | |
| 2833 destinationInsertionPoints.length - 1]; | |
| 2834 | |
| 2835 // 4.2 | |
| 2836 } else { | |
| 2837 if (isShadowRoot(current)) { | |
| 2838 if (inSameTree(node, current) && eventMustBeStopped(event)) { | |
| 2839 // Stop this algorithm | |
| 2840 break; | |
| 2841 } | |
| 2842 current = current.host; | |
| 2843 path.push(current); | |
| 2844 | |
| 2845 // 4.2.2 | |
| 2846 } else { | |
| 2847 current = current.parentNode; | |
| 2848 if (current) | |
| 2849 path.push(current); | |
| 2850 } | |
| 2851 } | |
| 2852 } | |
| 2853 | |
| 2854 return path; | |
| 2855 } | |
| 2856 | |
| 2857 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-events-always-stopped | |
| 2858 function eventMustBeStopped(event) { | |
| 2859 if (!event) | |
| 2860 return false; | |
| 2861 | |
| 2862 switch (event.type) { | |
| 2863 case 'abort': | |
| 2864 case 'error': | |
| 2865 case 'select': | |
| 2866 case 'change': | |
| 2867 case 'load': | |
| 2868 case 'reset': | |
| 2869 case 'resize': | |
| 2870 case 'scroll': | |
| 2871 case 'selectstart': | |
| 2872 return true; | |
| 2873 } | |
| 2874 return false; | |
| 2875 } | |
| 2876 | |
| 2877 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-shadow-insertion-point | |
| 2878 function isShadowInsertionPoint(node) { | |
| 2879 return node instanceof HTMLShadowElement; | |
| 2880 // and make sure that there are no shadow precing this? | |
| 2881 // and that there is no content ancestor? | |
| 2882 } | |
| 2883 | |
| 2884 function getDestinationInsertionPoints(node) { | |
| 2885 return scope.getDestinationInsertionPoints(node); | |
| 2886 } | |
| 2887 | |
| 2888 // http://w3c.github.io/webcomponents/spec/shadow/#event-retargeting | |
| 2889 function eventRetargetting(path, currentTarget) { | |
| 2890 if (path.length === 0) | |
| 2891 return currentTarget; | |
| 2892 | |
| 2893 // The currentTarget might be the window object. Use its document for the | |
| 2894 // purpose of finding the retargetted node. | |
| 2895 if (currentTarget instanceof wrappers.Window) | |
| 2896 currentTarget = currentTarget.document; | |
| 2897 | |
| 2898 var currentTargetTree = getTreeScope(currentTarget); | |
| 2899 var originalTarget = path[0]; | |
| 2900 var originalTargetTree = getTreeScope(originalTarget); | |
| 2901 var relativeTargetTree = | |
| 2902 lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree); | |
| 2903 | |
| 2904 for (var i = 0; i < path.length; i++) { | |
| 2905 var node = path[i]; | |
| 2906 if (getTreeScope(node) === relativeTargetTree) | |
| 2907 return node; | |
| 2908 } | |
| 2909 | |
| 2910 return path[path.length - 1]; | |
| 2911 } | |
| 2912 | |
| 2913 function getTreeScopeAncestors(treeScope) { | |
| 2914 var ancestors = []; | |
| 2915 for (;treeScope; treeScope = treeScope.parent) { | |
| 2916 ancestors.push(treeScope); | |
| 2917 } | |
| 2918 return ancestors; | |
| 2919 } | |
| 2920 | |
| 2921 function lowestCommonInclusiveAncestor(tsA, tsB) { | |
| 2922 var ancestorsA = getTreeScopeAncestors(tsA); | |
| 2923 var ancestorsB = getTreeScopeAncestors(tsB); | |
| 2924 | |
| 2925 var result = null; | |
| 2926 while (ancestorsA.length > 0 && ancestorsB.length > 0) { | |
| 2927 var a = ancestorsA.pop(); | |
| 2928 var b = ancestorsB.pop(); | |
| 2929 if (a === b) | |
| 2930 result = a; | |
| 2931 else | |
| 2932 break; | |
| 2933 } | |
| 2934 return result; | |
| 2935 } | |
| 2936 | |
| 2937 function getTreeScopeRoot(ts) { | |
| 2938 if (!ts.parent) | |
| 2939 return ts; | |
| 2940 return getTreeScopeRoot(ts.parent); | |
| 2941 } | |
| 2942 | |
| 2943 function relatedTargetResolution(event, currentTarget, relatedTarget) { | |
| 2944 // In case the current target is a window use its document for the purpose | |
| 2945 // of retargetting the related target. | |
| 2946 if (currentTarget instanceof wrappers.Window) | |
| 2947 currentTarget = currentTarget.document; | |
| 2948 | |
| 2949 var currentTargetTree = getTreeScope(currentTarget); | |
| 2950 var relatedTargetTree = getTreeScope(relatedTarget); | |
| 2951 | |
| 2952 var relatedTargetEventPath = getEventPath(relatedTarget, event); | |
| 2953 | |
| 2954 var lowestCommonAncestorTree; | |
| 2955 | |
| 2956 // 4 | |
| 2957 var lowestCommonAncestorTree = | |
| 2958 lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree); | |
| 2959 | |
| 2960 // 5 | |
| 2961 if (!lowestCommonAncestorTree) | |
| 2962 lowestCommonAncestorTree = relatedTargetTree.root; | |
| 2963 | |
| 2964 // 6 | |
| 2965 for (var commonAncestorTree = lowestCommonAncestorTree; | |
| 2966 commonAncestorTree; | |
| 2967 commonAncestorTree = commonAncestorTree.parent) { | |
| 2968 // 6.1 | |
| 2969 var adjustedRelatedTarget; | |
| 2970 for (var i = 0; i < relatedTargetEventPath.length; i++) { | |
| 2971 var node = relatedTargetEventPath[i]; | |
| 2972 if (getTreeScope(node) === commonAncestorTree) | |
| 2973 return node; | |
| 2974 } | |
| 2975 } | |
| 2976 | |
| 2977 return null; | |
| 2978 } | |
| 2979 | |
| 2980 function inSameTree(a, b) { | |
| 2981 return getTreeScope(a) === getTreeScope(b); | |
| 2982 } | |
| 2983 | |
| 2984 var NONE = 0; | |
| 2985 var CAPTURING_PHASE = 1; | |
| 2986 var AT_TARGET = 2; | |
| 2987 var BUBBLING_PHASE = 3; | |
| 2988 | |
| 2989 // pendingError is used to rethrow the first error we got during an event | |
| 2990 // dispatch. The browser actually reports all errors but to do that we would | |
| 2991 // need to rethrow the error asynchronously. | |
| 2992 var pendingError; | |
| 2993 | |
| 2994 function dispatchOriginalEvent(originalEvent) { | |
| 2995 // Make sure this event is only dispatched once. | |
| 2996 if (handledEventsTable.get(originalEvent)) | |
| 2997 return; | |
| 2998 handledEventsTable.set(originalEvent, true); | |
| 2999 dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); | |
| 3000 if (pendingError) { | |
| 3001 var err = pendingError; | |
| 3002 pendingError = null; | |
| 3003 throw err; | |
| 3004 } | |
| 3005 } | |
| 3006 | |
| 3007 | |
| 3008 function isLoadLikeEvent(event) { | |
| 3009 switch (event.type) { | |
| 3010 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.
html#events-and-the-window-object | |
| 3011 case 'load': | |
| 3012 // http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.ht
ml#unloading-documents | |
| 3013 case 'beforeunload': | |
| 3014 case 'unload': | |
| 3015 return true; | |
| 3016 } | |
| 3017 return false; | |
| 3018 } | |
| 3019 | |
| 3020 function dispatchEvent(event, originalWrapperTarget) { | |
| 3021 if (currentlyDispatchingEvents.get(event)) | |
| 3022 throw new Error('InvalidStateError'); | |
| 3023 | |
| 3024 currentlyDispatchingEvents.set(event, true); | |
| 3025 | |
| 3026 // Render to ensure that the event path is correct. | |
| 3027 scope.renderAllPending(); | |
| 3028 var eventPath; | |
| 3029 | |
| 3030 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.ht
ml#events-and-the-window-object | |
| 3031 // All events dispatched on Nodes with a default view, except load events, | |
| 3032 // should propagate to the Window. | |
| 3033 | |
| 3034 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#
the-end | |
| 3035 var overrideTarget; | |
| 3036 var win; | |
| 3037 | |
| 3038 // Should really be not cancelable too but since Firefox has a bug there | |
| 3039 // we skip that check. | |
| 3040 // https://bugzilla.mozilla.org/show_bug.cgi?id=999456 | |
| 3041 if (isLoadLikeEvent(event) && !event.bubbles) { | |
| 3042 var doc = originalWrapperTarget; | |
| 3043 if (doc instanceof wrappers.Document && (win = doc.defaultView)) { | |
| 3044 overrideTarget = doc; | |
| 3045 eventPath = []; | |
| 3046 } | |
| 3047 } | |
| 3048 | |
| 3049 if (!eventPath) { | |
| 3050 if (originalWrapperTarget instanceof wrappers.Window) { | |
| 3051 win = originalWrapperTarget; | |
| 3052 eventPath = []; | |
| 3053 } else { | |
| 3054 eventPath = getEventPath(originalWrapperTarget, event); | |
| 3055 | |
| 3056 if (!isLoadLikeEvent(event)) { | |
| 3057 var doc = eventPath[eventPath.length - 1]; | |
| 3058 if (doc instanceof wrappers.Document) | |
| 3059 win = doc.defaultView; | |
| 3060 } | |
| 3061 } | |
| 3062 } | |
| 3063 | |
| 3064 eventPathTable.set(event, eventPath); | |
| 3065 | |
| 3066 if (dispatchCapturing(event, eventPath, win, overrideTarget)) { | |
| 3067 if (dispatchAtTarget(event, eventPath, win, overrideTarget)) { | |
| 3068 dispatchBubbling(event, eventPath, win, overrideTarget); | |
| 3069 } | |
| 3070 } | |
| 3071 | |
| 3072 eventPhaseTable.set(event, NONE); | |
| 3073 currentTargetTable.delete(event, null); | |
| 3074 currentlyDispatchingEvents.delete(event); | |
| 3075 | |
| 3076 return event.defaultPrevented; | |
| 3077 } | |
| 3078 | |
| 3079 function dispatchCapturing(event, eventPath, win, overrideTarget) { | |
| 3080 var phase = CAPTURING_PHASE; | |
| 3081 | |
| 3082 if (win) { | |
| 3083 if (!invoke(win, event, phase, eventPath, overrideTarget)) | |
| 3084 return false; | |
| 3085 } | |
| 3086 | |
| 3087 for (var i = eventPath.length - 1; i > 0; i--) { | |
| 3088 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) | |
| 3089 return false; | |
| 3090 } | |
| 3091 | |
| 3092 return true; | |
| 3093 } | |
| 3094 | |
| 3095 function dispatchAtTarget(event, eventPath, win, overrideTarget) { | |
| 3096 var phase = AT_TARGET; | |
| 3097 var currentTarget = eventPath[0] || win; | |
| 3098 return invoke(currentTarget, event, phase, eventPath, overrideTarget); | |
| 3099 } | |
| 3100 | |
| 3101 function dispatchBubbling(event, eventPath, win, overrideTarget) { | |
| 3102 var phase = BUBBLING_PHASE; | |
| 3103 for (var i = 1; i < eventPath.length; i++) { | |
| 3104 if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) | |
| 3105 return; | |
| 3106 } | |
| 3107 | |
| 3108 if (win && eventPath.length > 0) { | |
| 3109 invoke(win, event, phase, eventPath, overrideTarget); | |
| 3110 } | |
| 3111 } | |
| 3112 | |
| 3113 function invoke(currentTarget, event, phase, eventPath, overrideTarget) { | |
| 3114 var listeners = listenersTable.get(currentTarget); | |
| 3115 if (!listeners) | |
| 3116 return true; | |
| 3117 | |
| 3118 var target = overrideTarget || eventRetargetting(eventPath, currentTarget); | |
| 3119 | |
| 3120 if (target === currentTarget) { | |
| 3121 if (phase === CAPTURING_PHASE) | |
| 3122 return true; | |
| 3123 | |
| 3124 if (phase === BUBBLING_PHASE) | |
| 3125 phase = AT_TARGET; | |
| 3126 | |
| 3127 } else if (phase === BUBBLING_PHASE && !event.bubbles) { | |
| 3128 return true; | |
| 3129 } | |
| 3130 | |
| 3131 if ('relatedTarget' in event) { | |
| 3132 var originalEvent = unwrap(event); | |
| 3133 var unwrappedRelatedTarget = originalEvent.relatedTarget; | |
| 3134 | |
| 3135 // X-Tag sets relatedTarget on a CustomEvent. If they do that there is no | |
| 3136 // way to have relatedTarget return the adjusted target but worse is that | |
| 3137 // the originalEvent might not have a relatedTarget so we hit an assert | |
| 3138 // when we try to wrap it. | |
| 3139 if (unwrappedRelatedTarget) { | |
| 3140 // In IE we can get objects that are not EventTargets at this point. | |
| 3141 // Safari does not have an EventTarget interface so revert to checking | |
| 3142 // for addEventListener as an approximation. | |
| 3143 if (unwrappedRelatedTarget instanceof Object && | |
| 3144 unwrappedRelatedTarget.addEventListener) { | |
| 3145 var relatedTarget = wrap(unwrappedRelatedTarget); | |
| 3146 | |
| 3147 var adjusted = | |
| 3148 relatedTargetResolution(event, currentTarget, relatedTarget); | |
| 3149 if (adjusted === target) | |
| 3150 return true; | |
| 3151 } else { | |
| 3152 adjusted = null; | |
| 3153 } | |
| 3154 relatedTargetTable.set(event, adjusted); | |
| 3155 } | |
| 3156 } | |
| 3157 | |
| 3158 eventPhaseTable.set(event, phase); | |
| 3159 var type = event.type; | |
| 3160 | |
| 3161 var anyRemoved = false; | |
| 3162 targetTable.set(event, target); | |
| 3163 currentTargetTable.set(event, currentTarget); | |
| 3164 | |
| 3165 // Keep track of the invoke depth so that we only clean up the removed | |
| 3166 // listeners if we are in the outermost invoke. | |
| 3167 listeners.depth++; | |
| 3168 | |
| 3169 for (var i = 0, len = listeners.length; i < len; i++) { | |
| 3170 var listener = listeners[i]; | |
| 3171 if (listener.removed) { | |
| 3172 anyRemoved = true; | |
| 3173 continue; | |
| 3174 } | |
| 3175 | |
| 3176 if (listener.type !== type || | |
| 3177 !listener.capture && phase === CAPTURING_PHASE || | |
| 3178 listener.capture && phase === BUBBLING_PHASE) { | |
| 3179 continue; | |
| 3180 } | |
| 3181 | |
| 3182 try { | |
| 3183 if (typeof listener.handler === 'function') | |
| 3184 listener.handler.call(currentTarget, event); | |
| 3185 else | |
| 3186 listener.handler.handleEvent(event); | |
| 3187 | |
| 3188 if (stopImmediatePropagationTable.get(event)) | |
| 3189 return false; | |
| 3190 | |
| 3191 } catch (ex) { | |
| 3192 if (!pendingError) | |
| 3193 pendingError = ex; | |
| 3194 } | |
| 3195 } | |
| 3196 | |
| 3197 listeners.depth--; | |
| 3198 | |
| 3199 if (anyRemoved && listeners.depth === 0) { | |
| 3200 var copy = listeners.slice(); | |
| 3201 listeners.length = 0; | |
| 3202 for (var i = 0; i < copy.length; i++) { | |
| 3203 if (!copy[i].removed) | |
| 3204 listeners.push(copy[i]); | |
| 3205 } | |
| 3206 } | |
| 3207 | |
| 3208 return !stopPropagationTable.get(event); | |
| 3209 } | |
| 3210 | |
| 3211 function Listener(type, handler, capture) { | |
| 3212 this.type = type; | |
| 3213 this.handler = handler; | |
| 3214 this.capture = Boolean(capture); | |
| 3215 } | |
| 3216 Listener.prototype = { | |
| 3217 equals: function(that) { | |
| 3218 return this.handler === that.handler && this.type === that.type && | |
| 3219 this.capture === that.capture; | |
| 3220 }, | |
| 3221 get removed() { | |
| 3222 return this.handler === null; | |
| 3223 }, | |
| 3224 remove: function() { | |
| 3225 this.handler = null; | |
| 3226 } | |
| 3227 }; | |
| 3228 | |
| 3229 var OriginalEvent = window.Event; | |
| 3230 OriginalEvent.prototype.polymerBlackList_ = { | |
| 3231 returnValue: true, | |
| 3232 // TODO(arv): keyLocation is part of KeyboardEvent but Firefox does not | |
| 3233 // support constructable KeyboardEvent so we keep it here for now. | |
| 3234 keyLocation: true | |
| 3235 }; | |
| 3236 | |
| 3237 /** | |
| 3238 * Creates a new Event wrapper or wraps an existin native Event object. | |
| 3239 * @param {string|Event} type | |
| 3240 * @param {Object=} options | |
| 3241 * @constructor | |
| 3242 */ | |
| 3243 function Event(type, options) { | |
| 3244 if (type instanceof OriginalEvent) { | |
| 3245 var impl = type; | |
| 3246 // In browsers that do not correctly support BeforeUnloadEvent we get to | |
| 3247 // the generic Event wrapper but we still want to ensure we create a | |
| 3248 // BeforeUnloadEvent. Since BeforeUnloadEvent calls super, we need to | |
| 3249 // prevent reentrancty. | |
| 3250 if (!OriginalBeforeUnloadEvent && impl.type === 'beforeunload' && | |
| 3251 !(this instanceof BeforeUnloadEvent)) { | |
| 3252 return new BeforeUnloadEvent(impl); | |
| 3253 } | |
| 3254 setWrapper(impl, this); | |
| 3255 } else { | |
| 3256 return wrap(constructEvent(OriginalEvent, 'Event', type, options)); | |
| 3257 } | |
| 3258 } | |
| 3259 Event.prototype = { | |
| 3260 get target() { | |
| 3261 return targetTable.get(this); | |
| 3262 }, | |
| 3263 get currentTarget() { | |
| 3264 return currentTargetTable.get(this); | |
| 3265 }, | |
| 3266 get eventPhase() { | |
| 3267 return eventPhaseTable.get(this); | |
| 3268 }, | |
| 3269 get path() { | |
| 3270 var eventPath = eventPathTable.get(this); | |
| 3271 if (!eventPath) | |
| 3272 return []; | |
| 3273 // TODO(arv): Event path should contain window. | |
| 3274 return eventPath.slice(); | |
| 3275 }, | |
| 3276 stopPropagation: function() { | |
| 3277 stopPropagationTable.set(this, true); | |
| 3278 }, | |
| 3279 stopImmediatePropagation: function() { | |
| 3280 stopPropagationTable.set(this, true); | |
| 3281 stopImmediatePropagationTable.set(this, true); | |
| 3282 } | |
| 3283 }; | |
| 3284 registerWrapper(OriginalEvent, Event, document.createEvent('Event')); | |
| 3285 | |
| 3286 function unwrapOptions(options) { | |
| 3287 if (!options || !options.relatedTarget) | |
| 3288 return options; | |
| 3289 return Object.create(options, { | |
| 3290 relatedTarget: {value: unwrap(options.relatedTarget)} | |
| 3291 }); | |
| 3292 } | |
| 3293 | |
| 3294 function registerGenericEvent(name, SuperEvent, prototype) { | |
| 3295 var OriginalEvent = window[name]; | |
| 3296 var GenericEvent = function(type, options) { | |
| 3297 if (type instanceof OriginalEvent) | |
| 3298 setWrapper(type, this); | |
| 3299 else | |
| 3300 return wrap(constructEvent(OriginalEvent, name, type, options)); | |
| 3301 }; | |
| 3302 GenericEvent.prototype = Object.create(SuperEvent.prototype); | |
| 3303 if (prototype) | |
| 3304 mixin(GenericEvent.prototype, prototype); | |
| 3305 if (OriginalEvent) { | |
| 3306 // - Old versions of Safari fails on new FocusEvent (and others?). | |
| 3307 // - IE does not support event constructors. | |
| 3308 // - createEvent('FocusEvent') throws in Firefox. | |
| 3309 // => Try the best practice solution first and fallback to the old way | |
| 3310 // if needed. | |
| 3311 try { | |
| 3312 registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent('temp')); | |
| 3313 } catch (ex) { | |
| 3314 registerWrapper(OriginalEvent, GenericEvent, | |
| 3315 document.createEvent(name)); | |
| 3316 } | |
| 3317 } | |
| 3318 return GenericEvent; | |
| 3319 } | |
| 3320 | |
| 3321 var UIEvent = registerGenericEvent('UIEvent', Event); | |
| 3322 var CustomEvent = registerGenericEvent('CustomEvent', Event); | |
| 3323 | |
| 3324 var relatedTargetProto = { | |
| 3325 get relatedTarget() { | |
| 3326 var relatedTarget = relatedTargetTable.get(this); | |
| 3327 // relatedTarget can be null. | |
| 3328 if (relatedTarget !== undefined) | |
| 3329 return relatedTarget; | |
| 3330 return wrap(unwrap(this).relatedTarget); | |
| 3331 } | |
| 3332 }; | |
| 3333 | |
| 3334 function getInitFunction(name, relatedTargetIndex) { | |
| 3335 return function() { | |
| 3336 arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]); | |
| 3337 var impl = unwrap(this); | |
| 3338 impl[name].apply(impl, arguments); | |
| 3339 }; | |
| 3340 } | |
| 3341 | |
| 3342 var mouseEventProto = mixin({ | |
| 3343 initMouseEvent: getInitFunction('initMouseEvent', 14) | |
| 3344 }, relatedTargetProto); | |
| 3345 | |
| 3346 var focusEventProto = mixin({ | |
| 3347 initFocusEvent: getInitFunction('initFocusEvent', 5) | |
| 3348 }, relatedTargetProto); | |
| 3349 | |
| 3350 var MouseEvent = registerGenericEvent('MouseEvent', UIEvent, mouseEventProto); | |
| 3351 var FocusEvent = registerGenericEvent('FocusEvent', UIEvent, focusEventProto); | |
| 3352 | |
| 3353 // In case the browser does not support event constructors we polyfill that | |
| 3354 // by calling `createEvent('Foo')` and `initFooEvent` where the arguments to | |
| 3355 // `initFooEvent` are derived from the registered default event init dict. | |
| 3356 var defaultInitDicts = Object.create(null); | |
| 3357 | |
| 3358 var supportsEventConstructors = (function() { | |
| 3359 try { | |
| 3360 new window.FocusEvent('focus'); | |
| 3361 } catch (ex) { | |
| 3362 return false; | |
| 3363 } | |
| 3364 return true; | |
| 3365 })(); | |
| 3366 | |
| 3367 /** | |
| 3368 * Constructs a new native event. | |
| 3369 */ | |
| 3370 function constructEvent(OriginalEvent, name, type, options) { | |
| 3371 if (supportsEventConstructors) | |
| 3372 return new OriginalEvent(type, unwrapOptions(options)); | |
| 3373 | |
| 3374 // Create the arguments from the default dictionary. | |
| 3375 var event = unwrap(document.createEvent(name)); | |
| 3376 var defaultDict = defaultInitDicts[name]; | |
| 3377 var args = [type]; | |
| 3378 Object.keys(defaultDict).forEach(function(key) { | |
| 3379 var v = options != null && key in options ? | |
| 3380 options[key] : defaultDict[key]; | |
| 3381 if (key === 'relatedTarget') | |
| 3382 v = unwrap(v); | |
| 3383 args.push(v); | |
| 3384 }); | |
| 3385 event['init' + name].apply(event, args); | |
| 3386 return event; | |
| 3387 } | |
| 3388 | |
| 3389 if (!supportsEventConstructors) { | |
| 3390 var configureEventConstructor = function(name, initDict, superName) { | |
| 3391 if (superName) { | |
| 3392 var superDict = defaultInitDicts[superName]; | |
| 3393 initDict = mixin(mixin({}, superDict), initDict); | |
| 3394 } | |
| 3395 | |
| 3396 defaultInitDicts[name] = initDict; | |
| 3397 }; | |
| 3398 | |
| 3399 // The order of the default event init dictionary keys is important, the | |
| 3400 // arguments to initFooEvent is derived from that. | |
| 3401 configureEventConstructor('Event', {bubbles: false, cancelable: false}); | |
| 3402 configureEventConstructor('CustomEvent', {detail: null}, 'Event'); | |
| 3403 configureEventConstructor('UIEvent', {view: null, detail: 0}, 'Event'); | |
| 3404 configureEventConstructor('MouseEvent', { | |
| 3405 screenX: 0, | |
| 3406 screenY: 0, | |
| 3407 clientX: 0, | |
| 3408 clientY: 0, | |
| 3409 ctrlKey: false, | |
| 3410 altKey: false, | |
| 3411 shiftKey: false, | |
| 3412 metaKey: false, | |
| 3413 button: 0, | |
| 3414 relatedTarget: null | |
| 3415 }, 'UIEvent'); | |
| 3416 configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent'); | |
| 3417 } | |
| 3418 | |
| 3419 // Safari 7 does not yet have BeforeUnloadEvent. | |
| 3420 // https://bugs.webkit.org/show_bug.cgi?id=120849 | |
| 3421 var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent; | |
| 3422 | |
| 3423 function BeforeUnloadEvent(impl) { | |
| 3424 Event.call(this, impl); | |
| 3425 } | |
| 3426 BeforeUnloadEvent.prototype = Object.create(Event.prototype); | |
| 3427 mixin(BeforeUnloadEvent.prototype, { | |
| 3428 get returnValue() { | |
| 3429 return unsafeUnwrap(this).returnValue; | |
| 3430 }, | |
| 3431 set returnValue(v) { | |
| 3432 unsafeUnwrap(this).returnValue = v; | |
| 3433 } | |
| 3434 }); | |
| 3435 | |
| 3436 if (OriginalBeforeUnloadEvent) | |
| 3437 registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent); | |
| 3438 | |
| 3439 function isValidListener(fun) { | |
| 3440 if (typeof fun === 'function') | |
| 3441 return true; | |
| 3442 return fun && fun.handleEvent; | |
| 3443 } | |
| 3444 | |
| 3445 function isMutationEvent(type) { | |
| 3446 switch (type) { | |
| 3447 case 'DOMAttrModified': | |
| 3448 case 'DOMAttributeNameChanged': | |
| 3449 case 'DOMCharacterDataModified': | |
| 3450 case 'DOMElementNameChanged': | |
| 3451 case 'DOMNodeInserted': | |
| 3452 case 'DOMNodeInsertedIntoDocument': | |
| 3453 case 'DOMNodeRemoved': | |
| 3454 case 'DOMNodeRemovedFromDocument': | |
| 3455 case 'DOMSubtreeModified': | |
| 3456 return true; | |
| 3457 } | |
| 3458 return false; | |
| 3459 } | |
| 3460 | |
| 3461 var OriginalEventTarget = window.EventTarget; | |
| 3462 | |
| 3463 /** | |
| 3464 * This represents a wrapper for an EventTarget. | |
| 3465 * @param {!EventTarget} impl The original event target. | |
| 3466 * @constructor | |
| 3467 */ | |
| 3468 function EventTarget(impl) { | |
| 3469 setWrapper(impl, this); | |
| 3470 } | |
| 3471 | |
| 3472 // Node and Window have different internal type checks in WebKit so we cannot | |
| 3473 // use the same method as the original function. | |
| 3474 var methodNames = [ | |
| 3475 'addEventListener', | |
| 3476 'removeEventListener', | |
| 3477 'dispatchEvent' | |
| 3478 ]; | |
| 3479 | |
| 3480 [Node, Window].forEach(function(constructor) { | |
| 3481 var p = constructor.prototype; | |
| 3482 methodNames.forEach(function(name) { | |
| 3483 Object.defineProperty(p, name + '_', {value: p[name]}); | |
| 3484 }); | |
| 3485 }); | |
| 3486 | |
| 3487 function getTargetToListenAt(wrapper) { | |
| 3488 if (wrapper instanceof wrappers.ShadowRoot) | |
| 3489 wrapper = wrapper.host; | |
| 3490 return unwrap(wrapper); | |
| 3491 } | |
| 3492 | |
| 3493 EventTarget.prototype = { | |
| 3494 addEventListener: function(type, fun, capture) { | |
| 3495 if (!isValidListener(fun) || isMutationEvent(type)) | |
| 3496 return; | |
| 3497 | |
| 3498 var listener = new Listener(type, fun, capture); | |
| 3499 var listeners = listenersTable.get(this); | |
| 3500 if (!listeners) { | |
| 3501 listeners = []; | |
| 3502 listeners.depth = 0; | |
| 3503 listenersTable.set(this, listeners); | |
| 3504 } else { | |
| 3505 // Might have a duplicate. | |
| 3506 for (var i = 0; i < listeners.length; i++) { | |
| 3507 if (listener.equals(listeners[i])) | |
| 3508 return; | |
| 3509 } | |
| 3510 } | |
| 3511 | |
| 3512 listeners.push(listener); | |
| 3513 | |
| 3514 var target = getTargetToListenAt(this); | |
| 3515 target.addEventListener_(type, dispatchOriginalEvent, true); | |
| 3516 }, | |
| 3517 removeEventListener: function(type, fun, capture) { | |
| 3518 capture = Boolean(capture); | |
| 3519 var listeners = listenersTable.get(this); | |
| 3520 if (!listeners) | |
| 3521 return; | |
| 3522 var count = 0, found = false; | |
| 3523 for (var i = 0; i < listeners.length; i++) { | |
| 3524 if (listeners[i].type === type && listeners[i].capture === capture) { | |
| 3525 count++; | |
| 3526 if (listeners[i].handler === fun) { | |
| 3527 found = true; | |
| 3528 listeners[i].remove(); | |
| 3529 } | |
| 3530 } | |
| 3531 } | |
| 3532 | |
| 3533 if (found && count === 1) { | |
| 3534 var target = getTargetToListenAt(this); | |
| 3535 target.removeEventListener_(type, dispatchOriginalEvent, true); | |
| 3536 } | |
| 3537 }, | |
| 3538 dispatchEvent: function(event) { | |
| 3539 // We want to use the native dispatchEvent because it triggers the default | |
| 3540 // actions (like checking a checkbox). However, if there are no listeners | |
| 3541 // in the composed tree then there are no events that will trigger and | |
| 3542 // listeners in the non composed tree that are part of the event path are | |
| 3543 // not notified. | |
| 3544 // | |
| 3545 // If we find out that there are no listeners in the composed tree we add | |
| 3546 // a temporary listener to the target which makes us get called back even | |
| 3547 // in that case. | |
| 3548 | |
| 3549 var nativeEvent = unwrap(event); | |
| 3550 var eventType = nativeEvent.type; | |
| 3551 | |
| 3552 // Allow dispatching the same event again. This is safe because if user | |
| 3553 // code calls this during an existing dispatch of the same event the | |
| 3554 // native dispatchEvent throws (that is required by the spec). | |
| 3555 handledEventsTable.set(nativeEvent, false); | |
| 3556 | |
| 3557 // Force rendering since we prefer native dispatch and that works on the | |
| 3558 // composed tree. | |
| 3559 scope.renderAllPending(); | |
| 3560 | |
| 3561 var tempListener; | |
| 3562 if (!hasListenerInAncestors(this, eventType)) { | |
| 3563 tempListener = function() {}; | |
| 3564 this.addEventListener(eventType, tempListener, true); | |
| 3565 } | |
| 3566 | |
| 3567 try { | |
| 3568 return unwrap(this).dispatchEvent_(nativeEvent); | |
| 3569 } finally { | |
| 3570 if (tempListener) | |
| 3571 this.removeEventListener(eventType, tempListener, true); | |
| 3572 } | |
| 3573 } | |
| 3574 }; | |
| 3575 | |
| 3576 function hasListener(node, type) { | |
| 3577 var listeners = listenersTable.get(node); | |
| 3578 if (listeners) { | |
| 3579 for (var i = 0; i < listeners.length; i++) { | |
| 3580 if (!listeners[i].removed && listeners[i].type === type) | |
| 3581 return true; | |
| 3582 } | |
| 3583 } | |
| 3584 return false; | |
| 3585 } | |
| 3586 | |
| 3587 function hasListenerInAncestors(target, type) { | |
| 3588 for (var node = unwrap(target); node; node = node.parentNode) { | |
| 3589 if (hasListener(wrap(node), type)) | |
| 3590 return true; | |
| 3591 } | |
| 3592 return false; | |
| 3593 } | |
| 3594 | |
| 3595 if (OriginalEventTarget) | |
| 3596 registerWrapper(OriginalEventTarget, EventTarget); | |
| 3597 | |
| 3598 function wrapEventTargetMethods(constructors) { | |
| 3599 forwardMethodsToWrapper(constructors, methodNames); | |
| 3600 } | |
| 3601 | |
| 3602 var originalElementFromPoint = document.elementFromPoint; | |
| 3603 | |
| 3604 function elementFromPoint(self, document, x, y) { | |
| 3605 scope.renderAllPending(); | |
| 3606 | |
| 3607 var element = | |
| 3608 wrap(originalElementFromPoint.call(unsafeUnwrap(document), x, y)); | |
| 3609 if (!element) | |
| 3610 return null; | |
| 3611 var path = getEventPath(element, null); | |
| 3612 | |
| 3613 // scope the path to this TreeScope | |
| 3614 var idx = path.lastIndexOf(self); | |
| 3615 if (idx == -1) | |
| 3616 return null; | |
| 3617 else | |
| 3618 path = path.slice(0, idx); | |
| 3619 | |
| 3620 // TODO(dfreedm): pass idx to eventRetargetting to avoid array copy | |
| 3621 return eventRetargetting(path, self); | |
| 3622 } | |
| 3623 | |
| 3624 /** | |
| 3625 * Returns a function that is to be used as a getter for `onfoo` properties. | |
| 3626 * @param {string} name | |
| 3627 * @return {Function} | |
| 3628 */ | |
| 3629 function getEventHandlerGetter(name) { | |
| 3630 return function() { | |
| 3631 var inlineEventHandlers = eventHandlersTable.get(this); | |
| 3632 return inlineEventHandlers && inlineEventHandlers[name] && | |
| 3633 inlineEventHandlers[name].value || null; | |
| 3634 }; | |
| 3635 } | |
| 3636 | |
| 3637 /** | |
| 3638 * Returns a function that is to be used as a setter for `onfoo` properties. | |
| 3639 * @param {string} name | |
| 3640 * @return {Function} | |
| 3641 */ | |
| 3642 function getEventHandlerSetter(name) { | |
| 3643 var eventType = name.slice(2); | |
| 3644 return function(value) { | |
| 3645 var inlineEventHandlers = eventHandlersTable.get(this); | |
| 3646 if (!inlineEventHandlers) { | |
| 3647 inlineEventHandlers = Object.create(null); | |
| 3648 eventHandlersTable.set(this, inlineEventHandlers); | |
| 3649 } | |
| 3650 | |
| 3651 var old = inlineEventHandlers[name]; | |
| 3652 if (old) | |
| 3653 this.removeEventListener(eventType, old.wrapped, false); | |
| 3654 | |
| 3655 if (typeof value === 'function') { | |
| 3656 var wrapped = function(e) { | |
| 3657 var rv = value.call(this, e); | |
| 3658 if (rv === false) | |
| 3659 e.preventDefault(); | |
| 3660 else if (name === 'onbeforeunload' && typeof rv === 'string') | |
| 3661 e.returnValue = rv; | |
| 3662 // mouseover uses true for preventDefault but preventDefault for | |
| 3663 // mouseover is ignored by browsers these day. | |
| 3664 }; | |
| 3665 | |
| 3666 this.addEventListener(eventType, wrapped, false); | |
| 3667 inlineEventHandlers[name] = { | |
| 3668 value: value, | |
| 3669 wrapped: wrapped | |
| 3670 }; | |
| 3671 } | |
| 3672 }; | |
| 3673 } | |
| 3674 | |
| 3675 scope.elementFromPoint = elementFromPoint; | |
| 3676 scope.getEventHandlerGetter = getEventHandlerGetter; | |
| 3677 scope.getEventHandlerSetter = getEventHandlerSetter; | |
| 3678 scope.wrapEventTargetMethods = wrapEventTargetMethods; | |
| 3679 scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent; | |
| 3680 scope.wrappers.CustomEvent = CustomEvent; | |
| 3681 scope.wrappers.Event = Event; | |
| 3682 scope.wrappers.EventTarget = EventTarget; | |
| 3683 scope.wrappers.FocusEvent = FocusEvent; | |
| 3684 scope.wrappers.MouseEvent = MouseEvent; | |
| 3685 scope.wrappers.UIEvent = UIEvent; | |
| 3686 | |
| 3687 })(window.ShadowDOMPolyfill); | |
| 3688 | |
| 3689 /* | |
| 3690 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 3691 * Use of this source code is goverened by a BSD-style | |
| 3692 * license that can be found in the LICENSE file. | |
| 3693 */ | |
| 3694 | |
| 3695 (function(scope) { | |
| 3696 'use strict'; | |
| 3697 | |
| 3698 var UIEvent = scope.wrappers.UIEvent; | |
| 3699 var mixin = scope.mixin; | |
| 3700 var registerWrapper = scope.registerWrapper; | |
| 3701 var setWrapper = scope.setWrapper; | |
| 3702 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 3703 var wrap = scope.wrap; | |
| 3704 | |
| 3705 // TouchEvent is WebKit/Blink only. | |
| 3706 var OriginalTouchEvent = window.TouchEvent; | |
| 3707 if (!OriginalTouchEvent) | |
| 3708 return; | |
| 3709 | |
| 3710 var nativeEvent; | |
| 3711 try { | |
| 3712 nativeEvent = document.createEvent('TouchEvent'); | |
| 3713 } catch (ex) { | |
| 3714 // In Chrome creating a TouchEvent fails if the feature is not turned on | |
| 3715 // which it isn't on desktop Chrome. | |
| 3716 return; | |
| 3717 } | |
| 3718 | |
| 3719 var nonEnumDescriptor = {enumerable: false}; | |
| 3720 | |
| 3721 function nonEnum(obj, prop) { | |
| 3722 Object.defineProperty(obj, prop, nonEnumDescriptor); | |
| 3723 } | |
| 3724 | |
| 3725 function Touch(impl) { | |
| 3726 setWrapper(impl, this); | |
| 3727 } | |
| 3728 | |
| 3729 Touch.prototype = { | |
| 3730 get target() { | |
| 3731 return wrap(unsafeUnwrap(this).target); | |
| 3732 } | |
| 3733 }; | |
| 3734 | |
| 3735 var descr = { | |
| 3736 configurable: true, | |
| 3737 enumerable: true, | |
| 3738 get: null | |
| 3739 }; | |
| 3740 | |
| 3741 [ | |
| 3742 'clientX', | |
| 3743 'clientY', | |
| 3744 'screenX', | |
| 3745 'screenY', | |
| 3746 'pageX', | |
| 3747 'pageY', | |
| 3748 'identifier', | |
| 3749 'webkitRadiusX', | |
| 3750 'webkitRadiusY', | |
| 3751 'webkitRotationAngle', | |
| 3752 'webkitForce' | |
| 3753 ].forEach(function(name) { | |
| 3754 descr.get = function() { | |
| 3755 return unsafeUnwrap(this)[name]; | |
| 3756 }; | |
| 3757 Object.defineProperty(Touch.prototype, name, descr); | |
| 3758 }); | |
| 3759 | |
| 3760 function TouchList() { | |
| 3761 this.length = 0; | |
| 3762 nonEnum(this, 'length'); | |
| 3763 } | |
| 3764 | |
| 3765 TouchList.prototype = { | |
| 3766 item: function(index) { | |
| 3767 return this[index]; | |
| 3768 } | |
| 3769 }; | |
| 3770 | |
| 3771 function wrapTouchList(nativeTouchList) { | |
| 3772 var list = new TouchList(); | |
| 3773 for (var i = 0; i < nativeTouchList.length; i++) { | |
| 3774 list[i] = new Touch(nativeTouchList[i]); | |
| 3775 } | |
| 3776 list.length = i; | |
| 3777 return list; | |
| 3778 } | |
| 3779 | |
| 3780 function TouchEvent(impl) { | |
| 3781 UIEvent.call(this, impl); | |
| 3782 } | |
| 3783 | |
| 3784 TouchEvent.prototype = Object.create(UIEvent.prototype); | |
| 3785 | |
| 3786 mixin(TouchEvent.prototype, { | |
| 3787 get touches() { | |
| 3788 return wrapTouchList(unsafeUnwrap(this).touches); | |
| 3789 }, | |
| 3790 | |
| 3791 get targetTouches() { | |
| 3792 return wrapTouchList(unsafeUnwrap(this).targetTouches); | |
| 3793 }, | |
| 3794 | |
| 3795 get changedTouches() { | |
| 3796 return wrapTouchList(unsafeUnwrap(this).changedTouches); | |
| 3797 }, | |
| 3798 | |
| 3799 initTouchEvent: function() { | |
| 3800 // The only way to use this is to reuse the TouchList from an existing | |
| 3801 // TouchEvent. Since this is WebKit/Blink proprietary API we will not | |
| 3802 // implement this until someone screams. | |
| 3803 throw new Error('Not implemented'); | |
| 3804 } | |
| 3805 }); | |
| 3806 | |
| 3807 registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent); | |
| 3808 | |
| 3809 scope.wrappers.Touch = Touch; | |
| 3810 scope.wrappers.TouchEvent = TouchEvent; | |
| 3811 scope.wrappers.TouchList = TouchList; | |
| 3812 | |
| 3813 })(window.ShadowDOMPolyfill); | |
| 3814 | |
| 3815 | |
| 3816 // Copyright 2012 The Polymer Authors. All rights reserved. | |
| 3817 // Use of this source code is goverened by a BSD-style | |
| 3818 // license that can be found in the LICENSE file. | |
| 3819 | |
| 3820 (function(scope) { | |
| 3821 'use strict'; | |
| 3822 | |
| 3823 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 3824 var wrap = scope.wrap; | |
| 3825 | |
| 3826 var nonEnumDescriptor = {enumerable: false}; | |
| 3827 | |
| 3828 function nonEnum(obj, prop) { | |
| 3829 Object.defineProperty(obj, prop, nonEnumDescriptor); | |
| 3830 } | |
| 3831 | |
| 3832 function NodeList() { | |
| 3833 this.length = 0; | |
| 3834 nonEnum(this, 'length'); | |
| 3835 } | |
| 3836 NodeList.prototype = { | |
| 3837 item: function(index) { | |
| 3838 return this[index]; | |
| 3839 } | |
| 3840 }; | |
| 3841 nonEnum(NodeList.prototype, 'item'); | |
| 3842 | |
| 3843 function wrapNodeList(list) { | |
| 3844 if (list == null) | |
| 3845 return list; | |
| 3846 var wrapperList = new NodeList(); | |
| 3847 for (var i = 0, length = list.length; i < length; i++) { | |
| 3848 wrapperList[i] = wrap(list[i]); | |
| 3849 } | |
| 3850 wrapperList.length = length; | |
| 3851 return wrapperList; | |
| 3852 } | |
| 3853 | |
| 3854 function addWrapNodeListMethod(wrapperConstructor, name) { | |
| 3855 wrapperConstructor.prototype[name] = function() { | |
| 3856 return wrapNodeList( | |
| 3857 unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments)); | |
| 3858 }; | |
| 3859 } | |
| 3860 | |
| 3861 scope.wrappers.NodeList = NodeList; | |
| 3862 scope.addWrapNodeListMethod = addWrapNodeListMethod; | |
| 3863 scope.wrapNodeList = wrapNodeList; | |
| 3864 | |
| 3865 })(window.ShadowDOMPolyfill); | |
| 3866 | |
| 3867 /* | |
| 3868 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 3869 * Use of this source code is goverened by a BSD-style | |
| 3870 * license that can be found in the LICENSE file. | |
| 3871 */ | |
| 3872 | |
| 3873 (function(scope) { | |
| 3874 'use strict'; | |
| 3875 | |
| 3876 // TODO(arv): Implement. | |
| 3877 | |
| 3878 scope.wrapHTMLCollection = scope.wrapNodeList; | |
| 3879 scope.wrappers.HTMLCollection = scope.wrappers.NodeList; | |
| 3880 | |
| 3881 })(window.ShadowDOMPolyfill); | |
| 3882 | |
| 3883 /** | |
| 3884 * Copyright 2012 The Polymer Authors. All rights reserved. | |
| 3885 * Use of this source code is goverened by a BSD-style | |
| 3886 * license that can be found in the LICENSE file. | |
| 3887 */ | |
| 3888 | |
| 3889 (function(scope) { | |
| 3890 'use strict'; | |
| 3891 | |
| 3892 var EventTarget = scope.wrappers.EventTarget; | |
| 3893 var NodeList = scope.wrappers.NodeList; | |
| 3894 var TreeScope = scope.TreeScope; | |
| 3895 var assert = scope.assert; | |
| 3896 var defineWrapGetter = scope.defineWrapGetter; | |
| 3897 var enqueueMutation = scope.enqueueMutation; | |
| 3898 var getTreeScope = scope.getTreeScope; | |
| 3899 var isWrapper = scope.isWrapper; | |
| 3900 var mixin = scope.mixin; | |
| 3901 var registerTransientObservers = scope.registerTransientObservers; | |
| 3902 var registerWrapper = scope.registerWrapper; | |
| 3903 var setTreeScope = scope.setTreeScope; | |
| 3904 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 3905 var unwrap = scope.unwrap; | |
| 3906 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 3907 var wrap = scope.wrap; | |
| 3908 var wrapIfNeeded = scope.wrapIfNeeded; | |
| 3909 var wrappers = scope.wrappers; | |
| 3910 | |
| 3911 function assertIsNodeWrapper(node) { | |
| 3912 assert(node instanceof Node); | |
| 3913 } | |
| 3914 | |
| 3915 function createOneElementNodeList(node) { | |
| 3916 var nodes = new NodeList(); | |
| 3917 nodes[0] = node; | |
| 3918 nodes.length = 1; | |
| 3919 return nodes; | |
| 3920 } | |
| 3921 | |
| 3922 var surpressMutations = false; | |
| 3923 | |
| 3924 /** | |
| 3925 * Called before node is inserted into a node to enqueue its removal from its | |
| 3926 * old parent. | |
| 3927 * @param {!Node} node The node that is about to be removed. | |
| 3928 * @param {!Node} parent The parent node that the node is being removed from. | |
| 3929 * @param {!NodeList} nodes The collected nodes. | |
| 3930 */ | |
| 3931 function enqueueRemovalForInsertedNodes(node, parent, nodes) { | |
| 3932 enqueueMutation(parent, 'childList', { | |
| 3933 removedNodes: nodes, | |
| 3934 previousSibling: node.previousSibling, | |
| 3935 nextSibling: node.nextSibling | |
| 3936 }); | |
| 3937 } | |
| 3938 | |
| 3939 function enqueueRemovalForInsertedDocumentFragment(df, nodes) { | |
| 3940 enqueueMutation(df, 'childList', { | |
| 3941 removedNodes: nodes | |
| 3942 }); | |
| 3943 } | |
| 3944 | |
| 3945 /** | |
| 3946 * Collects nodes from a DocumentFragment or a Node for removal followed | |
| 3947 * by an insertion. | |
| 3948 * | |
| 3949 * This updates the internal pointers for node, previousNode and nextNode. | |
| 3950 */ | |
| 3951 function collectNodes(node, parentNode, previousNode, nextNode) { | |
| 3952 if (node instanceof DocumentFragment) { | |
| 3953 var nodes = collectNodesForDocumentFragment(node); | |
| 3954 | |
| 3955 // The extra loop is to work around bugs with DocumentFragments in IE. | |
| 3956 surpressMutations = true; | |
| 3957 for (var i = nodes.length - 1; i >= 0; i--) { | |
| 3958 node.removeChild(nodes[i]); | |
| 3959 nodes[i].parentNode_ = parentNode; | |
| 3960 } | |
| 3961 surpressMutations = false; | |
| 3962 | |
| 3963 for (var i = 0; i < nodes.length; i++) { | |
| 3964 nodes[i].previousSibling_ = nodes[i - 1] || previousNode; | |
| 3965 nodes[i].nextSibling_ = nodes[i + 1] || nextNode; | |
| 3966 } | |
| 3967 | |
| 3968 if (previousNode) | |
| 3969 previousNode.nextSibling_ = nodes[0]; | |
| 3970 if (nextNode) | |
| 3971 nextNode.previousSibling_ = nodes[nodes.length - 1]; | |
| 3972 | |
| 3973 return nodes; | |
| 3974 } | |
| 3975 | |
| 3976 var nodes = createOneElementNodeList(node); | |
| 3977 var oldParent = node.parentNode; | |
| 3978 if (oldParent) { | |
| 3979 // This will enqueue the mutation record for the removal as needed. | |
| 3980 oldParent.removeChild(node); | |
| 3981 } | |
| 3982 | |
| 3983 node.parentNode_ = parentNode; | |
| 3984 node.previousSibling_ = previousNode; | |
| 3985 node.nextSibling_ = nextNode; | |
| 3986 if (previousNode) | |
| 3987 previousNode.nextSibling_ = node; | |
| 3988 if (nextNode) | |
| 3989 nextNode.previousSibling_ = node; | |
| 3990 | |
| 3991 return nodes; | |
| 3992 } | |
| 3993 | |
| 3994 function collectNodesNative(node) { | |
| 3995 if (node instanceof DocumentFragment) | |
| 3996 return collectNodesForDocumentFragment(node); | |
| 3997 | |
| 3998 var nodes = createOneElementNodeList(node); | |
| 3999 var oldParent = node.parentNode; | |
| 4000 if (oldParent) | |
| 4001 enqueueRemovalForInsertedNodes(node, oldParent, nodes); | |
| 4002 return nodes; | |
| 4003 } | |
| 4004 | |
| 4005 function collectNodesForDocumentFragment(node) { | |
| 4006 var nodes = new NodeList(); | |
| 4007 var i = 0; | |
| 4008 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 4009 nodes[i++] = child; | |
| 4010 } | |
| 4011 nodes.length = i; | |
| 4012 enqueueRemovalForInsertedDocumentFragment(node, nodes); | |
| 4013 return nodes; | |
| 4014 } | |
| 4015 | |
| 4016 function snapshotNodeList(nodeList) { | |
| 4017 // NodeLists are not live at the moment so just return the same object. | |
| 4018 return nodeList; | |
| 4019 } | |
| 4020 | |
| 4021 // http://dom.spec.whatwg.org/#node-is-inserted | |
| 4022 function nodeWasAdded(node, treeScope) { | |
| 4023 setTreeScope(node, treeScope); | |
| 4024 node.nodeIsInserted_(); | |
| 4025 } | |
| 4026 | |
| 4027 function nodesWereAdded(nodes, parent) { | |
| 4028 var treeScope = getTreeScope(parent); | |
| 4029 for (var i = 0; i < nodes.length; i++) { | |
| 4030 nodeWasAdded(nodes[i], treeScope); | |
| 4031 } | |
| 4032 } | |
| 4033 | |
| 4034 // http://dom.spec.whatwg.org/#node-is-removed | |
| 4035 function nodeWasRemoved(node) { | |
| 4036 setTreeScope(node, new TreeScope(node, null)); | |
| 4037 } | |
| 4038 | |
| 4039 function nodesWereRemoved(nodes) { | |
| 4040 for (var i = 0; i < nodes.length; i++) { | |
| 4041 nodeWasRemoved(nodes[i]); | |
| 4042 } | |
| 4043 } | |
| 4044 | |
| 4045 function ensureSameOwnerDocument(parent, child) { | |
| 4046 var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? | |
| 4047 parent : parent.ownerDocument; | |
| 4048 if (ownerDoc !== child.ownerDocument) | |
| 4049 ownerDoc.adoptNode(child); | |
| 4050 } | |
| 4051 | |
| 4052 function adoptNodesIfNeeded(owner, nodes) { | |
| 4053 if (!nodes.length) | |
| 4054 return; | |
| 4055 | |
| 4056 var ownerDoc = owner.ownerDocument; | |
| 4057 | |
| 4058 // All nodes have the same ownerDocument when we get here. | |
| 4059 if (ownerDoc === nodes[0].ownerDocument) | |
| 4060 return; | |
| 4061 | |
| 4062 for (var i = 0; i < nodes.length; i++) { | |
| 4063 scope.adoptNodeNoRemove(nodes[i], ownerDoc); | |
| 4064 } | |
| 4065 } | |
| 4066 | |
| 4067 function unwrapNodesForInsertion(owner, nodes) { | |
| 4068 adoptNodesIfNeeded(owner, nodes); | |
| 4069 var length = nodes.length; | |
| 4070 | |
| 4071 if (length === 1) | |
| 4072 return unwrap(nodes[0]); | |
| 4073 | |
| 4074 var df = unwrap(owner.ownerDocument.createDocumentFragment()); | |
| 4075 for (var i = 0; i < length; i++) { | |
| 4076 df.appendChild(unwrap(nodes[i])); | |
| 4077 } | |
| 4078 return df; | |
| 4079 } | |
| 4080 | |
| 4081 function clearChildNodes(wrapper) { | |
| 4082 if (wrapper.firstChild_ !== undefined) { | |
| 4083 var child = wrapper.firstChild_; | |
| 4084 while (child) { | |
| 4085 var tmp = child; | |
| 4086 child = child.nextSibling_; | |
| 4087 tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined; | |
| 4088 } | |
| 4089 } | |
| 4090 wrapper.firstChild_ = wrapper.lastChild_ = undefined; | |
| 4091 } | |
| 4092 | |
| 4093 function removeAllChildNodes(wrapper) { | |
| 4094 if (wrapper.invalidateShadowRenderer()) { | |
| 4095 var childWrapper = wrapper.firstChild; | |
| 4096 while (childWrapper) { | |
| 4097 assert(childWrapper.parentNode === wrapper); | |
| 4098 var nextSibling = childWrapper.nextSibling; | |
| 4099 var childNode = unwrap(childWrapper); | |
| 4100 var parentNode = childNode.parentNode; | |
| 4101 if (parentNode) | |
| 4102 originalRemoveChild.call(parentNode, childNode); | |
| 4103 childWrapper.previousSibling_ = childWrapper.nextSibling_ = | |
| 4104 childWrapper.parentNode_ = null; | |
| 4105 childWrapper = nextSibling; | |
| 4106 } | |
| 4107 wrapper.firstChild_ = wrapper.lastChild_ = null; | |
| 4108 } else { | |
| 4109 var node = unwrap(wrapper); | |
| 4110 var child = node.firstChild; | |
| 4111 var nextSibling; | |
| 4112 while (child) { | |
| 4113 nextSibling = child.nextSibling; | |
| 4114 originalRemoveChild.call(node, child); | |
| 4115 child = nextSibling; | |
| 4116 } | |
| 4117 } | |
| 4118 } | |
| 4119 | |
| 4120 function invalidateParent(node) { | |
| 4121 var p = node.parentNode; | |
| 4122 return p && p.invalidateShadowRenderer(); | |
| 4123 } | |
| 4124 | |
| 4125 function cleanupNodes(nodes) { | |
| 4126 for (var i = 0, n; i < nodes.length; i++) { | |
| 4127 n = nodes[i]; | |
| 4128 n.parentNode.removeChild(n); | |
| 4129 } | |
| 4130 } | |
| 4131 | |
| 4132 var originalImportNode = document.importNode; | |
| 4133 var originalCloneNode = window.Node.prototype.cloneNode; | |
| 4134 | |
| 4135 function cloneNode(node, deep, opt_doc) { | |
| 4136 var clone; | |
| 4137 if (opt_doc) | |
| 4138 clone = wrap(originalImportNode.call(opt_doc, unsafeUnwrap(node), false)); | |
| 4139 else | |
| 4140 clone = wrap(originalCloneNode.call(unsafeUnwrap(node), false)); | |
| 4141 | |
| 4142 if (deep) { | |
| 4143 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 4144 clone.appendChild(cloneNode(child, true, opt_doc)); | |
| 4145 } | |
| 4146 | |
| 4147 if (node instanceof wrappers.HTMLTemplateElement) { | |
| 4148 var cloneContent = clone.content; | |
| 4149 for (var child = node.content.firstChild; | |
| 4150 child; | |
| 4151 child = child.nextSibling) { | |
| 4152 cloneContent.appendChild(cloneNode(child, true, opt_doc)); | |
| 4153 } | |
| 4154 } | |
| 4155 } | |
| 4156 // TODO(arv): Some HTML elements also clone other data like value. | |
| 4157 return clone; | |
| 4158 } | |
| 4159 | |
| 4160 function contains(self, child) { | |
| 4161 if (!child || getTreeScope(self) !== getTreeScope(child)) | |
| 4162 return false; | |
| 4163 | |
| 4164 for (var node = child; node; node = node.parentNode) { | |
| 4165 if (node === self) | |
| 4166 return true; | |
| 4167 } | |
| 4168 return false; | |
| 4169 } | |
| 4170 | |
| 4171 var OriginalNode = window.Node; | |
| 4172 | |
| 4173 /** | |
| 4174 * This represents a wrapper of a native DOM node. | |
| 4175 * @param {!Node} original The original DOM node, aka, the visual DOM node. | |
| 4176 * @constructor | |
| 4177 * @extends {EventTarget} | |
| 4178 */ | |
| 4179 function Node(original) { | |
| 4180 assert(original instanceof OriginalNode); | |
| 4181 | |
| 4182 EventTarget.call(this, original); | |
| 4183 | |
| 4184 // These properties are used to override the visual references with the | |
| 4185 // logical ones. If the value is undefined it means that the logical is the | |
| 4186 // same as the visual. | |
| 4187 | |
| 4188 /** | |
| 4189 * @type {Node|undefined} | |
| 4190 * @private | |
| 4191 */ | |
| 4192 this.parentNode_ = undefined; | |
| 4193 | |
| 4194 /** | |
| 4195 * @type {Node|undefined} | |
| 4196 * @private | |
| 4197 */ | |
| 4198 this.firstChild_ = undefined; | |
| 4199 | |
| 4200 /** | |
| 4201 * @type {Node|undefined} | |
| 4202 * @private | |
| 4203 */ | |
| 4204 this.lastChild_ = undefined; | |
| 4205 | |
| 4206 /** | |
| 4207 * @type {Node|undefined} | |
| 4208 * @private | |
| 4209 */ | |
| 4210 this.nextSibling_ = undefined; | |
| 4211 | |
| 4212 /** | |
| 4213 * @type {Node|undefined} | |
| 4214 * @private | |
| 4215 */ | |
| 4216 this.previousSibling_ = undefined; | |
| 4217 | |
| 4218 this.treeScope_ = undefined; | |
| 4219 } | |
| 4220 | |
| 4221 var OriginalDocumentFragment = window.DocumentFragment; | |
| 4222 var originalAppendChild = OriginalNode.prototype.appendChild; | |
| 4223 var originalCompareDocumentPosition = | |
| 4224 OriginalNode.prototype.compareDocumentPosition; | |
| 4225 var originalInsertBefore = OriginalNode.prototype.insertBefore; | |
| 4226 var originalRemoveChild = OriginalNode.prototype.removeChild; | |
| 4227 var originalReplaceChild = OriginalNode.prototype.replaceChild; | |
| 4228 | |
| 4229 var isIe = /Trident/.test(navigator.userAgent); | |
| 4230 | |
| 4231 var removeChildOriginalHelper = isIe ? | |
| 4232 function(parent, child) { | |
| 4233 try { | |
| 4234 originalRemoveChild.call(parent, child); | |
| 4235 } catch (ex) { | |
| 4236 if (!(parent instanceof OriginalDocumentFragment)) | |
| 4237 throw ex; | |
| 4238 } | |
| 4239 } : | |
| 4240 function(parent, child) { | |
| 4241 originalRemoveChild.call(parent, child); | |
| 4242 }; | |
| 4243 | |
| 4244 Node.prototype = Object.create(EventTarget.prototype); | |
| 4245 mixin(Node.prototype, { | |
| 4246 appendChild: function(childWrapper) { | |
| 4247 return this.insertBefore(childWrapper, null); | |
| 4248 }, | |
| 4249 | |
| 4250 insertBefore: function(childWrapper, refWrapper) { | |
| 4251 assertIsNodeWrapper(childWrapper); | |
| 4252 | |
| 4253 var refNode; | |
| 4254 if (refWrapper) { | |
| 4255 if (isWrapper(refWrapper)) { | |
| 4256 refNode = unwrap(refWrapper); | |
| 4257 } else { | |
| 4258 refNode = refWrapper; | |
| 4259 refWrapper = wrap(refNode); | |
| 4260 } | |
| 4261 } else { | |
| 4262 refWrapper = null; | |
| 4263 refNode = null; | |
| 4264 } | |
| 4265 | |
| 4266 refWrapper && assert(refWrapper.parentNode === this); | |
| 4267 | |
| 4268 var nodes; | |
| 4269 var previousNode = | |
| 4270 refWrapper ? refWrapper.previousSibling : this.lastChild; | |
| 4271 | |
| 4272 var useNative = !this.invalidateShadowRenderer() && | |
| 4273 !invalidateParent(childWrapper); | |
| 4274 | |
| 4275 if (useNative) | |
| 4276 nodes = collectNodesNative(childWrapper); | |
| 4277 else | |
| 4278 nodes = collectNodes(childWrapper, this, previousNode, refWrapper); | |
| 4279 | |
| 4280 if (useNative) { | |
| 4281 ensureSameOwnerDocument(this, childWrapper); | |
| 4282 clearChildNodes(this); | |
| 4283 originalInsertBefore.call(unsafeUnwrap(this), unwrap(childWrapper), refN
ode); | |
| 4284 } else { | |
| 4285 if (!previousNode) | |
| 4286 this.firstChild_ = nodes[0]; | |
| 4287 if (!refWrapper) { | |
| 4288 this.lastChild_ = nodes[nodes.length - 1]; | |
| 4289 if (this.firstChild_ === undefined) | |
| 4290 this.firstChild_ = this.firstChild; | |
| 4291 } | |
| 4292 | |
| 4293 var parentNode = refNode ? refNode.parentNode : unsafeUnwrap(this); | |
| 4294 | |
| 4295 // insertBefore refWrapper no matter what the parent is? | |
| 4296 if (parentNode) { | |
| 4297 originalInsertBefore.call(parentNode, | |
| 4298 unwrapNodesForInsertion(this, nodes), refNode); | |
| 4299 } else { | |
| 4300 adoptNodesIfNeeded(this, nodes); | |
| 4301 } | |
| 4302 } | |
| 4303 | |
| 4304 enqueueMutation(this, 'childList', { | |
| 4305 addedNodes: nodes, | |
| 4306 nextSibling: refWrapper, | |
| 4307 previousSibling: previousNode | |
| 4308 }); | |
| 4309 | |
| 4310 nodesWereAdded(nodes, this); | |
| 4311 | |
| 4312 return childWrapper; | |
| 4313 }, | |
| 4314 | |
| 4315 removeChild: function(childWrapper) { | |
| 4316 assertIsNodeWrapper(childWrapper); | |
| 4317 if (childWrapper.parentNode !== this) { | |
| 4318 // IE has invalid DOM trees at times. | |
| 4319 var found = false; | |
| 4320 var childNodes = this.childNodes; | |
| 4321 for (var ieChild = this.firstChild; ieChild; | |
| 4322 ieChild = ieChild.nextSibling) { | |
| 4323 if (ieChild === childWrapper) { | |
| 4324 found = true; | |
| 4325 break; | |
| 4326 } | |
| 4327 } | |
| 4328 if (!found) { | |
| 4329 // TODO(arv): DOMException | |
| 4330 throw new Error('NotFoundError'); | |
| 4331 } | |
| 4332 } | |
| 4333 | |
| 4334 var childNode = unwrap(childWrapper); | |
| 4335 var childWrapperNextSibling = childWrapper.nextSibling; | |
| 4336 var childWrapperPreviousSibling = childWrapper.previousSibling; | |
| 4337 | |
| 4338 if (this.invalidateShadowRenderer()) { | |
| 4339 // We need to remove the real node from the DOM before updating the | |
| 4340 // pointers. This is so that that mutation event is dispatched before | |
| 4341 // the pointers have changed. | |
| 4342 var thisFirstChild = this.firstChild; | |
| 4343 var thisLastChild = this.lastChild; | |
| 4344 | |
| 4345 var parentNode = childNode.parentNode; | |
| 4346 if (parentNode) | |
| 4347 removeChildOriginalHelper(parentNode, childNode); | |
| 4348 | |
| 4349 if (thisFirstChild === childWrapper) | |
| 4350 this.firstChild_ = childWrapperNextSibling; | |
| 4351 if (thisLastChild === childWrapper) | |
| 4352 this.lastChild_ = childWrapperPreviousSibling; | |
| 4353 if (childWrapperPreviousSibling) | |
| 4354 childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling; | |
| 4355 if (childWrapperNextSibling) { | |
| 4356 childWrapperNextSibling.previousSibling_ = | |
| 4357 childWrapperPreviousSibling; | |
| 4358 } | |
| 4359 | |
| 4360 childWrapper.previousSibling_ = childWrapper.nextSibling_ = | |
| 4361 childWrapper.parentNode_ = undefined; | |
| 4362 } else { | |
| 4363 clearChildNodes(this); | |
| 4364 removeChildOriginalHelper(unsafeUnwrap(this), childNode); | |
| 4365 } | |
| 4366 | |
| 4367 if (!surpressMutations) { | |
| 4368 enqueueMutation(this, 'childList', { | |
| 4369 removedNodes: createOneElementNodeList(childWrapper), | |
| 4370 nextSibling: childWrapperNextSibling, | |
| 4371 previousSibling: childWrapperPreviousSibling | |
| 4372 }); | |
| 4373 } | |
| 4374 | |
| 4375 registerTransientObservers(this, childWrapper); | |
| 4376 | |
| 4377 return childWrapper; | |
| 4378 }, | |
| 4379 | |
| 4380 replaceChild: function(newChildWrapper, oldChildWrapper) { | |
| 4381 assertIsNodeWrapper(newChildWrapper); | |
| 4382 | |
| 4383 var oldChildNode; | |
| 4384 if (isWrapper(oldChildWrapper)) { | |
| 4385 oldChildNode = unwrap(oldChildWrapper); | |
| 4386 } else { | |
| 4387 oldChildNode = oldChildWrapper; | |
| 4388 oldChildWrapper = wrap(oldChildNode); | |
| 4389 } | |
| 4390 | |
| 4391 if (oldChildWrapper.parentNode !== this) { | |
| 4392 // TODO(arv): DOMException | |
| 4393 throw new Error('NotFoundError'); | |
| 4394 } | |
| 4395 | |
| 4396 var nextNode = oldChildWrapper.nextSibling; | |
| 4397 var previousNode = oldChildWrapper.previousSibling; | |
| 4398 var nodes; | |
| 4399 | |
| 4400 var useNative = !this.invalidateShadowRenderer() && | |
| 4401 !invalidateParent(newChildWrapper); | |
| 4402 | |
| 4403 if (useNative) { | |
| 4404 nodes = collectNodesNative(newChildWrapper); | |
| 4405 } else { | |
| 4406 if (nextNode === newChildWrapper) | |
| 4407 nextNode = newChildWrapper.nextSibling; | |
| 4408 nodes = collectNodes(newChildWrapper, this, previousNode, nextNode); | |
| 4409 } | |
| 4410 | |
| 4411 if (!useNative) { | |
| 4412 if (this.firstChild === oldChildWrapper) | |
| 4413 this.firstChild_ = nodes[0]; | |
| 4414 if (this.lastChild === oldChildWrapper) | |
| 4415 this.lastChild_ = nodes[nodes.length - 1]; | |
| 4416 | |
| 4417 oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = | |
| 4418 oldChildWrapper.parentNode_ = undefined; | |
| 4419 | |
| 4420 // replaceChild no matter what the parent is? | |
| 4421 if (oldChildNode.parentNode) { | |
| 4422 originalReplaceChild.call( | |
| 4423 oldChildNode.parentNode, | |
| 4424 unwrapNodesForInsertion(this, nodes), | |
| 4425 oldChildNode); | |
| 4426 } | |
| 4427 } else { | |
| 4428 ensureSameOwnerDocument(this, newChildWrapper); | |
| 4429 clearChildNodes(this); | |
| 4430 originalReplaceChild.call(unsafeUnwrap(this), unwrap(newChildWrapper), | |
| 4431 oldChildNode); | |
| 4432 } | |
| 4433 | |
| 4434 enqueueMutation(this, 'childList', { | |
| 4435 addedNodes: nodes, | |
| 4436 removedNodes: createOneElementNodeList(oldChildWrapper), | |
| 4437 nextSibling: nextNode, | |
| 4438 previousSibling: previousNode | |
| 4439 }); | |
| 4440 | |
| 4441 nodeWasRemoved(oldChildWrapper); | |
| 4442 nodesWereAdded(nodes, this); | |
| 4443 | |
| 4444 return oldChildWrapper; | |
| 4445 }, | |
| 4446 | |
| 4447 /** | |
| 4448 * Called after a node was inserted. Subclasses override this to invalidate | |
| 4449 * the renderer as needed. | |
| 4450 * @private | |
| 4451 */ | |
| 4452 nodeIsInserted_: function() { | |
| 4453 for (var child = this.firstChild; child; child = child.nextSibling) { | |
| 4454 child.nodeIsInserted_(); | |
| 4455 } | |
| 4456 }, | |
| 4457 | |
| 4458 hasChildNodes: function() { | |
| 4459 return this.firstChild !== null; | |
| 4460 }, | |
| 4461 | |
| 4462 /** @type {Node} */ | |
| 4463 get parentNode() { | |
| 4464 // If the parentNode has not been overridden, use the original parentNode. | |
| 4465 return this.parentNode_ !== undefined ? | |
| 4466 this.parentNode_ : wrap(unsafeUnwrap(this).parentNode); | |
| 4467 }, | |
| 4468 | |
| 4469 /** @type {Node} */ | |
| 4470 get firstChild() { | |
| 4471 return this.firstChild_ !== undefined ? | |
| 4472 this.firstChild_ : wrap(unsafeUnwrap(this).firstChild); | |
| 4473 }, | |
| 4474 | |
| 4475 /** @type {Node} */ | |
| 4476 get lastChild() { | |
| 4477 return this.lastChild_ !== undefined ? | |
| 4478 this.lastChild_ : wrap(unsafeUnwrap(this).lastChild); | |
| 4479 }, | |
| 4480 | |
| 4481 /** @type {Node} */ | |
| 4482 get nextSibling() { | |
| 4483 return this.nextSibling_ !== undefined ? | |
| 4484 this.nextSibling_ : wrap(unsafeUnwrap(this).nextSibling); | |
| 4485 }, | |
| 4486 | |
| 4487 /** @type {Node} */ | |
| 4488 get previousSibling() { | |
| 4489 return this.previousSibling_ !== undefined ? | |
| 4490 this.previousSibling_ : wrap(unsafeUnwrap(this).previousSibling); | |
| 4491 }, | |
| 4492 | |
| 4493 get parentElement() { | |
| 4494 var p = this.parentNode; | |
| 4495 while (p && p.nodeType !== Node.ELEMENT_NODE) { | |
| 4496 p = p.parentNode; | |
| 4497 } | |
| 4498 return p; | |
| 4499 }, | |
| 4500 | |
| 4501 get textContent() { | |
| 4502 // TODO(arv): This should fallback to unsafeUnwrap(this).textContent if th
ere | |
| 4503 // are no shadow trees below or above the context node. | |
| 4504 var s = ''; | |
| 4505 for (var child = this.firstChild; child; child = child.nextSibling) { | |
| 4506 if (child.nodeType != Node.COMMENT_NODE) { | |
| 4507 s += child.textContent; | |
| 4508 } | |
| 4509 } | |
| 4510 return s; | |
| 4511 }, | |
| 4512 set textContent(textContent) { | |
| 4513 if (textContent == null) textContent = ''; | |
| 4514 var removedNodes = snapshotNodeList(this.childNodes); | |
| 4515 | |
| 4516 if (this.invalidateShadowRenderer()) { | |
| 4517 removeAllChildNodes(this); | |
| 4518 if (textContent !== '') { | |
| 4519 var textNode = unsafeUnwrap(this).ownerDocument.createTextNode(textCon
tent); | |
| 4520 this.appendChild(textNode); | |
| 4521 } | |
| 4522 } else { | |
| 4523 clearChildNodes(this); | |
| 4524 unsafeUnwrap(this).textContent = textContent; | |
| 4525 } | |
| 4526 | |
| 4527 var addedNodes = snapshotNodeList(this.childNodes); | |
| 4528 | |
| 4529 enqueueMutation(this, 'childList', { | |
| 4530 addedNodes: addedNodes, | |
| 4531 removedNodes: removedNodes | |
| 4532 }); | |
| 4533 | |
| 4534 nodesWereRemoved(removedNodes); | |
| 4535 nodesWereAdded(addedNodes, this); | |
| 4536 }, | |
| 4537 | |
| 4538 get childNodes() { | |
| 4539 var wrapperList = new NodeList(); | |
| 4540 var i = 0; | |
| 4541 for (var child = this.firstChild; child; child = child.nextSibling) { | |
| 4542 wrapperList[i++] = child; | |
| 4543 } | |
| 4544 wrapperList.length = i; | |
| 4545 return wrapperList; | |
| 4546 }, | |
| 4547 | |
| 4548 cloneNode: function(deep) { | |
| 4549 return cloneNode(this, deep); | |
| 4550 }, | |
| 4551 | |
| 4552 contains: function(child) { | |
| 4553 return contains(this, wrapIfNeeded(child)); | |
| 4554 }, | |
| 4555 | |
| 4556 compareDocumentPosition: function(otherNode) { | |
| 4557 // This only wraps, it therefore only operates on the composed DOM and not | |
| 4558 // the logical DOM. | |
| 4559 return originalCompareDocumentPosition.call(unsafeUnwrap(this), | |
| 4560 unwrapIfNeeded(otherNode)); | |
| 4561 }, | |
| 4562 | |
| 4563 normalize: function() { | |
| 4564 var nodes = snapshotNodeList(this.childNodes); | |
| 4565 var remNodes = []; | |
| 4566 var s = ''; | |
| 4567 var modNode; | |
| 4568 | |
| 4569 for (var i = 0, n; i < nodes.length; i++) { | |
| 4570 n = nodes[i]; | |
| 4571 if (n.nodeType === Node.TEXT_NODE) { | |
| 4572 if (!modNode && !n.data.length) | |
| 4573 this.removeNode(n); | |
| 4574 else if (!modNode) | |
| 4575 modNode = n; | |
| 4576 else { | |
| 4577 s += n.data; | |
| 4578 remNodes.push(n); | |
| 4579 } | |
| 4580 } else { | |
| 4581 if (modNode && remNodes.length) { | |
| 4582 modNode.data += s; | |
| 4583 cleanupNodes(remNodes); | |
| 4584 } | |
| 4585 remNodes = []; | |
| 4586 s = ''; | |
| 4587 modNode = null; | |
| 4588 if (n.childNodes.length) | |
| 4589 n.normalize(); | |
| 4590 } | |
| 4591 } | |
| 4592 | |
| 4593 // handle case where >1 text nodes are the last children | |
| 4594 if (modNode && remNodes.length) { | |
| 4595 modNode.data += s; | |
| 4596 cleanupNodes(remNodes); | |
| 4597 } | |
| 4598 } | |
| 4599 }); | |
| 4600 | |
| 4601 defineWrapGetter(Node, 'ownerDocument'); | |
| 4602 | |
| 4603 // We use a DocumentFragment as a base and then delete the properties of | |
| 4604 // DocumentFragment.prototype from the wrapper Node. Since delete makes | |
| 4605 // objects slow in some JS engines we recreate the prototype object. | |
| 4606 registerWrapper(OriginalNode, Node, document.createDocumentFragment()); | |
| 4607 delete Node.prototype.querySelector; | |
| 4608 delete Node.prototype.querySelectorAll; | |
| 4609 Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); | |
| 4610 | |
| 4611 scope.cloneNode = cloneNode; | |
| 4612 scope.nodeWasAdded = nodeWasAdded; | |
| 4613 scope.nodeWasRemoved = nodeWasRemoved; | |
| 4614 scope.nodesWereAdded = nodesWereAdded; | |
| 4615 scope.nodesWereRemoved = nodesWereRemoved; | |
| 4616 scope.originalInsertBefore = originalInsertBefore; | |
| 4617 scope.originalRemoveChild = originalRemoveChild; | |
| 4618 scope.snapshotNodeList = snapshotNodeList; | |
| 4619 scope.wrappers.Node = Node; | |
| 4620 | |
| 4621 })(window.ShadowDOMPolyfill); | |
| 4622 | |
| 4623 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 4624 // Use of this source code is governed by a BSD-style | |
| 4625 // license that can be found in the LICENSE file. | |
| 4626 | |
| 4627 (function(scope) { | |
| 4628 'use strict'; | |
| 4629 | |
| 4630 var HTMLCollection = scope.wrappers.HTMLCollection; | |
| 4631 var NodeList = scope.wrappers.NodeList; | |
| 4632 var getTreeScope = scope.getTreeScope; | |
| 4633 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 4634 var wrap = scope.wrap; | |
| 4635 | |
| 4636 var originalDocumentQuerySelector = document.querySelector; | |
| 4637 var originalElementQuerySelector = document.documentElement.querySelector; | |
| 4638 | |
| 4639 var originalDocumentQuerySelectorAll = document.querySelectorAll; | |
| 4640 var originalElementQuerySelectorAll = document.documentElement.querySelectorAl
l; | |
| 4641 | |
| 4642 var originalDocumentGetElementsByTagName = document.getElementsByTagName; | |
| 4643 var originalElementGetElementsByTagName = document.documentElement.getElements
ByTagName; | |
| 4644 | |
| 4645 var originalDocumentGetElementsByTagNameNS = document.getElementsByTagNameNS; | |
| 4646 var originalElementGetElementsByTagNameNS = document.documentElement.getElemen
tsByTagNameNS; | |
| 4647 | |
| 4648 var OriginalElement = window.Element; | |
| 4649 var OriginalDocument = window.HTMLDocument || window.Document; | |
| 4650 | |
| 4651 function filterNodeList(list, index, result, deep) { | |
| 4652 var wrappedItem = null; | |
| 4653 var root = null; | |
| 4654 for (var i = 0, length = list.length; i < length; i++) { | |
| 4655 wrappedItem = wrap(list[i]); | |
| 4656 if (!deep && (root = getTreeScope(wrappedItem).root)) { | |
| 4657 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4658 continue; | |
| 4659 } | |
| 4660 } | |
| 4661 result[index++] = wrappedItem; | |
| 4662 } | |
| 4663 | |
| 4664 return index; | |
| 4665 } | |
| 4666 | |
| 4667 function shimSelector(selector) { | |
| 4668 return String(selector).replace(/\/deep\//g, ' '); | |
| 4669 } | |
| 4670 | |
| 4671 function findOne(node, selector) { | |
| 4672 var m, el = node.firstElementChild; | |
| 4673 while (el) { | |
| 4674 if (el.matches(selector)) | |
| 4675 return el; | |
| 4676 m = findOne(el, selector); | |
| 4677 if (m) | |
| 4678 return m; | |
| 4679 el = el.nextElementSibling; | |
| 4680 } | |
| 4681 return null; | |
| 4682 } | |
| 4683 | |
| 4684 function matchesSelector(el, selector) { | |
| 4685 return el.matches(selector); | |
| 4686 } | |
| 4687 | |
| 4688 var XHTML_NS = 'http://www.w3.org/1999/xhtml'; | |
| 4689 | |
| 4690 function matchesTagName(el, localName, localNameLowerCase) { | |
| 4691 var ln = el.localName; | |
| 4692 return ln === localName || | |
| 4693 ln === localNameLowerCase && el.namespaceURI === XHTML_NS; | |
| 4694 } | |
| 4695 | |
| 4696 function matchesEveryThing() { | |
| 4697 return true; | |
| 4698 } | |
| 4699 | |
| 4700 function matchesLocalNameOnly(el, ns, localName) { | |
| 4701 return el.localName === localName; | |
| 4702 } | |
| 4703 | |
| 4704 function matchesNameSpace(el, ns) { | |
| 4705 return el.namespaceURI === ns; | |
| 4706 } | |
| 4707 | |
| 4708 function matchesLocalNameNS(el, ns, localName) { | |
| 4709 return el.namespaceURI === ns && el.localName === localName; | |
| 4710 } | |
| 4711 | |
| 4712 function findElements(node, index, result, p, arg0, arg1) { | |
| 4713 var el = node.firstElementChild; | |
| 4714 while (el) { | |
| 4715 if (p(el, arg0, arg1)) | |
| 4716 result[index++] = el; | |
| 4717 index = findElements(el, index, result, p, arg0, arg1); | |
| 4718 el = el.nextElementSibling; | |
| 4719 } | |
| 4720 return index; | |
| 4721 } | |
| 4722 | |
| 4723 // find and findAll will only match Simple Selectors, | |
| 4724 // Structural Pseudo Classes are not guarenteed to be correct | |
| 4725 // http://www.w3.org/TR/css3-selectors/#simple-selectors | |
| 4726 | |
| 4727 function querySelectorAllFiltered(p, index, result, selector, deep) { | |
| 4728 var target = unsafeUnwrap(this); | |
| 4729 var list; | |
| 4730 var root = getTreeScope(this).root; | |
| 4731 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4732 // We are in the shadow tree and the logical tree is | |
| 4733 // going to be disconnected so we do a manual tree traversal | |
| 4734 return findElements(this, index, result, p, selector, null); | |
| 4735 } else if (target instanceof OriginalElement) { | |
| 4736 list = originalElementQuerySelectorAll.call(target, selector); | |
| 4737 } else if (target instanceof OriginalDocument) { | |
| 4738 list = originalDocumentQuerySelectorAll.call(target, selector); | |
| 4739 } else { | |
| 4740 // When we get a ShadowRoot the logical tree is going to be disconnected | |
| 4741 // so we do a manual tree traversal | |
| 4742 return findElements(this, index, result, p, selector, null); | |
| 4743 } | |
| 4744 | |
| 4745 return filterNodeList(list, index, result, deep); | |
| 4746 } | |
| 4747 | |
| 4748 var SelectorsInterface = { | |
| 4749 querySelector: function(selector) { | |
| 4750 var shimmed = shimSelector(selector); | |
| 4751 var deep = shimmed !== selector; | |
| 4752 selector = shimmed; | |
| 4753 | |
| 4754 var target = unsafeUnwrap(this); | |
| 4755 var wrappedItem; | |
| 4756 var root = getTreeScope(this).root; | |
| 4757 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4758 // We are in the shadow tree and the logical tree is | |
| 4759 // going to be disconnected so we do a manual tree traversal | |
| 4760 return findOne(this, selector); | |
| 4761 } else if (target instanceof OriginalElement) { | |
| 4762 wrappedItem = wrap(originalElementQuerySelector.call(target, selector)); | |
| 4763 } else if (target instanceof OriginalDocument) { | |
| 4764 wrappedItem = wrap(originalDocumentQuerySelector.call(target, selector))
; | |
| 4765 } else { | |
| 4766 // When we get a ShadowRoot the logical tree is going to be disconnected | |
| 4767 // so we do a manual tree traversal | |
| 4768 return findOne(this, selector); | |
| 4769 } | |
| 4770 | |
| 4771 if (!wrappedItem) { | |
| 4772 // When the original query returns nothing | |
| 4773 // we return nothing (to be consistent with the other wrapped calls) | |
| 4774 return wrappedItem; | |
| 4775 } else if (!deep && (root = getTreeScope(wrappedItem).root)) { | |
| 4776 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4777 // When the original query returns an element in the ShadowDOM | |
| 4778 // we must do a manual tree traversal | |
| 4779 return findOne(this, selector); | |
| 4780 } | |
| 4781 } | |
| 4782 | |
| 4783 return wrappedItem; | |
| 4784 }, | |
| 4785 querySelectorAll: function(selector) { | |
| 4786 var shimmed = shimSelector(selector); | |
| 4787 var deep = shimmed !== selector; | |
| 4788 selector = shimmed; | |
| 4789 | |
| 4790 var result = new NodeList(); | |
| 4791 | |
| 4792 result.length = querySelectorAllFiltered.call(this, | |
| 4793 matchesSelector, | |
| 4794 0, | |
| 4795 result, | |
| 4796 selector, | |
| 4797 deep); | |
| 4798 | |
| 4799 return result; | |
| 4800 } | |
| 4801 }; | |
| 4802 | |
| 4803 function getElementsByTagNameFiltered(p, index, result, localName, | |
| 4804 lowercase) { | |
| 4805 var target = unsafeUnwrap(this); | |
| 4806 var list; | |
| 4807 var root = getTreeScope(this).root; | |
| 4808 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4809 // We are in the shadow tree and the logical tree is | |
| 4810 // going to be disconnected so we do a manual tree traversal | |
| 4811 return findElements(this, index, result, p, localName, lowercase); | |
| 4812 } else if (target instanceof OriginalElement) { | |
| 4813 list = originalElementGetElementsByTagName.call(target, localName, | |
| 4814 lowercase); | |
| 4815 } else if (target instanceof OriginalDocument) { | |
| 4816 list = originalDocumentGetElementsByTagName.call(target, localName, | |
| 4817 lowercase); | |
| 4818 } else { | |
| 4819 // When we get a ShadowRoot the logical tree is going to be disconnected | |
| 4820 // so we do a manual tree traversal | |
| 4821 return findElements(this, index, result, p, localName, lowercase); | |
| 4822 } | |
| 4823 | |
| 4824 return filterNodeList(list, index, result, false); | |
| 4825 } | |
| 4826 | |
| 4827 function getElementsByTagNameNSFiltered(p, index, result, ns, localName) { | |
| 4828 var target = unsafeUnwrap(this); | |
| 4829 var list; | |
| 4830 var root = getTreeScope(this).root; | |
| 4831 if (root instanceof scope.wrappers.ShadowRoot) { | |
| 4832 // We are in the shadow tree and the logical tree is | |
| 4833 // going to be disconnected so we do a manual tree traversal | |
| 4834 return findElements(this, index, result, p, ns, localName); | |
| 4835 } else if (target instanceof OriginalElement) { | |
| 4836 list = originalElementGetElementsByTagNameNS.call(target, ns, localName); | |
| 4837 } else if (target instanceof OriginalDocument) { | |
| 4838 list = originalDocumentGetElementsByTagNameNS.call(target, ns, localName); | |
| 4839 } else { | |
| 4840 // When we get a ShadowRoot the logical tree is going to be disconnected | |
| 4841 // so we do a manual tree traversal | |
| 4842 return findElements(this, index, result, p, ns, localName); | |
| 4843 } | |
| 4844 | |
| 4845 return filterNodeList(list, index, result, false); | |
| 4846 } | |
| 4847 | |
| 4848 var GetElementsByInterface = { | |
| 4849 getElementsByTagName: function(localName) { | |
| 4850 var result = new HTMLCollection(); | |
| 4851 var match = localName === '*' ? matchesEveryThing : matchesTagName; | |
| 4852 | |
| 4853 result.length = getElementsByTagNameFiltered.call(this, | |
| 4854 match, | |
| 4855 0, | |
| 4856 result, | |
| 4857 localName, | |
| 4858 localName.toLowerCase()); | |
| 4859 | |
| 4860 return result; | |
| 4861 }, | |
| 4862 | |
| 4863 getElementsByClassName: function(className) { | |
| 4864 // TODO(arv): Check className? | |
| 4865 return this.querySelectorAll('.' + className); | |
| 4866 }, | |
| 4867 | |
| 4868 getElementsByTagNameNS: function(ns, localName) { | |
| 4869 var result = new HTMLCollection(); | |
| 4870 var match = null; | |
| 4871 | |
| 4872 if (ns === '*') { | |
| 4873 match = localName === '*' ? matchesEveryThing : matchesLocalNameOnly; | |
| 4874 } else { | |
| 4875 match = localName === '*' ? matchesNameSpace : matchesLocalNameNS; | |
| 4876 } | |
| 4877 | |
| 4878 result.length = getElementsByTagNameNSFiltered.call(this, | |
| 4879 match, | |
| 4880 0, | |
| 4881 result, | |
| 4882 ns || null, | |
| 4883 localName); | |
| 4884 | |
| 4885 return result; | |
| 4886 } | |
| 4887 }; | |
| 4888 | |
| 4889 scope.GetElementsByInterface = GetElementsByInterface; | |
| 4890 scope.SelectorsInterface = SelectorsInterface; | |
| 4891 | |
| 4892 })(window.ShadowDOMPolyfill); | |
| 4893 | |
| 4894 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 4895 // Use of this source code is goverened by a BSD-style | |
| 4896 // license that can be found in the LICENSE file. | |
| 4897 | |
| 4898 (function(scope) { | |
| 4899 'use strict'; | |
| 4900 | |
| 4901 var NodeList = scope.wrappers.NodeList; | |
| 4902 | |
| 4903 function forwardElement(node) { | |
| 4904 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
| 4905 node = node.nextSibling; | |
| 4906 } | |
| 4907 return node; | |
| 4908 } | |
| 4909 | |
| 4910 function backwardsElement(node) { | |
| 4911 while (node && node.nodeType !== Node.ELEMENT_NODE) { | |
| 4912 node = node.previousSibling; | |
| 4913 } | |
| 4914 return node; | |
| 4915 } | |
| 4916 | |
| 4917 var ParentNodeInterface = { | |
| 4918 get firstElementChild() { | |
| 4919 return forwardElement(this.firstChild); | |
| 4920 }, | |
| 4921 | |
| 4922 get lastElementChild() { | |
| 4923 return backwardsElement(this.lastChild); | |
| 4924 }, | |
| 4925 | |
| 4926 get childElementCount() { | |
| 4927 var count = 0; | |
| 4928 for (var child = this.firstElementChild; | |
| 4929 child; | |
| 4930 child = child.nextElementSibling) { | |
| 4931 count++; | |
| 4932 } | |
| 4933 return count; | |
| 4934 }, | |
| 4935 | |
| 4936 get children() { | |
| 4937 var wrapperList = new NodeList(); | |
| 4938 var i = 0; | |
| 4939 for (var child = this.firstElementChild; | |
| 4940 child; | |
| 4941 child = child.nextElementSibling) { | |
| 4942 wrapperList[i++] = child; | |
| 4943 } | |
| 4944 wrapperList.length = i; | |
| 4945 return wrapperList; | |
| 4946 }, | |
| 4947 | |
| 4948 remove: function() { | |
| 4949 var p = this.parentNode; | |
| 4950 if (p) | |
| 4951 p.removeChild(this); | |
| 4952 } | |
| 4953 }; | |
| 4954 | |
| 4955 var ChildNodeInterface = { | |
| 4956 get nextElementSibling() { | |
| 4957 return forwardElement(this.nextSibling); | |
| 4958 }, | |
| 4959 | |
| 4960 get previousElementSibling() { | |
| 4961 return backwardsElement(this.previousSibling); | |
| 4962 } | |
| 4963 }; | |
| 4964 | |
| 4965 scope.ChildNodeInterface = ChildNodeInterface; | |
| 4966 scope.ParentNodeInterface = ParentNodeInterface; | |
| 4967 | |
| 4968 })(window.ShadowDOMPolyfill); | |
| 4969 | |
| 4970 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 4971 // Use of this source code is goverened by a BSD-style | |
| 4972 // license that can be found in the LICENSE file. | |
| 4973 | |
| 4974 (function(scope) { | |
| 4975 'use strict'; | |
| 4976 | |
| 4977 var ChildNodeInterface = scope.ChildNodeInterface; | |
| 4978 var Node = scope.wrappers.Node; | |
| 4979 var enqueueMutation = scope.enqueueMutation; | |
| 4980 var mixin = scope.mixin; | |
| 4981 var registerWrapper = scope.registerWrapper; | |
| 4982 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 4983 | |
| 4984 var OriginalCharacterData = window.CharacterData; | |
| 4985 | |
| 4986 function CharacterData(node) { | |
| 4987 Node.call(this, node); | |
| 4988 } | |
| 4989 CharacterData.prototype = Object.create(Node.prototype); | |
| 4990 mixin(CharacterData.prototype, { | |
| 4991 get textContent() { | |
| 4992 return this.data; | |
| 4993 }, | |
| 4994 set textContent(value) { | |
| 4995 this.data = value; | |
| 4996 }, | |
| 4997 get data() { | |
| 4998 return unsafeUnwrap(this).data; | |
| 4999 }, | |
| 5000 set data(value) { | |
| 5001 var oldValue = unsafeUnwrap(this).data; | |
| 5002 enqueueMutation(this, 'characterData', { | |
| 5003 oldValue: oldValue | |
| 5004 }); | |
| 5005 unsafeUnwrap(this).data = value; | |
| 5006 } | |
| 5007 }); | |
| 5008 | |
| 5009 mixin(CharacterData.prototype, ChildNodeInterface); | |
| 5010 | |
| 5011 registerWrapper(OriginalCharacterData, CharacterData, | |
| 5012 document.createTextNode('')); | |
| 5013 | |
| 5014 scope.wrappers.CharacterData = CharacterData; | |
| 5015 })(window.ShadowDOMPolyfill); | |
| 5016 | |
| 5017 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 5018 // Use of this source code is goverened by a BSD-style | |
| 5019 // license that can be found in the LICENSE file. | |
| 5020 | |
| 5021 (function(scope) { | |
| 5022 'use strict'; | |
| 5023 | |
| 5024 var CharacterData = scope.wrappers.CharacterData; | |
| 5025 var enqueueMutation = scope.enqueueMutation; | |
| 5026 var mixin = scope.mixin; | |
| 5027 var registerWrapper = scope.registerWrapper; | |
| 5028 | |
| 5029 function toUInt32(x) { | |
| 5030 return x >>> 0; | |
| 5031 } | |
| 5032 | |
| 5033 var OriginalText = window.Text; | |
| 5034 | |
| 5035 function Text(node) { | |
| 5036 CharacterData.call(this, node); | |
| 5037 } | |
| 5038 Text.prototype = Object.create(CharacterData.prototype); | |
| 5039 mixin(Text.prototype, { | |
| 5040 splitText: function(offset) { | |
| 5041 offset = toUInt32(offset); | |
| 5042 var s = this.data; | |
| 5043 if (offset > s.length) | |
| 5044 throw new Error('IndexSizeError'); | |
| 5045 var head = s.slice(0, offset); | |
| 5046 var tail = s.slice(offset); | |
| 5047 this.data = head; | |
| 5048 var newTextNode = this.ownerDocument.createTextNode(tail); | |
| 5049 if (this.parentNode) | |
| 5050 this.parentNode.insertBefore(newTextNode, this.nextSibling); | |
| 5051 return newTextNode; | |
| 5052 } | |
| 5053 }); | |
| 5054 | |
| 5055 registerWrapper(OriginalText, Text, document.createTextNode('')); | |
| 5056 | |
| 5057 scope.wrappers.Text = Text; | |
| 5058 })(window.ShadowDOMPolyfill); | |
| 5059 | |
| 5060 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 5061 // Use of this source code is goverened by a BSD-style | |
| 5062 // license that can be found in the LICENSE file. | |
| 5063 | |
| 5064 (function(scope) { | |
| 5065 'use strict'; | |
| 5066 | |
| 5067 var setWrapper = scope.setWrapper; | |
| 5068 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 5069 | |
| 5070 function invalidateClass(el) { | |
| 5071 scope.invalidateRendererBasedOnAttribute(el, 'class'); | |
| 5072 } | |
| 5073 | |
| 5074 function DOMTokenList(impl, ownerElement) { | |
| 5075 setWrapper(impl, this); | |
| 5076 this.ownerElement_ = ownerElement; | |
| 5077 } | |
| 5078 | |
| 5079 DOMTokenList.prototype = { | |
| 5080 constructor: DOMTokenList, | |
| 5081 get length() { | |
| 5082 return unsafeUnwrap(this).length; | |
| 5083 }, | |
| 5084 item: function(index) { | |
| 5085 return unsafeUnwrap(this).item(index); | |
| 5086 }, | |
| 5087 contains: function(token) { | |
| 5088 return unsafeUnwrap(this).contains(token); | |
| 5089 }, | |
| 5090 add: function() { | |
| 5091 unsafeUnwrap(this).add.apply(unsafeUnwrap(this), arguments); | |
| 5092 invalidateClass(this.ownerElement_); | |
| 5093 }, | |
| 5094 remove: function() { | |
| 5095 unsafeUnwrap(this).remove.apply(unsafeUnwrap(this), arguments); | |
| 5096 invalidateClass(this.ownerElement_); | |
| 5097 }, | |
| 5098 toggle: function(token) { | |
| 5099 var rv = unsafeUnwrap(this).toggle.apply(unsafeUnwrap(this), arguments); | |
| 5100 invalidateClass(this.ownerElement_); | |
| 5101 return rv; | |
| 5102 }, | |
| 5103 toString: function() { | |
| 5104 return unsafeUnwrap(this).toString(); | |
| 5105 } | |
| 5106 }; | |
| 5107 | |
| 5108 scope.wrappers.DOMTokenList = DOMTokenList; | |
| 5109 })(window.ShadowDOMPolyfill); | |
| 5110 | |
| 5111 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5112 // Use of this source code is goverened by a BSD-style | |
| 5113 // license that can be found in the LICENSE file. | |
| 5114 | |
| 5115 (function(scope) { | |
| 5116 'use strict'; | |
| 5117 | |
| 5118 var ChildNodeInterface = scope.ChildNodeInterface; | |
| 5119 var GetElementsByInterface = scope.GetElementsByInterface; | |
| 5120 var Node = scope.wrappers.Node; | |
| 5121 var DOMTokenList = scope.wrappers.DOMTokenList; | |
| 5122 var ParentNodeInterface = scope.ParentNodeInterface; | |
| 5123 var SelectorsInterface = scope.SelectorsInterface; | |
| 5124 var addWrapNodeListMethod = scope.addWrapNodeListMethod; | |
| 5125 var enqueueMutation = scope.enqueueMutation; | |
| 5126 var mixin = scope.mixin; | |
| 5127 var oneOf = scope.oneOf; | |
| 5128 var registerWrapper = scope.registerWrapper; | |
| 5129 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 5130 var wrappers = scope.wrappers; | |
| 5131 | |
| 5132 var OriginalElement = window.Element; | |
| 5133 | |
| 5134 var matchesNames = [ | |
| 5135 'matches', // needs to come first. | |
| 5136 'mozMatchesSelector', | |
| 5137 'msMatchesSelector', | |
| 5138 'webkitMatchesSelector', | |
| 5139 ].filter(function(name) { | |
| 5140 return OriginalElement.prototype[name]; | |
| 5141 }); | |
| 5142 | |
| 5143 var matchesName = matchesNames[0]; | |
| 5144 | |
| 5145 var originalMatches = OriginalElement.prototype[matchesName]; | |
| 5146 | |
| 5147 function invalidateRendererBasedOnAttribute(element, name) { | |
| 5148 // Only invalidate if parent node is a shadow host. | |
| 5149 var p = element.parentNode; | |
| 5150 if (!p || !p.shadowRoot) | |
| 5151 return; | |
| 5152 | |
| 5153 var renderer = scope.getRendererForHost(p); | |
| 5154 if (renderer.dependsOnAttribute(name)) | |
| 5155 renderer.invalidate(); | |
| 5156 } | |
| 5157 | |
| 5158 function enqueAttributeChange(element, name, oldValue) { | |
| 5159 // This is not fully spec compliant. We should use localName (which might | |
| 5160 // have a different case than name) and the namespace (which requires us | |
| 5161 // to get the Attr object). | |
| 5162 enqueueMutation(element, 'attributes', { | |
| 5163 name: name, | |
| 5164 namespace: null, | |
| 5165 oldValue: oldValue | |
| 5166 }); | |
| 5167 } | |
| 5168 | |
| 5169 var classListTable = new WeakMap(); | |
| 5170 | |
| 5171 function Element(node) { | |
| 5172 Node.call(this, node); | |
| 5173 } | |
| 5174 Element.prototype = Object.create(Node.prototype); | |
| 5175 mixin(Element.prototype, { | |
| 5176 createShadowRoot: function() { | |
| 5177 var newShadowRoot = new wrappers.ShadowRoot(this); | |
| 5178 unsafeUnwrap(this).polymerShadowRoot_ = newShadowRoot; | |
| 5179 | |
| 5180 var renderer = scope.getRendererForHost(this); | |
| 5181 renderer.invalidate(); | |
| 5182 | |
| 5183 return newShadowRoot; | |
| 5184 }, | |
| 5185 | |
| 5186 get shadowRoot() { | |
| 5187 return unsafeUnwrap(this).polymerShadowRoot_ || null; | |
| 5188 }, | |
| 5189 | |
| 5190 // getDestinationInsertionPoints added in ShadowRenderer.js | |
| 5191 | |
| 5192 setAttribute: function(name, value) { | |
| 5193 var oldValue = unsafeUnwrap(this).getAttribute(name); | |
| 5194 unsafeUnwrap(this).setAttribute(name, value); | |
| 5195 enqueAttributeChange(this, name, oldValue); | |
| 5196 invalidateRendererBasedOnAttribute(this, name); | |
| 5197 }, | |
| 5198 | |
| 5199 removeAttribute: function(name) { | |
| 5200 var oldValue = unsafeUnwrap(this).getAttribute(name); | |
| 5201 unsafeUnwrap(this).removeAttribute(name); | |
| 5202 enqueAttributeChange(this, name, oldValue); | |
| 5203 invalidateRendererBasedOnAttribute(this, name); | |
| 5204 }, | |
| 5205 | |
| 5206 matches: function(selector) { | |
| 5207 return originalMatches.call(unsafeUnwrap(this), selector); | |
| 5208 }, | |
| 5209 | |
| 5210 get classList() { | |
| 5211 var list = classListTable.get(this); | |
| 5212 if (!list) { | |
| 5213 classListTable.set(this, | |
| 5214 list = new DOMTokenList(unsafeUnwrap(this).classList, this)); | |
| 5215 } | |
| 5216 return list; | |
| 5217 }, | |
| 5218 | |
| 5219 get className() { | |
| 5220 return unsafeUnwrap(this).className; | |
| 5221 }, | |
| 5222 | |
| 5223 set className(v) { | |
| 5224 this.setAttribute('class', v); | |
| 5225 }, | |
| 5226 | |
| 5227 get id() { | |
| 5228 return unsafeUnwrap(this).id; | |
| 5229 }, | |
| 5230 | |
| 5231 set id(v) { | |
| 5232 this.setAttribute('id', v); | |
| 5233 } | |
| 5234 }); | |
| 5235 | |
| 5236 matchesNames.forEach(function(name) { | |
| 5237 if (name !== 'matches') { | |
| 5238 Element.prototype[name] = function(selector) { | |
| 5239 return this.matches(selector); | |
| 5240 }; | |
| 5241 } | |
| 5242 }); | |
| 5243 | |
| 5244 if (OriginalElement.prototype.webkitCreateShadowRoot) { | |
| 5245 Element.prototype.webkitCreateShadowRoot = | |
| 5246 Element.prototype.createShadowRoot; | |
| 5247 } | |
| 5248 | |
| 5249 mixin(Element.prototype, ChildNodeInterface); | |
| 5250 mixin(Element.prototype, GetElementsByInterface); | |
| 5251 mixin(Element.prototype, ParentNodeInterface); | |
| 5252 mixin(Element.prototype, SelectorsInterface); | |
| 5253 | |
| 5254 registerWrapper(OriginalElement, Element, | |
| 5255 document.createElementNS(null, 'x')); | |
| 5256 | |
| 5257 scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute; | |
| 5258 scope.matchesNames = matchesNames; | |
| 5259 scope.wrappers.Element = Element; | |
| 5260 })(window.ShadowDOMPolyfill); | |
| 5261 | |
| 5262 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5263 // Use of this source code is goverened by a BSD-style | |
| 5264 // license that can be found in the LICENSE file. | |
| 5265 | |
| 5266 (function(scope) { | |
| 5267 'use strict'; | |
| 5268 | |
| 5269 var Element = scope.wrappers.Element; | |
| 5270 var defineGetter = scope.defineGetter; | |
| 5271 var enqueueMutation = scope.enqueueMutation; | |
| 5272 var mixin = scope.mixin; | |
| 5273 var nodesWereAdded = scope.nodesWereAdded; | |
| 5274 var nodesWereRemoved = scope.nodesWereRemoved; | |
| 5275 var registerWrapper = scope.registerWrapper; | |
| 5276 var snapshotNodeList = scope.snapshotNodeList; | |
| 5277 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 5278 var unwrap = scope.unwrap; | |
| 5279 var wrap = scope.wrap; | |
| 5280 var wrappers = scope.wrappers; | |
| 5281 | |
| 5282 ///////////////////////////////////////////////////////////////////////////// | |
| 5283 // innerHTML and outerHTML | |
| 5284 | |
| 5285 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#es
capingString | |
| 5286 var escapeAttrRegExp = /[&\u00A0"]/g; | |
| 5287 var escapeDataRegExp = /[&\u00A0<>]/g; | |
| 5288 | |
| 5289 function escapeReplace(c) { | |
| 5290 switch (c) { | |
| 5291 case '&': | |
| 5292 return '&'; | |
| 5293 case '<': | |
| 5294 return '<'; | |
| 5295 case '>': | |
| 5296 return '>'; | |
| 5297 case '"': | |
| 5298 return '"' | |
| 5299 case '\u00A0': | |
| 5300 return ' '; | |
| 5301 } | |
| 5302 } | |
| 5303 | |
| 5304 function escapeAttr(s) { | |
| 5305 return s.replace(escapeAttrRegExp, escapeReplace); | |
| 5306 } | |
| 5307 | |
| 5308 function escapeData(s) { | |
| 5309 return s.replace(escapeDataRegExp, escapeReplace); | |
| 5310 } | |
| 5311 | |
| 5312 function makeSet(arr) { | |
| 5313 var set = {}; | |
| 5314 for (var i = 0; i < arr.length; i++) { | |
| 5315 set[arr[i]] = true; | |
| 5316 } | |
| 5317 return set; | |
| 5318 } | |
| 5319 | |
| 5320 // http://www.whatwg.org/specs/web-apps/current-work/#void-elements | |
| 5321 var voidElements = makeSet([ | |
| 5322 'area', | |
| 5323 'base', | |
| 5324 'br', | |
| 5325 'col', | |
| 5326 'command', | |
| 5327 'embed', | |
| 5328 'hr', | |
| 5329 'img', | |
| 5330 'input', | |
| 5331 'keygen', | |
| 5332 'link', | |
| 5333 'meta', | |
| 5334 'param', | |
| 5335 'source', | |
| 5336 'track', | |
| 5337 'wbr' | |
| 5338 ]); | |
| 5339 | |
| 5340 var plaintextParents = makeSet([ | |
| 5341 'style', | |
| 5342 'script', | |
| 5343 'xmp', | |
| 5344 'iframe', | |
| 5345 'noembed', | |
| 5346 'noframes', | |
| 5347 'plaintext', | |
| 5348 'noscript' | |
| 5349 ]); | |
| 5350 | |
| 5351 function getOuterHTML(node, parentNode) { | |
| 5352 switch (node.nodeType) { | |
| 5353 case Node.ELEMENT_NODE: | |
| 5354 var tagName = node.tagName.toLowerCase(); | |
| 5355 var s = '<' + tagName; | |
| 5356 var attrs = node.attributes; | |
| 5357 for (var i = 0, attr; attr = attrs[i]; i++) { | |
| 5358 s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"'; | |
| 5359 } | |
| 5360 s += '>'; | |
| 5361 if (voidElements[tagName]) | |
| 5362 return s; | |
| 5363 | |
| 5364 return s + getInnerHTML(node) + '</' + tagName + '>'; | |
| 5365 | |
| 5366 case Node.TEXT_NODE: | |
| 5367 var data = node.data; | |
| 5368 if (parentNode && plaintextParents[parentNode.localName]) | |
| 5369 return data; | |
| 5370 return escapeData(data); | |
| 5371 | |
| 5372 case Node.COMMENT_NODE: | |
| 5373 return '<!--' + node.data + '-->'; | |
| 5374 | |
| 5375 default: | |
| 5376 console.error(node); | |
| 5377 throw new Error('not implemented'); | |
| 5378 } | |
| 5379 } | |
| 5380 | |
| 5381 function getInnerHTML(node) { | |
| 5382 if (node instanceof wrappers.HTMLTemplateElement) | |
| 5383 node = node.content; | |
| 5384 | |
| 5385 var s = ''; | |
| 5386 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 5387 s += getOuterHTML(child, node); | |
| 5388 } | |
| 5389 return s; | |
| 5390 } | |
| 5391 | |
| 5392 function setInnerHTML(node, value, opt_tagName) { | |
| 5393 var tagName = opt_tagName || 'div'; | |
| 5394 node.textContent = ''; | |
| 5395 var tempElement = unwrap(node.ownerDocument.createElement(tagName)); | |
| 5396 tempElement.innerHTML = value; | |
| 5397 var firstChild; | |
| 5398 while (firstChild = tempElement.firstChild) { | |
| 5399 node.appendChild(wrap(firstChild)); | |
| 5400 } | |
| 5401 } | |
| 5402 | |
| 5403 // IE11 does not have MSIE in the user agent string. | |
| 5404 var oldIe = /MSIE/.test(navigator.userAgent); | |
| 5405 | |
| 5406 var OriginalHTMLElement = window.HTMLElement; | |
| 5407 var OriginalHTMLTemplateElement = window.HTMLTemplateElement; | |
| 5408 | |
| 5409 function HTMLElement(node) { | |
| 5410 Element.call(this, node); | |
| 5411 } | |
| 5412 HTMLElement.prototype = Object.create(Element.prototype); | |
| 5413 mixin(HTMLElement.prototype, { | |
| 5414 get innerHTML() { | |
| 5415 return getInnerHTML(this); | |
| 5416 }, | |
| 5417 set innerHTML(value) { | |
| 5418 // IE9 does not handle set innerHTML correctly on plaintextParents. It | |
| 5419 // creates element children. For example | |
| 5420 // | |
| 5421 // scriptElement.innerHTML = '<a>test</a>' | |
| 5422 // | |
| 5423 // Creates a single HTMLAnchorElement child. | |
| 5424 if (oldIe && plaintextParents[this.localName]) { | |
| 5425 this.textContent = value; | |
| 5426 return; | |
| 5427 } | |
| 5428 | |
| 5429 var removedNodes = snapshotNodeList(this.childNodes); | |
| 5430 | |
| 5431 if (this.invalidateShadowRenderer()) { | |
| 5432 if (this instanceof wrappers.HTMLTemplateElement) | |
| 5433 setInnerHTML(this.content, value); | |
| 5434 else | |
| 5435 setInnerHTML(this, value, this.tagName); | |
| 5436 | |
| 5437 // If we have a non native template element we need to handle this | |
| 5438 // manually since setting impl.innerHTML would add the html as direct | |
| 5439 // children and not be moved over to the content fragment. | |
| 5440 } else if (!OriginalHTMLTemplateElement && | |
| 5441 this instanceof wrappers.HTMLTemplateElement) { | |
| 5442 setInnerHTML(this.content, value); | |
| 5443 } else { | |
| 5444 unsafeUnwrap(this).innerHTML = value; | |
| 5445 } | |
| 5446 | |
| 5447 var addedNodes = snapshotNodeList(this.childNodes); | |
| 5448 | |
| 5449 enqueueMutation(this, 'childList', { | |
| 5450 addedNodes: addedNodes, | |
| 5451 removedNodes: removedNodes | |
| 5452 }); | |
| 5453 | |
| 5454 nodesWereRemoved(removedNodes); | |
| 5455 nodesWereAdded(addedNodes, this); | |
| 5456 }, | |
| 5457 | |
| 5458 get outerHTML() { | |
| 5459 return getOuterHTML(this, this.parentNode); | |
| 5460 }, | |
| 5461 set outerHTML(value) { | |
| 5462 var p = this.parentNode; | |
| 5463 if (p) { | |
| 5464 p.invalidateShadowRenderer(); | |
| 5465 var df = frag(p, value); | |
| 5466 p.replaceChild(df, this); | |
| 5467 } | |
| 5468 }, | |
| 5469 | |
| 5470 insertAdjacentHTML: function(position, text) { | |
| 5471 var contextElement, refNode; | |
| 5472 switch (String(position).toLowerCase()) { | |
| 5473 case 'beforebegin': | |
| 5474 contextElement = this.parentNode; | |
| 5475 refNode = this; | |
| 5476 break; | |
| 5477 case 'afterend': | |
| 5478 contextElement = this.parentNode; | |
| 5479 refNode = this.nextSibling; | |
| 5480 break; | |
| 5481 case 'afterbegin': | |
| 5482 contextElement = this; | |
| 5483 refNode = this.firstChild; | |
| 5484 break; | |
| 5485 case 'beforeend': | |
| 5486 contextElement = this; | |
| 5487 refNode = null; | |
| 5488 break; | |
| 5489 default: | |
| 5490 return; | |
| 5491 } | |
| 5492 | |
| 5493 var df = frag(contextElement, text); | |
| 5494 contextElement.insertBefore(df, refNode); | |
| 5495 }, | |
| 5496 | |
| 5497 get hidden() { | |
| 5498 return this.hasAttribute('hidden'); | |
| 5499 }, | |
| 5500 set hidden(v) { | |
| 5501 if (v) { | |
| 5502 this.setAttribute('hidden', ''); | |
| 5503 } else { | |
| 5504 this.removeAttribute('hidden'); | |
| 5505 } | |
| 5506 } | |
| 5507 }); | |
| 5508 | |
| 5509 function frag(contextElement, html) { | |
| 5510 // TODO(arv): This does not work with SVG and other non HTML elements. | |
| 5511 var p = unwrap(contextElement.cloneNode(false)); | |
| 5512 p.innerHTML = html; | |
| 5513 var df = unwrap(document.createDocumentFragment()); | |
| 5514 var c; | |
| 5515 while (c = p.firstChild) { | |
| 5516 df.appendChild(c); | |
| 5517 } | |
| 5518 return wrap(df); | |
| 5519 } | |
| 5520 | |
| 5521 function getter(name) { | |
| 5522 return function() { | |
| 5523 scope.renderAllPending(); | |
| 5524 return unsafeUnwrap(this)[name]; | |
| 5525 }; | |
| 5526 } | |
| 5527 | |
| 5528 function getterRequiresRendering(name) { | |
| 5529 defineGetter(HTMLElement, name, getter(name)); | |
| 5530 } | |
| 5531 | |
| 5532 [ | |
| 5533 'clientHeight', | |
| 5534 'clientLeft', | |
| 5535 'clientTop', | |
| 5536 'clientWidth', | |
| 5537 'offsetHeight', | |
| 5538 'offsetLeft', | |
| 5539 'offsetTop', | |
| 5540 'offsetWidth', | |
| 5541 'scrollHeight', | |
| 5542 'scrollWidth', | |
| 5543 ].forEach(getterRequiresRendering); | |
| 5544 | |
| 5545 function getterAndSetterRequiresRendering(name) { | |
| 5546 Object.defineProperty(HTMLElement.prototype, name, { | |
| 5547 get: getter(name), | |
| 5548 set: function(v) { | |
| 5549 scope.renderAllPending(); | |
| 5550 unsafeUnwrap(this)[name] = v; | |
| 5551 }, | |
| 5552 configurable: true, | |
| 5553 enumerable: true | |
| 5554 }); | |
| 5555 } | |
| 5556 | |
| 5557 [ | |
| 5558 'scrollLeft', | |
| 5559 'scrollTop', | |
| 5560 ].forEach(getterAndSetterRequiresRendering); | |
| 5561 | |
| 5562 function methodRequiresRendering(name) { | |
| 5563 Object.defineProperty(HTMLElement.prototype, name, { | |
| 5564 value: function() { | |
| 5565 scope.renderAllPending(); | |
| 5566 return unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments); | |
| 5567 }, | |
| 5568 configurable: true, | |
| 5569 enumerable: true | |
| 5570 }); | |
| 5571 } | |
| 5572 | |
| 5573 [ | |
| 5574 'getBoundingClientRect', | |
| 5575 'getClientRects', | |
| 5576 'scrollIntoView' | |
| 5577 ].forEach(methodRequiresRendering); | |
| 5578 | |
| 5579 // HTMLElement is abstract so we use a subclass that has no members. | |
| 5580 registerWrapper(OriginalHTMLElement, HTMLElement, | |
| 5581 document.createElement('b')); | |
| 5582 | |
| 5583 scope.wrappers.HTMLElement = HTMLElement; | |
| 5584 | |
| 5585 // TODO: Find a better way to share these two with WrapperShadowRoot. | |
| 5586 scope.getInnerHTML = getInnerHTML; | |
| 5587 scope.setInnerHTML = setInnerHTML | |
| 5588 })(window.ShadowDOMPolyfill); | |
| 5589 | |
| 5590 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5591 // Use of this source code is goverened by a BSD-style | |
| 5592 // license that can be found in the LICENSE file. | |
| 5593 | |
| 5594 (function(scope) { | |
| 5595 'use strict'; | |
| 5596 | |
| 5597 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5598 var mixin = scope.mixin; | |
| 5599 var registerWrapper = scope.registerWrapper; | |
| 5600 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 5601 var wrap = scope.wrap; | |
| 5602 | |
| 5603 var OriginalHTMLCanvasElement = window.HTMLCanvasElement; | |
| 5604 | |
| 5605 function HTMLCanvasElement(node) { | |
| 5606 HTMLElement.call(this, node); | |
| 5607 } | |
| 5608 HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype); | |
| 5609 | |
| 5610 mixin(HTMLCanvasElement.prototype, { | |
| 5611 getContext: function() { | |
| 5612 var context = unsafeUnwrap(this).getContext.apply(unsafeUnwrap(this), argu
ments); | |
| 5613 return context && wrap(context); | |
| 5614 } | |
| 5615 }); | |
| 5616 | |
| 5617 registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement, | |
| 5618 document.createElement('canvas')); | |
| 5619 | |
| 5620 scope.wrappers.HTMLCanvasElement = HTMLCanvasElement; | |
| 5621 })(window.ShadowDOMPolyfill); | |
| 5622 | |
| 5623 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5624 // Use of this source code is goverened by a BSD-style | |
| 5625 // license that can be found in the LICENSE file. | |
| 5626 | |
| 5627 (function(scope) { | |
| 5628 'use strict'; | |
| 5629 | |
| 5630 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5631 var mixin = scope.mixin; | |
| 5632 var registerWrapper = scope.registerWrapper; | |
| 5633 | |
| 5634 var OriginalHTMLContentElement = window.HTMLContentElement; | |
| 5635 | |
| 5636 function HTMLContentElement(node) { | |
| 5637 HTMLElement.call(this, node); | |
| 5638 } | |
| 5639 HTMLContentElement.prototype = Object.create(HTMLElement.prototype); | |
| 5640 mixin(HTMLContentElement.prototype, { | |
| 5641 constructor: HTMLContentElement, | |
| 5642 | |
| 5643 get select() { | |
| 5644 return this.getAttribute('select'); | |
| 5645 }, | |
| 5646 set select(value) { | |
| 5647 this.setAttribute('select', value); | |
| 5648 }, | |
| 5649 | |
| 5650 setAttribute: function(n, v) { | |
| 5651 HTMLElement.prototype.setAttribute.call(this, n, v); | |
| 5652 if (String(n).toLowerCase() === 'select') | |
| 5653 this.invalidateShadowRenderer(true); | |
| 5654 } | |
| 5655 | |
| 5656 // getDistributedNodes is added in ShadowRenderer | |
| 5657 }); | |
| 5658 | |
| 5659 if (OriginalHTMLContentElement) | |
| 5660 registerWrapper(OriginalHTMLContentElement, HTMLContentElement); | |
| 5661 | |
| 5662 scope.wrappers.HTMLContentElement = HTMLContentElement; | |
| 5663 })(window.ShadowDOMPolyfill); | |
| 5664 | |
| 5665 /* | |
| 5666 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 5667 * Use of this source code is governed by a BSD-style | |
| 5668 * license that can be found in the LICENSE file. | |
| 5669 */ | |
| 5670 | |
| 5671 (function(scope) { | |
| 5672 'use strict'; | |
| 5673 | |
| 5674 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5675 var mixin = scope.mixin; | |
| 5676 var registerWrapper = scope.registerWrapper; | |
| 5677 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
| 5678 var unwrap = scope.unwrap; | |
| 5679 | |
| 5680 var OriginalHTMLFormElement = window.HTMLFormElement; | |
| 5681 | |
| 5682 function HTMLFormElement(node) { | |
| 5683 HTMLElement.call(this, node); | |
| 5684 } | |
| 5685 HTMLFormElement.prototype = Object.create(HTMLElement.prototype); | |
| 5686 mixin(HTMLFormElement.prototype, { | |
| 5687 get elements() { | |
| 5688 // Note: technically this should be an HTMLFormControlsCollection, but | |
| 5689 // that inherits from HTMLCollection, so should be good enough. Spec: | |
| 5690 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-
interfaces.html#htmlformcontrolscollection | |
| 5691 return wrapHTMLCollection(unwrap(this).elements); | |
| 5692 } | |
| 5693 }); | |
| 5694 | |
| 5695 registerWrapper(OriginalHTMLFormElement, HTMLFormElement, | |
| 5696 document.createElement('form')); | |
| 5697 | |
| 5698 scope.wrappers.HTMLFormElement = HTMLFormElement; | |
| 5699 })(window.ShadowDOMPolyfill); | |
| 5700 | |
| 5701 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5702 // Use of this source code is goverened by a BSD-style | |
| 5703 // license that can be found in the LICENSE file. | |
| 5704 | |
| 5705 (function(scope) { | |
| 5706 'use strict'; | |
| 5707 | |
| 5708 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5709 var registerWrapper = scope.registerWrapper; | |
| 5710 var unwrap = scope.unwrap; | |
| 5711 var rewrap = scope.rewrap; | |
| 5712 | |
| 5713 var OriginalHTMLImageElement = window.HTMLImageElement; | |
| 5714 | |
| 5715 function HTMLImageElement(node) { | |
| 5716 HTMLElement.call(this, node); | |
| 5717 } | |
| 5718 HTMLImageElement.prototype = Object.create(HTMLElement.prototype); | |
| 5719 | |
| 5720 registerWrapper(OriginalHTMLImageElement, HTMLImageElement, | |
| 5721 document.createElement('img')); | |
| 5722 | |
| 5723 function Image(width, height) { | |
| 5724 if (!(this instanceof Image)) { | |
| 5725 throw new TypeError( | |
| 5726 'DOM object constructor cannot be called as a function.'); | |
| 5727 } | |
| 5728 | |
| 5729 var node = unwrap(document.createElement('img')); | |
| 5730 HTMLElement.call(this, node); | |
| 5731 rewrap(node, this); | |
| 5732 | |
| 5733 if (width !== undefined) | |
| 5734 node.width = width; | |
| 5735 if (height !== undefined) | |
| 5736 node.height = height; | |
| 5737 } | |
| 5738 | |
| 5739 Image.prototype = HTMLImageElement.prototype; | |
| 5740 | |
| 5741 scope.wrappers.HTMLImageElement = HTMLImageElement; | |
| 5742 scope.wrappers.Image = Image; | |
| 5743 })(window.ShadowDOMPolyfill); | |
| 5744 | |
| 5745 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5746 // Use of this source code is goverened by a BSD-style | |
| 5747 // license that can be found in the LICENSE file. | |
| 5748 | |
| 5749 (function(scope) { | |
| 5750 'use strict'; | |
| 5751 | |
| 5752 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5753 var mixin = scope.mixin; | |
| 5754 var NodeList = scope.wrappers.NodeList; | |
| 5755 var registerWrapper = scope.registerWrapper; | |
| 5756 | |
| 5757 var OriginalHTMLShadowElement = window.HTMLShadowElement; | |
| 5758 | |
| 5759 function HTMLShadowElement(node) { | |
| 5760 HTMLElement.call(this, node); | |
| 5761 } | |
| 5762 HTMLShadowElement.prototype = Object.create(HTMLElement.prototype); | |
| 5763 HTMLShadowElement.prototype.constructor = HTMLShadowElement; | |
| 5764 | |
| 5765 // getDistributedNodes is added in ShadowRenderer | |
| 5766 | |
| 5767 if (OriginalHTMLShadowElement) | |
| 5768 registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement); | |
| 5769 | |
| 5770 scope.wrappers.HTMLShadowElement = HTMLShadowElement; | |
| 5771 })(window.ShadowDOMPolyfill); | |
| 5772 | |
| 5773 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5774 // Use of this source code is goverened by a BSD-style | |
| 5775 // license that can be found in the LICENSE file. | |
| 5776 | |
| 5777 (function(scope) { | |
| 5778 'use strict'; | |
| 5779 | |
| 5780 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5781 var mixin = scope.mixin; | |
| 5782 var registerWrapper = scope.registerWrapper; | |
| 5783 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 5784 var unwrap = scope.unwrap; | |
| 5785 var wrap = scope.wrap; | |
| 5786 | |
| 5787 var contentTable = new WeakMap(); | |
| 5788 var templateContentsOwnerTable = new WeakMap(); | |
| 5789 | |
| 5790 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner | |
| 5791 function getTemplateContentsOwner(doc) { | |
| 5792 if (!doc.defaultView) | |
| 5793 return doc; | |
| 5794 var d = templateContentsOwnerTable.get(doc); | |
| 5795 if (!d) { | |
| 5796 // TODO(arv): This should either be a Document or HTMLDocument depending | |
| 5797 // on doc. | |
| 5798 d = doc.implementation.createHTMLDocument(''); | |
| 5799 while (d.lastChild) { | |
| 5800 d.removeChild(d.lastChild); | |
| 5801 } | |
| 5802 templateContentsOwnerTable.set(doc, d); | |
| 5803 } | |
| 5804 return d; | |
| 5805 } | |
| 5806 | |
| 5807 function extractContent(templateElement) { | |
| 5808 // templateElement is not a wrapper here. | |
| 5809 var doc = getTemplateContentsOwner(templateElement.ownerDocument); | |
| 5810 var df = unwrap(doc.createDocumentFragment()); | |
| 5811 var child; | |
| 5812 while (child = templateElement.firstChild) { | |
| 5813 df.appendChild(child); | |
| 5814 } | |
| 5815 return df; | |
| 5816 } | |
| 5817 | |
| 5818 var OriginalHTMLTemplateElement = window.HTMLTemplateElement; | |
| 5819 | |
| 5820 function HTMLTemplateElement(node) { | |
| 5821 HTMLElement.call(this, node); | |
| 5822 if (!OriginalHTMLTemplateElement) { | |
| 5823 var content = extractContent(node); | |
| 5824 contentTable.set(this, wrap(content)); | |
| 5825 } | |
| 5826 } | |
| 5827 HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype); | |
| 5828 | |
| 5829 mixin(HTMLTemplateElement.prototype, { | |
| 5830 constructor: HTMLTemplateElement, | |
| 5831 get content() { | |
| 5832 if (OriginalHTMLTemplateElement) | |
| 5833 return wrap(unsafeUnwrap(this).content); | |
| 5834 return contentTable.get(this); | |
| 5835 }, | |
| 5836 | |
| 5837 // TODO(arv): cloneNode needs to clone content. | |
| 5838 | |
| 5839 }); | |
| 5840 | |
| 5841 if (OriginalHTMLTemplateElement) | |
| 5842 registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement); | |
| 5843 | |
| 5844 scope.wrappers.HTMLTemplateElement = HTMLTemplateElement; | |
| 5845 })(window.ShadowDOMPolyfill); | |
| 5846 | |
| 5847 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5848 // Use of this source code is goverened by a BSD-style | |
| 5849 // license that can be found in the LICENSE file. | |
| 5850 | |
| 5851 (function(scope) { | |
| 5852 'use strict'; | |
| 5853 | |
| 5854 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5855 var registerWrapper = scope.registerWrapper; | |
| 5856 | |
| 5857 var OriginalHTMLMediaElement = window.HTMLMediaElement; | |
| 5858 | |
| 5859 if (!OriginalHTMLMediaElement) return; | |
| 5860 | |
| 5861 function HTMLMediaElement(node) { | |
| 5862 HTMLElement.call(this, node); | |
| 5863 } | |
| 5864 HTMLMediaElement.prototype = Object.create(HTMLElement.prototype); | |
| 5865 | |
| 5866 registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, | |
| 5867 document.createElement('audio')); | |
| 5868 | |
| 5869 scope.wrappers.HTMLMediaElement = HTMLMediaElement; | |
| 5870 })(window.ShadowDOMPolyfill); | |
| 5871 | |
| 5872 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5873 // Use of this source code is goverened by a BSD-style | |
| 5874 // license that can be found in the LICENSE file. | |
| 5875 | |
| 5876 (function(scope) { | |
| 5877 'use strict'; | |
| 5878 | |
| 5879 var HTMLMediaElement = scope.wrappers.HTMLMediaElement; | |
| 5880 var registerWrapper = scope.registerWrapper; | |
| 5881 var unwrap = scope.unwrap; | |
| 5882 var rewrap = scope.rewrap; | |
| 5883 | |
| 5884 var OriginalHTMLAudioElement = window.HTMLAudioElement; | |
| 5885 | |
| 5886 if (!OriginalHTMLAudioElement) return; | |
| 5887 | |
| 5888 function HTMLAudioElement(node) { | |
| 5889 HTMLMediaElement.call(this, node); | |
| 5890 } | |
| 5891 HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype); | |
| 5892 | |
| 5893 registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, | |
| 5894 document.createElement('audio')); | |
| 5895 | |
| 5896 function Audio(src) { | |
| 5897 if (!(this instanceof Audio)) { | |
| 5898 throw new TypeError( | |
| 5899 'DOM object constructor cannot be called as a function.'); | |
| 5900 } | |
| 5901 | |
| 5902 var node = unwrap(document.createElement('audio')); | |
| 5903 HTMLMediaElement.call(this, node); | |
| 5904 rewrap(node, this); | |
| 5905 | |
| 5906 node.setAttribute('preload', 'auto'); | |
| 5907 if (src !== undefined) | |
| 5908 node.setAttribute('src', src); | |
| 5909 } | |
| 5910 | |
| 5911 Audio.prototype = HTMLAudioElement.prototype; | |
| 5912 | |
| 5913 scope.wrappers.HTMLAudioElement = HTMLAudioElement; | |
| 5914 scope.wrappers.Audio = Audio; | |
| 5915 })(window.ShadowDOMPolyfill); | |
| 5916 | |
| 5917 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 5918 // Use of this source code is goverened by a BSD-style | |
| 5919 // license that can be found in the LICENSE file. | |
| 5920 | |
| 5921 (function(scope) { | |
| 5922 'use strict'; | |
| 5923 | |
| 5924 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5925 var mixin = scope.mixin; | |
| 5926 var registerWrapper = scope.registerWrapper; | |
| 5927 var rewrap = scope.rewrap; | |
| 5928 var unwrap = scope.unwrap; | |
| 5929 var wrap = scope.wrap; | |
| 5930 | |
| 5931 var OriginalHTMLOptionElement = window.HTMLOptionElement; | |
| 5932 | |
| 5933 function trimText(s) { | |
| 5934 return s.replace(/\s+/g, ' ').trim(); | |
| 5935 } | |
| 5936 | |
| 5937 function HTMLOptionElement(node) { | |
| 5938 HTMLElement.call(this, node); | |
| 5939 } | |
| 5940 HTMLOptionElement.prototype = Object.create(HTMLElement.prototype); | |
| 5941 mixin(HTMLOptionElement.prototype, { | |
| 5942 get text() { | |
| 5943 return trimText(this.textContent); | |
| 5944 }, | |
| 5945 set text(value) { | |
| 5946 this.textContent = trimText(String(value)); | |
| 5947 }, | |
| 5948 get form() { | |
| 5949 return wrap(unwrap(this).form); | |
| 5950 } | |
| 5951 }); | |
| 5952 | |
| 5953 registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, | |
| 5954 document.createElement('option')); | |
| 5955 | |
| 5956 function Option(text, value, defaultSelected, selected) { | |
| 5957 if (!(this instanceof Option)) { | |
| 5958 throw new TypeError( | |
| 5959 'DOM object constructor cannot be called as a function.'); | |
| 5960 } | |
| 5961 | |
| 5962 var node = unwrap(document.createElement('option')); | |
| 5963 HTMLElement.call(this, node); | |
| 5964 rewrap(node, this); | |
| 5965 | |
| 5966 if (text !== undefined) | |
| 5967 node.text = text; | |
| 5968 if (value !== undefined) | |
| 5969 node.setAttribute('value', value); | |
| 5970 if (defaultSelected === true) | |
| 5971 node.setAttribute('selected', ''); | |
| 5972 node.selected = selected === true; | |
| 5973 } | |
| 5974 | |
| 5975 Option.prototype = HTMLOptionElement.prototype; | |
| 5976 | |
| 5977 scope.wrappers.HTMLOptionElement = HTMLOptionElement; | |
| 5978 scope.wrappers.Option = Option; | |
| 5979 })(window.ShadowDOMPolyfill); | |
| 5980 | |
| 5981 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 5982 // Use of this source code is goverened by a BSD-style | |
| 5983 // license that can be found in the LICENSE file. | |
| 5984 | |
| 5985 (function(scope) { | |
| 5986 'use strict'; | |
| 5987 | |
| 5988 var HTMLElement = scope.wrappers.HTMLElement; | |
| 5989 var mixin = scope.mixin; | |
| 5990 var registerWrapper = scope.registerWrapper; | |
| 5991 var unwrap = scope.unwrap; | |
| 5992 var wrap = scope.wrap; | |
| 5993 | |
| 5994 var OriginalHTMLSelectElement = window.HTMLSelectElement; | |
| 5995 | |
| 5996 function HTMLSelectElement(node) { | |
| 5997 HTMLElement.call(this, node); | |
| 5998 } | |
| 5999 HTMLSelectElement.prototype = Object.create(HTMLElement.prototype); | |
| 6000 mixin(HTMLSelectElement.prototype, { | |
| 6001 add: function(element, before) { | |
| 6002 if (typeof before === 'object') // also includes null | |
| 6003 before = unwrap(before); | |
| 6004 unwrap(this).add(unwrap(element), before); | |
| 6005 }, | |
| 6006 | |
| 6007 remove: function(indexOrNode) { | |
| 6008 // Spec only allows index but implementations allow index or node. | |
| 6009 // remove() is also allowed which is same as remove(undefined) | |
| 6010 if (indexOrNode === undefined) { | |
| 6011 HTMLElement.prototype.remove.call(this); | |
| 6012 return; | |
| 6013 } | |
| 6014 | |
| 6015 if (typeof indexOrNode === 'object') | |
| 6016 indexOrNode = unwrap(indexOrNode); | |
| 6017 | |
| 6018 unwrap(this).remove(indexOrNode); | |
| 6019 }, | |
| 6020 | |
| 6021 get form() { | |
| 6022 return wrap(unwrap(this).form); | |
| 6023 } | |
| 6024 }); | |
| 6025 | |
| 6026 registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, | |
| 6027 document.createElement('select')); | |
| 6028 | |
| 6029 scope.wrappers.HTMLSelectElement = HTMLSelectElement; | |
| 6030 })(window.ShadowDOMPolyfill); | |
| 6031 | |
| 6032 /* | |
| 6033 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6034 * Use of this source code is goverened by a BSD-style | |
| 6035 * license that can be found in the LICENSE file. | |
| 6036 */ | |
| 6037 | |
| 6038 (function(scope) { | |
| 6039 'use strict'; | |
| 6040 | |
| 6041 var HTMLElement = scope.wrappers.HTMLElement; | |
| 6042 var mixin = scope.mixin; | |
| 6043 var registerWrapper = scope.registerWrapper; | |
| 6044 var unwrap = scope.unwrap; | |
| 6045 var wrap = scope.wrap; | |
| 6046 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
| 6047 | |
| 6048 var OriginalHTMLTableElement = window.HTMLTableElement; | |
| 6049 | |
| 6050 function HTMLTableElement(node) { | |
| 6051 HTMLElement.call(this, node); | |
| 6052 } | |
| 6053 HTMLTableElement.prototype = Object.create(HTMLElement.prototype); | |
| 6054 mixin(HTMLTableElement.prototype, { | |
| 6055 get caption() { | |
| 6056 return wrap(unwrap(this).caption); | |
| 6057 }, | |
| 6058 createCaption: function() { | |
| 6059 return wrap(unwrap(this).createCaption()); | |
| 6060 }, | |
| 6061 | |
| 6062 get tHead() { | |
| 6063 return wrap(unwrap(this).tHead); | |
| 6064 }, | |
| 6065 createTHead: function() { | |
| 6066 return wrap(unwrap(this).createTHead()); | |
| 6067 }, | |
| 6068 | |
| 6069 createTFoot: function() { | |
| 6070 return wrap(unwrap(this).createTFoot()); | |
| 6071 }, | |
| 6072 get tFoot() { | |
| 6073 return wrap(unwrap(this).tFoot); | |
| 6074 }, | |
| 6075 | |
| 6076 get tBodies() { | |
| 6077 return wrapHTMLCollection(unwrap(this).tBodies); | |
| 6078 }, | |
| 6079 createTBody: function() { | |
| 6080 return wrap(unwrap(this).createTBody()); | |
| 6081 }, | |
| 6082 | |
| 6083 get rows() { | |
| 6084 return wrapHTMLCollection(unwrap(this).rows); | |
| 6085 }, | |
| 6086 insertRow: function(index) { | |
| 6087 return wrap(unwrap(this).insertRow(index)); | |
| 6088 } | |
| 6089 }); | |
| 6090 | |
| 6091 registerWrapper(OriginalHTMLTableElement, HTMLTableElement, | |
| 6092 document.createElement('table')); | |
| 6093 | |
| 6094 scope.wrappers.HTMLTableElement = HTMLTableElement; | |
| 6095 })(window.ShadowDOMPolyfill); | |
| 6096 | |
| 6097 /* | |
| 6098 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6099 * Use of this source code is goverened by a BSD-style | |
| 6100 * license that can be found in the LICENSE file. | |
| 6101 */ | |
| 6102 | |
| 6103 (function(scope) { | |
| 6104 'use strict'; | |
| 6105 | |
| 6106 var HTMLElement = scope.wrappers.HTMLElement; | |
| 6107 var mixin = scope.mixin; | |
| 6108 var registerWrapper = scope.registerWrapper; | |
| 6109 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
| 6110 var unwrap = scope.unwrap; | |
| 6111 var wrap = scope.wrap; | |
| 6112 | |
| 6113 var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement; | |
| 6114 | |
| 6115 function HTMLTableSectionElement(node) { | |
| 6116 HTMLElement.call(this, node); | |
| 6117 } | |
| 6118 HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype); | |
| 6119 mixin(HTMLTableSectionElement.prototype, { | |
| 6120 constructor: HTMLTableSectionElement, | |
| 6121 get rows() { | |
| 6122 return wrapHTMLCollection(unwrap(this).rows); | |
| 6123 }, | |
| 6124 insertRow: function(index) { | |
| 6125 return wrap(unwrap(this).insertRow(index)); | |
| 6126 } | |
| 6127 }); | |
| 6128 | |
| 6129 registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, | |
| 6130 document.createElement('thead')); | |
| 6131 | |
| 6132 scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement; | |
| 6133 })(window.ShadowDOMPolyfill); | |
| 6134 | |
| 6135 /* | |
| 6136 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6137 * Use of this source code is goverened by a BSD-style | |
| 6138 * license that can be found in the LICENSE file. | |
| 6139 */ | |
| 6140 | |
| 6141 (function(scope) { | |
| 6142 'use strict'; | |
| 6143 | |
| 6144 var HTMLElement = scope.wrappers.HTMLElement; | |
| 6145 var mixin = scope.mixin; | |
| 6146 var registerWrapper = scope.registerWrapper; | |
| 6147 var wrapHTMLCollection = scope.wrapHTMLCollection; | |
| 6148 var unwrap = scope.unwrap; | |
| 6149 var wrap = scope.wrap; | |
| 6150 | |
| 6151 var OriginalHTMLTableRowElement = window.HTMLTableRowElement; | |
| 6152 | |
| 6153 function HTMLTableRowElement(node) { | |
| 6154 HTMLElement.call(this, node); | |
| 6155 } | |
| 6156 HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype); | |
| 6157 mixin(HTMLTableRowElement.prototype, { | |
| 6158 get cells() { | |
| 6159 return wrapHTMLCollection(unwrap(this).cells); | |
| 6160 }, | |
| 6161 | |
| 6162 insertCell: function(index) { | |
| 6163 return wrap(unwrap(this).insertCell(index)); | |
| 6164 } | |
| 6165 }); | |
| 6166 | |
| 6167 registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, | |
| 6168 document.createElement('tr')); | |
| 6169 | |
| 6170 scope.wrappers.HTMLTableRowElement = HTMLTableRowElement; | |
| 6171 })(window.ShadowDOMPolyfill); | |
| 6172 | |
| 6173 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6174 // Use of this source code is goverened by a BSD-style | |
| 6175 // license that can be found in the LICENSE file. | |
| 6176 | |
| 6177 (function(scope) { | |
| 6178 'use strict'; | |
| 6179 | |
| 6180 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
| 6181 var HTMLElement = scope.wrappers.HTMLElement; | |
| 6182 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | |
| 6183 var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement; | |
| 6184 var mixin = scope.mixin; | |
| 6185 var registerWrapper = scope.registerWrapper; | |
| 6186 | |
| 6187 var OriginalHTMLUnknownElement = window.HTMLUnknownElement; | |
| 6188 | |
| 6189 function HTMLUnknownElement(node) { | |
| 6190 switch (node.localName) { | |
| 6191 case 'content': | |
| 6192 return new HTMLContentElement(node); | |
| 6193 case 'shadow': | |
| 6194 return new HTMLShadowElement(node); | |
| 6195 case 'template': | |
| 6196 return new HTMLTemplateElement(node); | |
| 6197 } | |
| 6198 HTMLElement.call(this, node); | |
| 6199 } | |
| 6200 HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype); | |
| 6201 registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement); | |
| 6202 scope.wrappers.HTMLUnknownElement = HTMLUnknownElement; | |
| 6203 })(window.ShadowDOMPolyfill); | |
| 6204 | |
| 6205 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6206 // Use of this source code is goverened by a BSD-style | |
| 6207 // license that can be found in the LICENSE file. | |
| 6208 | |
| 6209 (function(scope) { | |
| 6210 'use strict'; | |
| 6211 | |
| 6212 var Element = scope.wrappers.Element; | |
| 6213 var HTMLElement = scope.wrappers.HTMLElement; | |
| 6214 var registerObject = scope.registerObject; | |
| 6215 | |
| 6216 var SVG_NS = 'http://www.w3.org/2000/svg'; | |
| 6217 var svgTitleElement = document.createElementNS(SVG_NS, 'title'); | |
| 6218 var SVGTitleElement = registerObject(svgTitleElement); | |
| 6219 var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor; | |
| 6220 | |
| 6221 // IE11 does not have classList for SVG elements. The spec says that classList | |
| 6222 // is an accessor on Element, but IE11 puts classList on HTMLElement, leaving | |
| 6223 // SVGElement without a classList property. We therefore move the accessor for | |
| 6224 // IE11. | |
| 6225 if (!('classList' in svgTitleElement)) { | |
| 6226 var descr = Object.getOwnPropertyDescriptor(Element.prototype, 'classList'); | |
| 6227 Object.defineProperty(HTMLElement.prototype, 'classList', descr); | |
| 6228 delete Element.prototype.classList; | |
| 6229 } | |
| 6230 | |
| 6231 scope.wrappers.SVGElement = SVGElement; | |
| 6232 })(window.ShadowDOMPolyfill); | |
| 6233 | |
| 6234 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6235 // Use of this source code is goverened by a BSD-style | |
| 6236 // license that can be found in the LICENSE file. | |
| 6237 | |
| 6238 (function(scope) { | |
| 6239 'use strict'; | |
| 6240 | |
| 6241 var mixin = scope.mixin; | |
| 6242 var registerWrapper = scope.registerWrapper; | |
| 6243 var unwrap = scope.unwrap; | |
| 6244 var wrap = scope.wrap; | |
| 6245 | |
| 6246 var OriginalSVGUseElement = window.SVGUseElement; | |
| 6247 | |
| 6248 // IE uses SVGElement as parent interface, SVG2 (Blink & Gecko) uses | |
| 6249 // SVGGraphicsElement. Use the <g> element to get the right prototype. | |
| 6250 | |
| 6251 var SVG_NS = 'http://www.w3.org/2000/svg'; | |
| 6252 var gWrapper = wrap(document.createElementNS(SVG_NS, 'g')); | |
| 6253 var useElement = document.createElementNS(SVG_NS, 'use'); | |
| 6254 var SVGGElement = gWrapper.constructor; | |
| 6255 var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype); | |
| 6256 var parentInterface = parentInterfacePrototype.constructor; | |
| 6257 | |
| 6258 function SVGUseElement(impl) { | |
| 6259 parentInterface.call(this, impl); | |
| 6260 } | |
| 6261 | |
| 6262 SVGUseElement.prototype = Object.create(parentInterfacePrototype); | |
| 6263 | |
| 6264 // Firefox does not expose instanceRoot. | |
| 6265 if ('instanceRoot' in useElement) { | |
| 6266 mixin(SVGUseElement.prototype, { | |
| 6267 get instanceRoot() { | |
| 6268 return wrap(unwrap(this).instanceRoot); | |
| 6269 }, | |
| 6270 get animatedInstanceRoot() { | |
| 6271 return wrap(unwrap(this).animatedInstanceRoot); | |
| 6272 }, | |
| 6273 }); | |
| 6274 } | |
| 6275 | |
| 6276 registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement); | |
| 6277 | |
| 6278 scope.wrappers.SVGUseElement = SVGUseElement; | |
| 6279 })(window.ShadowDOMPolyfill); | |
| 6280 | |
| 6281 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 6282 // Use of this source code is goverened by a BSD-style | |
| 6283 // license that can be found in the LICENSE file. | |
| 6284 | |
| 6285 (function(scope) { | |
| 6286 'use strict'; | |
| 6287 | |
| 6288 var EventTarget = scope.wrappers.EventTarget; | |
| 6289 var mixin = scope.mixin; | |
| 6290 var registerWrapper = scope.registerWrapper; | |
| 6291 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6292 var wrap = scope.wrap; | |
| 6293 | |
| 6294 var OriginalSVGElementInstance = window.SVGElementInstance; | |
| 6295 if (!OriginalSVGElementInstance) | |
| 6296 return; | |
| 6297 | |
| 6298 function SVGElementInstance(impl) { | |
| 6299 EventTarget.call(this, impl); | |
| 6300 } | |
| 6301 | |
| 6302 SVGElementInstance.prototype = Object.create(EventTarget.prototype); | |
| 6303 mixin(SVGElementInstance.prototype, { | |
| 6304 /** @type {SVGElement} */ | |
| 6305 get correspondingElement() { | |
| 6306 return wrap(unsafeUnwrap(this).correspondingElement); | |
| 6307 }, | |
| 6308 | |
| 6309 /** @type {SVGUseElement} */ | |
| 6310 get correspondingUseElement() { | |
| 6311 return wrap(unsafeUnwrap(this).correspondingUseElement); | |
| 6312 }, | |
| 6313 | |
| 6314 /** @type {SVGElementInstance} */ | |
| 6315 get parentNode() { | |
| 6316 return wrap(unsafeUnwrap(this).parentNode); | |
| 6317 }, | |
| 6318 | |
| 6319 /** @type {SVGElementInstanceList} */ | |
| 6320 get childNodes() { | |
| 6321 throw new Error('Not implemented'); | |
| 6322 }, | |
| 6323 | |
| 6324 /** @type {SVGElementInstance} */ | |
| 6325 get firstChild() { | |
| 6326 return wrap(unsafeUnwrap(this).firstChild); | |
| 6327 }, | |
| 6328 | |
| 6329 /** @type {SVGElementInstance} */ | |
| 6330 get lastChild() { | |
| 6331 return wrap(unsafeUnwrap(this).lastChild); | |
| 6332 }, | |
| 6333 | |
| 6334 /** @type {SVGElementInstance} */ | |
| 6335 get previousSibling() { | |
| 6336 return wrap(unsafeUnwrap(this).previousSibling); | |
| 6337 }, | |
| 6338 | |
| 6339 /** @type {SVGElementInstance} */ | |
| 6340 get nextSibling() { | |
| 6341 return wrap(unsafeUnwrap(this).nextSibling); | |
| 6342 } | |
| 6343 }); | |
| 6344 | |
| 6345 registerWrapper(OriginalSVGElementInstance, SVGElementInstance); | |
| 6346 | |
| 6347 scope.wrappers.SVGElementInstance = SVGElementInstance; | |
| 6348 })(window.ShadowDOMPolyfill); | |
| 6349 | |
| 6350 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6351 // Use of this source code is goverened by a BSD-style | |
| 6352 // license that can be found in the LICENSE file. | |
| 6353 | |
| 6354 (function(scope) { | |
| 6355 'use strict'; | |
| 6356 | |
| 6357 var mixin = scope.mixin; | |
| 6358 var registerWrapper = scope.registerWrapper; | |
| 6359 var setWrapper = scope.setWrapper; | |
| 6360 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6361 var unwrap = scope.unwrap; | |
| 6362 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 6363 var wrap = scope.wrap; | |
| 6364 | |
| 6365 var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D; | |
| 6366 | |
| 6367 function CanvasRenderingContext2D(impl) { | |
| 6368 setWrapper(impl, this); | |
| 6369 } | |
| 6370 | |
| 6371 mixin(CanvasRenderingContext2D.prototype, { | |
| 6372 get canvas() { | |
| 6373 return wrap(unsafeUnwrap(this).canvas); | |
| 6374 }, | |
| 6375 | |
| 6376 drawImage: function() { | |
| 6377 arguments[0] = unwrapIfNeeded(arguments[0]); | |
| 6378 unsafeUnwrap(this).drawImage.apply(unsafeUnwrap(this), arguments); | |
| 6379 }, | |
| 6380 | |
| 6381 createPattern: function() { | |
| 6382 arguments[0] = unwrap(arguments[0]); | |
| 6383 return unsafeUnwrap(this).createPattern.apply(unsafeUnwrap(this), argument
s); | |
| 6384 } | |
| 6385 }); | |
| 6386 | |
| 6387 registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D, | |
| 6388 document.createElement('canvas').getContext('2d')); | |
| 6389 | |
| 6390 scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D; | |
| 6391 })(window.ShadowDOMPolyfill); | |
| 6392 | |
| 6393 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6394 // Use of this source code is goverened by a BSD-style | |
| 6395 // license that can be found in the LICENSE file. | |
| 6396 | |
| 6397 (function(scope) { | |
| 6398 'use strict'; | |
| 6399 | |
| 6400 var mixin = scope.mixin; | |
| 6401 var registerWrapper = scope.registerWrapper; | |
| 6402 var setWrapper = scope.setWrapper; | |
| 6403 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6404 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 6405 var wrap = scope.wrap; | |
| 6406 | |
| 6407 var OriginalWebGLRenderingContext = window.WebGLRenderingContext; | |
| 6408 | |
| 6409 // IE10 does not have WebGL. | |
| 6410 if (!OriginalWebGLRenderingContext) | |
| 6411 return; | |
| 6412 | |
| 6413 function WebGLRenderingContext(impl) { | |
| 6414 setWrapper(impl, this); | |
| 6415 } | |
| 6416 | |
| 6417 mixin(WebGLRenderingContext.prototype, { | |
| 6418 get canvas() { | |
| 6419 return wrap(unsafeUnwrap(this).canvas); | |
| 6420 }, | |
| 6421 | |
| 6422 texImage2D: function() { | |
| 6423 arguments[5] = unwrapIfNeeded(arguments[5]); | |
| 6424 unsafeUnwrap(this).texImage2D.apply(unsafeUnwrap(this), arguments); | |
| 6425 }, | |
| 6426 | |
| 6427 texSubImage2D: function() { | |
| 6428 arguments[6] = unwrapIfNeeded(arguments[6]); | |
| 6429 unsafeUnwrap(this).texSubImage2D.apply(unsafeUnwrap(this), arguments); | |
| 6430 } | |
| 6431 }); | |
| 6432 | |
| 6433 // Blink/WebKit has broken DOM bindings. Usually we would create an instance | |
| 6434 // of the object and pass it into registerWrapper as a "blueprint" but | |
| 6435 // creating WebGL contexts is expensive and might fail so we use a dummy | |
| 6436 // object with dummy instance properties for these broken browsers. | |
| 6437 var instanceProperties = /WebKit/.test(navigator.userAgent) ? | |
| 6438 {drawingBufferHeight: null, drawingBufferWidth: null} : {}; | |
| 6439 | |
| 6440 registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext, | |
| 6441 instanceProperties); | |
| 6442 | |
| 6443 scope.wrappers.WebGLRenderingContext = WebGLRenderingContext; | |
| 6444 })(window.ShadowDOMPolyfill); | |
| 6445 | |
| 6446 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6447 // Use of this source code is goverened by a BSD-style | |
| 6448 // license that can be found in the LICENSE file. | |
| 6449 | |
| 6450 (function(scope) { | |
| 6451 'use strict'; | |
| 6452 | |
| 6453 var registerWrapper = scope.registerWrapper; | |
| 6454 var setWrapper = scope.setWrapper; | |
| 6455 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6456 var unwrap = scope.unwrap; | |
| 6457 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 6458 var wrap = scope.wrap; | |
| 6459 | |
| 6460 var OriginalRange = window.Range; | |
| 6461 | |
| 6462 function Range(impl) { | |
| 6463 setWrapper(impl, this); | |
| 6464 } | |
| 6465 Range.prototype = { | |
| 6466 get startContainer() { | |
| 6467 return wrap(unsafeUnwrap(this).startContainer); | |
| 6468 }, | |
| 6469 get endContainer() { | |
| 6470 return wrap(unsafeUnwrap(this).endContainer); | |
| 6471 }, | |
| 6472 get commonAncestorContainer() { | |
| 6473 return wrap(unsafeUnwrap(this).commonAncestorContainer); | |
| 6474 }, | |
| 6475 setStart: function(refNode,offset) { | |
| 6476 unsafeUnwrap(this).setStart(unwrapIfNeeded(refNode), offset); | |
| 6477 }, | |
| 6478 setEnd: function(refNode,offset) { | |
| 6479 unsafeUnwrap(this).setEnd(unwrapIfNeeded(refNode), offset); | |
| 6480 }, | |
| 6481 setStartBefore: function(refNode) { | |
| 6482 unsafeUnwrap(this).setStartBefore(unwrapIfNeeded(refNode)); | |
| 6483 }, | |
| 6484 setStartAfter: function(refNode) { | |
| 6485 unsafeUnwrap(this).setStartAfter(unwrapIfNeeded(refNode)); | |
| 6486 }, | |
| 6487 setEndBefore: function(refNode) { | |
| 6488 unsafeUnwrap(this).setEndBefore(unwrapIfNeeded(refNode)); | |
| 6489 }, | |
| 6490 setEndAfter: function(refNode) { | |
| 6491 unsafeUnwrap(this).setEndAfter(unwrapIfNeeded(refNode)); | |
| 6492 }, | |
| 6493 selectNode: function(refNode) { | |
| 6494 unsafeUnwrap(this).selectNode(unwrapIfNeeded(refNode)); | |
| 6495 }, | |
| 6496 selectNodeContents: function(refNode) { | |
| 6497 unsafeUnwrap(this).selectNodeContents(unwrapIfNeeded(refNode)); | |
| 6498 }, | |
| 6499 compareBoundaryPoints: function(how, sourceRange) { | |
| 6500 return unsafeUnwrap(this).compareBoundaryPoints(how, unwrap(sourceRange)); | |
| 6501 }, | |
| 6502 extractContents: function() { | |
| 6503 return wrap(unsafeUnwrap(this).extractContents()); | |
| 6504 }, | |
| 6505 cloneContents: function() { | |
| 6506 return wrap(unsafeUnwrap(this).cloneContents()); | |
| 6507 }, | |
| 6508 insertNode: function(node) { | |
| 6509 unsafeUnwrap(this).insertNode(unwrapIfNeeded(node)); | |
| 6510 }, | |
| 6511 surroundContents: function(newParent) { | |
| 6512 unsafeUnwrap(this).surroundContents(unwrapIfNeeded(newParent)); | |
| 6513 }, | |
| 6514 cloneRange: function() { | |
| 6515 return wrap(unsafeUnwrap(this).cloneRange()); | |
| 6516 }, | |
| 6517 isPointInRange: function(node, offset) { | |
| 6518 return unsafeUnwrap(this).isPointInRange(unwrapIfNeeded(node), offset); | |
| 6519 }, | |
| 6520 comparePoint: function(node, offset) { | |
| 6521 return unsafeUnwrap(this).comparePoint(unwrapIfNeeded(node), offset); | |
| 6522 }, | |
| 6523 intersectsNode: function(node) { | |
| 6524 return unsafeUnwrap(this).intersectsNode(unwrapIfNeeded(node)); | |
| 6525 }, | |
| 6526 toString: function() { | |
| 6527 return unsafeUnwrap(this).toString(); | |
| 6528 } | |
| 6529 }; | |
| 6530 | |
| 6531 // IE9 does not have createContextualFragment. | |
| 6532 if (OriginalRange.prototype.createContextualFragment) { | |
| 6533 Range.prototype.createContextualFragment = function(html) { | |
| 6534 return wrap(unsafeUnwrap(this).createContextualFragment(html)); | |
| 6535 }; | |
| 6536 } | |
| 6537 | |
| 6538 registerWrapper(window.Range, Range, document.createRange()); | |
| 6539 | |
| 6540 scope.wrappers.Range = Range; | |
| 6541 | |
| 6542 })(window.ShadowDOMPolyfill); | |
| 6543 | |
| 6544 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6545 // Use of this source code is goverened by a BSD-style | |
| 6546 // license that can be found in the LICENSE file. | |
| 6547 | |
| 6548 (function(scope) { | |
| 6549 'use strict'; | |
| 6550 | |
| 6551 var GetElementsByInterface = scope.GetElementsByInterface; | |
| 6552 var ParentNodeInterface = scope.ParentNodeInterface; | |
| 6553 var SelectorsInterface = scope.SelectorsInterface; | |
| 6554 var mixin = scope.mixin; | |
| 6555 var registerObject = scope.registerObject; | |
| 6556 | |
| 6557 var DocumentFragment = registerObject(document.createDocumentFragment()); | |
| 6558 mixin(DocumentFragment.prototype, ParentNodeInterface); | |
| 6559 mixin(DocumentFragment.prototype, SelectorsInterface); | |
| 6560 mixin(DocumentFragment.prototype, GetElementsByInterface); | |
| 6561 | |
| 6562 var Comment = registerObject(document.createComment('')); | |
| 6563 | |
| 6564 scope.wrappers.Comment = Comment; | |
| 6565 scope.wrappers.DocumentFragment = DocumentFragment; | |
| 6566 | |
| 6567 })(window.ShadowDOMPolyfill); | |
| 6568 | |
| 6569 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6570 // Use of this source code is goverened by a BSD-style | |
| 6571 // license that can be found in the LICENSE file. | |
| 6572 | |
| 6573 (function(scope) { | |
| 6574 'use strict'; | |
| 6575 | |
| 6576 var DocumentFragment = scope.wrappers.DocumentFragment; | |
| 6577 var TreeScope = scope.TreeScope; | |
| 6578 var elementFromPoint = scope.elementFromPoint; | |
| 6579 var getInnerHTML = scope.getInnerHTML; | |
| 6580 var getTreeScope = scope.getTreeScope; | |
| 6581 var mixin = scope.mixin; | |
| 6582 var rewrap = scope.rewrap; | |
| 6583 var setInnerHTML = scope.setInnerHTML; | |
| 6584 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6585 var unwrap = scope.unwrap; | |
| 6586 | |
| 6587 var shadowHostTable = new WeakMap(); | |
| 6588 var nextOlderShadowTreeTable = new WeakMap(); | |
| 6589 | |
| 6590 var spaceCharRe = /[ \t\n\r\f]/; | |
| 6591 | |
| 6592 function ShadowRoot(hostWrapper) { | |
| 6593 var node = unwrap(unsafeUnwrap(hostWrapper).ownerDocument.createDocumentFrag
ment()); | |
| 6594 DocumentFragment.call(this, node); | |
| 6595 | |
| 6596 // createDocumentFragment associates the node with a wrapper | |
| 6597 // DocumentFragment instance. Override that. | |
| 6598 rewrap(node, this); | |
| 6599 | |
| 6600 var oldShadowRoot = hostWrapper.shadowRoot; | |
| 6601 nextOlderShadowTreeTable.set(this, oldShadowRoot); | |
| 6602 | |
| 6603 this.treeScope_ = | |
| 6604 new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper)); | |
| 6605 | |
| 6606 shadowHostTable.set(this, hostWrapper); | |
| 6607 } | |
| 6608 ShadowRoot.prototype = Object.create(DocumentFragment.prototype); | |
| 6609 mixin(ShadowRoot.prototype, { | |
| 6610 constructor: ShadowRoot, | |
| 6611 | |
| 6612 get innerHTML() { | |
| 6613 return getInnerHTML(this); | |
| 6614 }, | |
| 6615 set innerHTML(value) { | |
| 6616 setInnerHTML(this, value); | |
| 6617 this.invalidateShadowRenderer(); | |
| 6618 }, | |
| 6619 | |
| 6620 get olderShadowRoot() { | |
| 6621 return nextOlderShadowTreeTable.get(this) || null; | |
| 6622 }, | |
| 6623 | |
| 6624 get host() { | |
| 6625 return shadowHostTable.get(this) || null; | |
| 6626 }, | |
| 6627 | |
| 6628 invalidateShadowRenderer: function() { | |
| 6629 return shadowHostTable.get(this).invalidateShadowRenderer(); | |
| 6630 }, | |
| 6631 | |
| 6632 elementFromPoint: function(x, y) { | |
| 6633 return elementFromPoint(this, this.ownerDocument, x, y); | |
| 6634 }, | |
| 6635 | |
| 6636 getElementById: function(id) { | |
| 6637 if (spaceCharRe.test(id)) | |
| 6638 return null; | |
| 6639 return this.querySelector('[id="' + id + '"]'); | |
| 6640 } | |
| 6641 }); | |
| 6642 | |
| 6643 scope.wrappers.ShadowRoot = ShadowRoot; | |
| 6644 | |
| 6645 })(window.ShadowDOMPolyfill); | |
| 6646 | |
| 6647 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 6648 // Use of this source code is governed by a BSD-style | |
| 6649 // license that can be found in the LICENSE file. | |
| 6650 | |
| 6651 (function(scope) { | |
| 6652 'use strict'; | |
| 6653 | |
| 6654 var Element = scope.wrappers.Element; | |
| 6655 var HTMLContentElement = scope.wrappers.HTMLContentElement; | |
| 6656 var HTMLShadowElement = scope.wrappers.HTMLShadowElement; | |
| 6657 var Node = scope.wrappers.Node; | |
| 6658 var ShadowRoot = scope.wrappers.ShadowRoot; | |
| 6659 var assert = scope.assert; | |
| 6660 var getTreeScope = scope.getTreeScope; | |
| 6661 var mixin = scope.mixin; | |
| 6662 var oneOf = scope.oneOf; | |
| 6663 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 6664 var unwrap = scope.unwrap; | |
| 6665 var wrap = scope.wrap; | |
| 6666 | |
| 6667 /** | |
| 6668 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
| 6669 * Up means parentNode | |
| 6670 * Sideways means previous and next sibling. | |
| 6671 * @param {!Node} wrapper | |
| 6672 */ | |
| 6673 function updateWrapperUpAndSideways(wrapper) { | |
| 6674 wrapper.previousSibling_ = wrapper.previousSibling; | |
| 6675 wrapper.nextSibling_ = wrapper.nextSibling; | |
| 6676 wrapper.parentNode_ = wrapper.parentNode; | |
| 6677 } | |
| 6678 | |
| 6679 /** | |
| 6680 * Updates the fields of a wrapper to a snapshot of the logical DOM as needed. | |
| 6681 * Down means first and last child | |
| 6682 * @param {!Node} wrapper | |
| 6683 */ | |
| 6684 function updateWrapperDown(wrapper) { | |
| 6685 wrapper.firstChild_ = wrapper.firstChild; | |
| 6686 wrapper.lastChild_ = wrapper.lastChild; | |
| 6687 } | |
| 6688 | |
| 6689 function updateAllChildNodes(parentNodeWrapper) { | |
| 6690 assert(parentNodeWrapper instanceof Node); | |
| 6691 for (var childWrapper = parentNodeWrapper.firstChild; | |
| 6692 childWrapper; | |
| 6693 childWrapper = childWrapper.nextSibling) { | |
| 6694 updateWrapperUpAndSideways(childWrapper); | |
| 6695 } | |
| 6696 updateWrapperDown(parentNodeWrapper); | |
| 6697 } | |
| 6698 | |
| 6699 function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) { | |
| 6700 var parentNode = unwrap(parentNodeWrapper); | |
| 6701 var newChild = unwrap(newChildWrapper); | |
| 6702 var refChild = refChildWrapper ? unwrap(refChildWrapper) : null; | |
| 6703 | |
| 6704 remove(newChildWrapper); | |
| 6705 updateWrapperUpAndSideways(newChildWrapper); | |
| 6706 | |
| 6707 if (!refChildWrapper) { | |
| 6708 parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild; | |
| 6709 if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) | |
| 6710 parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild; | |
| 6711 | |
| 6712 var lastChildWrapper = wrap(parentNode.lastChild); | |
| 6713 if (lastChildWrapper) | |
| 6714 lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling; | |
| 6715 } else { | |
| 6716 if (parentNodeWrapper.firstChild === refChildWrapper) | |
| 6717 parentNodeWrapper.firstChild_ = refChildWrapper; | |
| 6718 | |
| 6719 refChildWrapper.previousSibling_ = refChildWrapper.previousSibling; | |
| 6720 } | |
| 6721 | |
| 6722 scope.originalInsertBefore.call(parentNode, newChild, refChild); | |
| 6723 } | |
| 6724 | |
| 6725 function remove(nodeWrapper) { | |
| 6726 var node = unwrap(nodeWrapper) | |
| 6727 var parentNode = node.parentNode; | |
| 6728 if (!parentNode) | |
| 6729 return; | |
| 6730 | |
| 6731 var parentNodeWrapper = wrap(parentNode); | |
| 6732 updateWrapperUpAndSideways(nodeWrapper); | |
| 6733 | |
| 6734 if (nodeWrapper.previousSibling) | |
| 6735 nodeWrapper.previousSibling.nextSibling_ = nodeWrapper; | |
| 6736 if (nodeWrapper.nextSibling) | |
| 6737 nodeWrapper.nextSibling.previousSibling_ = nodeWrapper; | |
| 6738 | |
| 6739 if (parentNodeWrapper.lastChild === nodeWrapper) | |
| 6740 parentNodeWrapper.lastChild_ = nodeWrapper; | |
| 6741 if (parentNodeWrapper.firstChild === nodeWrapper) | |
| 6742 parentNodeWrapper.firstChild_ = nodeWrapper; | |
| 6743 | |
| 6744 scope.originalRemoveChild.call(parentNode, node); | |
| 6745 } | |
| 6746 | |
| 6747 var distributedNodesTable = new WeakMap(); | |
| 6748 var destinationInsertionPointsTable = new WeakMap(); | |
| 6749 var rendererForHostTable = new WeakMap(); | |
| 6750 | |
| 6751 function resetDistributedNodes(insertionPoint) { | |
| 6752 distributedNodesTable.set(insertionPoint, []); | |
| 6753 } | |
| 6754 | |
| 6755 function getDistributedNodes(insertionPoint) { | |
| 6756 var rv = distributedNodesTable.get(insertionPoint); | |
| 6757 if (!rv) | |
| 6758 distributedNodesTable.set(insertionPoint, rv = []); | |
| 6759 return rv; | |
| 6760 } | |
| 6761 | |
| 6762 function getChildNodesSnapshot(node) { | |
| 6763 var result = [], i = 0; | |
| 6764 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 6765 result[i++] = child; | |
| 6766 } | |
| 6767 return result; | |
| 6768 } | |
| 6769 | |
| 6770 var request = oneOf(window, [ | |
| 6771 'requestAnimationFrame', | |
| 6772 'mozRequestAnimationFrame', | |
| 6773 'webkitRequestAnimationFrame', | |
| 6774 'setTimeout' | |
| 6775 ]); | |
| 6776 | |
| 6777 var pendingDirtyRenderers = []; | |
| 6778 var renderTimer; | |
| 6779 | |
| 6780 function renderAllPending() { | |
| 6781 // TODO(arv): Order these in document order. That way we do not have to | |
| 6782 // render something twice. | |
| 6783 for (var i = 0; i < pendingDirtyRenderers.length; i++) { | |
| 6784 var renderer = pendingDirtyRenderers[i]; | |
| 6785 var parentRenderer = renderer.parentRenderer; | |
| 6786 if (parentRenderer && parentRenderer.dirty) | |
| 6787 continue; | |
| 6788 renderer.render(); | |
| 6789 } | |
| 6790 | |
| 6791 pendingDirtyRenderers = []; | |
| 6792 } | |
| 6793 | |
| 6794 function handleRequestAnimationFrame() { | |
| 6795 renderTimer = null; | |
| 6796 renderAllPending(); | |
| 6797 } | |
| 6798 | |
| 6799 /** | |
| 6800 * Returns existing shadow renderer for a host or creates it if it is needed. | |
| 6801 * @params {!Element} host | |
| 6802 * @return {!ShadowRenderer} | |
| 6803 */ | |
| 6804 function getRendererForHost(host) { | |
| 6805 var renderer = rendererForHostTable.get(host); | |
| 6806 if (!renderer) { | |
| 6807 renderer = new ShadowRenderer(host); | |
| 6808 rendererForHostTable.set(host, renderer); | |
| 6809 } | |
| 6810 return renderer; | |
| 6811 } | |
| 6812 | |
| 6813 function getShadowRootAncestor(node) { | |
| 6814 var root = getTreeScope(node).root; | |
| 6815 if (root instanceof ShadowRoot) | |
| 6816 return root; | |
| 6817 return null; | |
| 6818 } | |
| 6819 | |
| 6820 function getRendererForShadowRoot(shadowRoot) { | |
| 6821 return getRendererForHost(shadowRoot.host); | |
| 6822 } | |
| 6823 | |
| 6824 var spliceDiff = new ArraySplice(); | |
| 6825 spliceDiff.equals = function(renderNode, rawNode) { | |
| 6826 return unwrap(renderNode.node) === rawNode; | |
| 6827 }; | |
| 6828 | |
| 6829 /** | |
| 6830 * RenderNode is used as an in memory "render tree". When we render the | |
| 6831 * composed tree we create a tree of RenderNodes, then we diff this against | |
| 6832 * the real DOM tree and make minimal changes as needed. | |
| 6833 */ | |
| 6834 function RenderNode(node) { | |
| 6835 this.skip = false; | |
| 6836 this.node = node; | |
| 6837 this.childNodes = []; | |
| 6838 } | |
| 6839 | |
| 6840 RenderNode.prototype = { | |
| 6841 append: function(node) { | |
| 6842 var rv = new RenderNode(node); | |
| 6843 this.childNodes.push(rv); | |
| 6844 return rv; | |
| 6845 }, | |
| 6846 | |
| 6847 sync: function(opt_added) { | |
| 6848 if (this.skip) | |
| 6849 return; | |
| 6850 | |
| 6851 var nodeWrapper = this.node; | |
| 6852 // plain array of RenderNodes | |
| 6853 var newChildren = this.childNodes; | |
| 6854 // plain array of real nodes. | |
| 6855 var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper)); | |
| 6856 var added = opt_added || new WeakMap(); | |
| 6857 | |
| 6858 var splices = spliceDiff.calculateSplices(newChildren, oldChildren); | |
| 6859 | |
| 6860 var newIndex = 0, oldIndex = 0; | |
| 6861 var lastIndex = 0; | |
| 6862 for (var i = 0; i < splices.length; i++) { | |
| 6863 var splice = splices[i]; | |
| 6864 for (; lastIndex < splice.index; lastIndex++) { | |
| 6865 oldIndex++; | |
| 6866 newChildren[newIndex++].sync(added); | |
| 6867 } | |
| 6868 | |
| 6869 var removedCount = splice.removed.length; | |
| 6870 for (var j = 0; j < removedCount; j++) { | |
| 6871 var wrapper = wrap(oldChildren[oldIndex++]); | |
| 6872 if (!added.get(wrapper)) | |
| 6873 remove(wrapper); | |
| 6874 } | |
| 6875 | |
| 6876 var addedCount = splice.addedCount; | |
| 6877 var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]); | |
| 6878 for (var j = 0; j < addedCount; j++) { | |
| 6879 var newChildRenderNode = newChildren[newIndex++]; | |
| 6880 var newChildWrapper = newChildRenderNode.node; | |
| 6881 insertBefore(nodeWrapper, newChildWrapper, refNode); | |
| 6882 | |
| 6883 // Keep track of added so that we do not remove the node after it | |
| 6884 // has been added. | |
| 6885 added.set(newChildWrapper, true); | |
| 6886 | |
| 6887 newChildRenderNode.sync(added); | |
| 6888 } | |
| 6889 | |
| 6890 lastIndex += addedCount; | |
| 6891 } | |
| 6892 | |
| 6893 for (var i = lastIndex; i < newChildren.length; i++) { | |
| 6894 newChildren[i].sync(added); | |
| 6895 } | |
| 6896 } | |
| 6897 }; | |
| 6898 | |
| 6899 function ShadowRenderer(host) { | |
| 6900 this.host = host; | |
| 6901 this.dirty = false; | |
| 6902 this.invalidateAttributes(); | |
| 6903 this.associateNode(host); | |
| 6904 } | |
| 6905 | |
| 6906 ShadowRenderer.prototype = { | |
| 6907 | |
| 6908 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#r
endering-shadow-trees | |
| 6909 render: function(opt_renderNode) { | |
| 6910 if (!this.dirty) | |
| 6911 return; | |
| 6912 | |
| 6913 this.invalidateAttributes(); | |
| 6914 | |
| 6915 var host = this.host; | |
| 6916 | |
| 6917 this.distribution(host); | |
| 6918 var renderNode = opt_renderNode || new RenderNode(host); | |
| 6919 this.buildRenderTree(renderNode, host); | |
| 6920 | |
| 6921 var topMostRenderer = !opt_renderNode; | |
| 6922 if (topMostRenderer) | |
| 6923 renderNode.sync(); | |
| 6924 | |
| 6925 this.dirty = false; | |
| 6926 }, | |
| 6927 | |
| 6928 get parentRenderer() { | |
| 6929 return getTreeScope(this.host).renderer; | |
| 6930 }, | |
| 6931 | |
| 6932 invalidate: function() { | |
| 6933 if (!this.dirty) { | |
| 6934 this.dirty = true; | |
| 6935 var parentRenderer = this.parentRenderer; | |
| 6936 if (parentRenderer) | |
| 6937 parentRenderer.invalidate(); | |
| 6938 pendingDirtyRenderers.push(this); | |
| 6939 if (renderTimer) | |
| 6940 return; | |
| 6941 renderTimer = window[request](handleRequestAnimationFrame, 0); | |
| 6942 } | |
| 6943 }, | |
| 6944 | |
| 6945 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-algorithms | |
| 6946 distribution: function(root) { | |
| 6947 this.resetAllSubtrees(root); | |
| 6948 this.distributionResolution(root); | |
| 6949 }, | |
| 6950 | |
| 6951 resetAll: function(node) { | |
| 6952 if (isInsertionPoint(node)) | |
| 6953 resetDistributedNodes(node); | |
| 6954 else | |
| 6955 resetDestinationInsertionPoints(node); | |
| 6956 | |
| 6957 this.resetAllSubtrees(node); | |
| 6958 }, | |
| 6959 | |
| 6960 resetAllSubtrees: function(node) { | |
| 6961 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 6962 this.resetAll(child); | |
| 6963 } | |
| 6964 | |
| 6965 if (node.shadowRoot) | |
| 6966 this.resetAll(node.shadowRoot); | |
| 6967 | |
| 6968 if (node.olderShadowRoot) | |
| 6969 this.resetAll(node.olderShadowRoot); | |
| 6970 }, | |
| 6971 | |
| 6972 // http://w3c.github.io/webcomponents/spec/shadow/#distribution-results | |
| 6973 distributionResolution: function(node) { | |
| 6974 if (isShadowHost(node)) { | |
| 6975 var shadowHost = node; | |
| 6976 // 1.1 | |
| 6977 var pool = poolPopulation(shadowHost); | |
| 6978 | |
| 6979 var shadowTrees = getShadowTrees(shadowHost); | |
| 6980 | |
| 6981 // 1.2 | |
| 6982 for (var i = 0; i < shadowTrees.length; i++) { | |
| 6983 // 1.2.1 | |
| 6984 this.poolDistribution(shadowTrees[i], pool); | |
| 6985 } | |
| 6986 | |
| 6987 // 1.3 | |
| 6988 for (var i = shadowTrees.length - 1; i >= 0; i--) { | |
| 6989 var shadowTree = shadowTrees[i]; | |
| 6990 | |
| 6991 // 1.3.1 | |
| 6992 // TODO(arv): We should keep the shadow insertion points on the | |
| 6993 // shadow root (or renderer) so we don't have to search the tree | |
| 6994 // every time. | |
| 6995 var shadow = getShadowInsertionPoint(shadowTree); | |
| 6996 | |
| 6997 // 1.3.2 | |
| 6998 if (shadow) { | |
| 6999 | |
| 7000 // 1.3.2.1 | |
| 7001 var olderShadowRoot = shadowTree.olderShadowRoot; | |
| 7002 if (olderShadowRoot) { | |
| 7003 // 1.3.2.1.1 | |
| 7004 pool = poolPopulation(olderShadowRoot); | |
| 7005 } | |
| 7006 | |
| 7007 // 1.3.2.2 | |
| 7008 for (var j = 0; j < pool.length; j++) { | |
| 7009 // 1.3.2.2.1 | |
| 7010 destributeNodeInto(pool[j], shadow); | |
| 7011 } | |
| 7012 } | |
| 7013 | |
| 7014 // 1.3.3 | |
| 7015 this.distributionResolution(shadowTree); | |
| 7016 } | |
| 7017 } | |
| 7018 | |
| 7019 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 7020 this.distributionResolution(child); | |
| 7021 } | |
| 7022 }, | |
| 7023 | |
| 7024 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-distribution-alg
orithm | |
| 7025 poolDistribution: function (node, pool) { | |
| 7026 if (node instanceof HTMLShadowElement) | |
| 7027 return; | |
| 7028 | |
| 7029 if (node instanceof HTMLContentElement) { | |
| 7030 var content = node; | |
| 7031 this.updateDependentAttributes(content.getAttribute('select')); | |
| 7032 | |
| 7033 var anyDistributed = false; | |
| 7034 | |
| 7035 // 1.1 | |
| 7036 for (var i = 0; i < pool.length; i++) { | |
| 7037 var node = pool[i]; | |
| 7038 if (!node) | |
| 7039 continue; | |
| 7040 if (matches(node, content)) { | |
| 7041 destributeNodeInto(node, content); | |
| 7042 pool[i] = undefined; | |
| 7043 anyDistributed = true; | |
| 7044 } | |
| 7045 } | |
| 7046 | |
| 7047 // 1.2 | |
| 7048 // Fallback content | |
| 7049 if (!anyDistributed) { | |
| 7050 for (var child = content.firstChild; | |
| 7051 child; | |
| 7052 child = child.nextSibling) { | |
| 7053 destributeNodeInto(child, content); | |
| 7054 } | |
| 7055 } | |
| 7056 | |
| 7057 return; | |
| 7058 } | |
| 7059 | |
| 7060 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 7061 this.poolDistribution(child, pool); | |
| 7062 } | |
| 7063 }, | |
| 7064 | |
| 7065 buildRenderTree: function(renderNode, node) { | |
| 7066 var children = this.compose(node); | |
| 7067 for (var i = 0; i < children.length; i++) { | |
| 7068 var child = children[i]; | |
| 7069 var childRenderNode = renderNode.append(child); | |
| 7070 this.buildRenderTree(childRenderNode, child); | |
| 7071 } | |
| 7072 | |
| 7073 if (isShadowHost(node)) { | |
| 7074 var renderer = getRendererForHost(node); | |
| 7075 renderer.dirty = false; | |
| 7076 } | |
| 7077 | |
| 7078 }, | |
| 7079 | |
| 7080 compose: function(node) { | |
| 7081 var children = []; | |
| 7082 var p = node.shadowRoot || node; | |
| 7083 for (var child = p.firstChild; child; child = child.nextSibling) { | |
| 7084 if (isInsertionPoint(child)) { | |
| 7085 this.associateNode(p); | |
| 7086 var distributedNodes = getDistributedNodes(child); | |
| 7087 for (var j = 0; j < distributedNodes.length; j++) { | |
| 7088 var distributedNode = distributedNodes[j]; | |
| 7089 if (isFinalDestination(child, distributedNode)) | |
| 7090 children.push(distributedNode); | |
| 7091 } | |
| 7092 } else { | |
| 7093 children.push(child); | |
| 7094 } | |
| 7095 } | |
| 7096 return children; | |
| 7097 }, | |
| 7098 | |
| 7099 /** | |
| 7100 * Invalidates the attributes used to keep track of which attributes may | |
| 7101 * cause the renderer to be invalidated. | |
| 7102 */ | |
| 7103 invalidateAttributes: function() { | |
| 7104 this.attributes = Object.create(null); | |
| 7105 }, | |
| 7106 | |
| 7107 /** | |
| 7108 * Parses the selector and makes this renderer dependent on the attribute | |
| 7109 * being used in the selector. | |
| 7110 * @param {string} selector | |
| 7111 */ | |
| 7112 updateDependentAttributes: function(selector) { | |
| 7113 if (!selector) | |
| 7114 return; | |
| 7115 | |
| 7116 var attributes = this.attributes; | |
| 7117 | |
| 7118 // .class | |
| 7119 if (/\.\w+/.test(selector)) | |
| 7120 attributes['class'] = true; | |
| 7121 | |
| 7122 // #id | |
| 7123 if (/#\w+/.test(selector)) | |
| 7124 attributes['id'] = true; | |
| 7125 | |
| 7126 selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) { | |
| 7127 attributes[name] = true; | |
| 7128 }); | |
| 7129 | |
| 7130 // Pseudo selectors have been removed from the spec. | |
| 7131 }, | |
| 7132 | |
| 7133 dependsOnAttribute: function(name) { | |
| 7134 return this.attributes[name]; | |
| 7135 }, | |
| 7136 | |
| 7137 associateNode: function(node) { | |
| 7138 unsafeUnwrap(node).polymerShadowRenderer_ = this; | |
| 7139 } | |
| 7140 }; | |
| 7141 | |
| 7142 // http://w3c.github.io/webcomponents/spec/shadow/#dfn-pool-population-algorit
hm | |
| 7143 function poolPopulation(node) { | |
| 7144 var pool = []; | |
| 7145 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 7146 if (isInsertionPoint(child)) { | |
| 7147 pool.push.apply(pool, getDistributedNodes(child)); | |
| 7148 } else { | |
| 7149 pool.push(child); | |
| 7150 } | |
| 7151 } | |
| 7152 return pool; | |
| 7153 } | |
| 7154 | |
| 7155 function getShadowInsertionPoint(node) { | |
| 7156 if (node instanceof HTMLShadowElement) | |
| 7157 return node; | |
| 7158 if (node instanceof HTMLContentElement) | |
| 7159 return null; | |
| 7160 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 7161 var res = getShadowInsertionPoint(child); | |
| 7162 if (res) | |
| 7163 return res; | |
| 7164 } | |
| 7165 return null; | |
| 7166 } | |
| 7167 | |
| 7168 function destributeNodeInto(child, insertionPoint) { | |
| 7169 getDistributedNodes(insertionPoint).push(child); | |
| 7170 var points = destinationInsertionPointsTable.get(child); | |
| 7171 if (!points) | |
| 7172 destinationInsertionPointsTable.set(child, [insertionPoint]); | |
| 7173 else | |
| 7174 points.push(insertionPoint); | |
| 7175 } | |
| 7176 | |
| 7177 function getDestinationInsertionPoints(node) { | |
| 7178 return destinationInsertionPointsTable.get(node); | |
| 7179 } | |
| 7180 | |
| 7181 function resetDestinationInsertionPoints(node) { | |
| 7182 // IE11 crashes when delete is used. | |
| 7183 destinationInsertionPointsTable.set(node, undefined); | |
| 7184 } | |
| 7185 | |
| 7186 // AllowedSelectors : | |
| 7187 // TypeSelector | |
| 7188 // * | |
| 7189 // ClassSelector | |
| 7190 // IDSelector | |
| 7191 // AttributeSelector | |
| 7192 // negation | |
| 7193 var selectorStartCharRe = /^(:not\()?[*.#[a-zA-Z_|]/; | |
| 7194 | |
| 7195 function matches(node, contentElement) { | |
| 7196 var select = contentElement.getAttribute('select'); | |
| 7197 if (!select) | |
| 7198 return true; | |
| 7199 | |
| 7200 // Here we know the select attribute is a non empty string. | |
| 7201 select = select.trim(); | |
| 7202 if (!select) | |
| 7203 return true; | |
| 7204 | |
| 7205 if (!(node instanceof Element)) | |
| 7206 return false; | |
| 7207 | |
| 7208 if (!selectorStartCharRe.test(select)) | |
| 7209 return false; | |
| 7210 | |
| 7211 try { | |
| 7212 return node.matches(select); | |
| 7213 } catch (ex) { | |
| 7214 // Invalid selector. | |
| 7215 return false; | |
| 7216 } | |
| 7217 } | |
| 7218 | |
| 7219 function isFinalDestination(insertionPoint, node) { | |
| 7220 var points = getDestinationInsertionPoints(node); | |
| 7221 return points && points[points.length - 1] === insertionPoint; | |
| 7222 } | |
| 7223 | |
| 7224 function isInsertionPoint(node) { | |
| 7225 return node instanceof HTMLContentElement || | |
| 7226 node instanceof HTMLShadowElement; | |
| 7227 } | |
| 7228 | |
| 7229 function isShadowHost(shadowHost) { | |
| 7230 return shadowHost.shadowRoot; | |
| 7231 } | |
| 7232 | |
| 7233 // Returns the shadow trees as an array, with the youngest tree at the | |
| 7234 // beginning of the array. | |
| 7235 function getShadowTrees(host) { | |
| 7236 var trees = []; | |
| 7237 | |
| 7238 for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) { | |
| 7239 trees.push(tree); | |
| 7240 } | |
| 7241 return trees; | |
| 7242 } | |
| 7243 | |
| 7244 function render(host) { | |
| 7245 new ShadowRenderer(host).render(); | |
| 7246 }; | |
| 7247 | |
| 7248 // Need to rerender shadow host when: | |
| 7249 // | |
| 7250 // - a direct child to the ShadowRoot is added or removed | |
| 7251 // - a direct child to the host is added or removed | |
| 7252 // - a new shadow root is created | |
| 7253 // - a direct child to a content/shadow element is added or removed | |
| 7254 // - a sibling to a content/shadow element is added or removed | |
| 7255 // - content[select] is changed | |
| 7256 // - an attribute in a direct child to a host is modified | |
| 7257 | |
| 7258 /** | |
| 7259 * This gets called when a node was added or removed to it. | |
| 7260 */ | |
| 7261 Node.prototype.invalidateShadowRenderer = function(force) { | |
| 7262 var renderer = unsafeUnwrap(this).polymerShadowRenderer_; | |
| 7263 if (renderer) { | |
| 7264 renderer.invalidate(); | |
| 7265 return true; | |
| 7266 } | |
| 7267 | |
| 7268 return false; | |
| 7269 }; | |
| 7270 | |
| 7271 HTMLContentElement.prototype.getDistributedNodes = | |
| 7272 HTMLShadowElement.prototype.getDistributedNodes = function() { | |
| 7273 // TODO(arv): We should only rerender the dirty ancestor renderers (from | |
| 7274 // the root and down). | |
| 7275 renderAllPending(); | |
| 7276 return getDistributedNodes(this); | |
| 7277 }; | |
| 7278 | |
| 7279 Element.prototype.getDestinationInsertionPoints = function() { | |
| 7280 renderAllPending(); | |
| 7281 return getDestinationInsertionPoints(this) || []; | |
| 7282 }; | |
| 7283 | |
| 7284 HTMLContentElement.prototype.nodeIsInserted_ = | |
| 7285 HTMLShadowElement.prototype.nodeIsInserted_ = function() { | |
| 7286 // Invalidate old renderer if any. | |
| 7287 this.invalidateShadowRenderer(); | |
| 7288 | |
| 7289 var shadowRoot = getShadowRootAncestor(this); | |
| 7290 var renderer; | |
| 7291 if (shadowRoot) | |
| 7292 renderer = getRendererForShadowRoot(shadowRoot); | |
| 7293 unsafeUnwrap(this).polymerShadowRenderer_ = renderer; | |
| 7294 if (renderer) | |
| 7295 renderer.invalidate(); | |
| 7296 }; | |
| 7297 | |
| 7298 scope.getRendererForHost = getRendererForHost; | |
| 7299 scope.getShadowTrees = getShadowTrees; | |
| 7300 scope.renderAllPending = renderAllPending; | |
| 7301 | |
| 7302 scope.getDestinationInsertionPoints = getDestinationInsertionPoints; | |
| 7303 | |
| 7304 // Exposed for testing | |
| 7305 scope.visual = { | |
| 7306 insertBefore: insertBefore, | |
| 7307 remove: remove, | |
| 7308 }; | |
| 7309 | |
| 7310 })(window.ShadowDOMPolyfill); | |
| 7311 | |
| 7312 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 7313 // Use of this source code is goverened by a BSD-style | |
| 7314 // license that can be found in the LICENSE file. | |
| 7315 | |
| 7316 (function(scope) { | |
| 7317 'use strict'; | |
| 7318 | |
| 7319 var HTMLElement = scope.wrappers.HTMLElement; | |
| 7320 var assert = scope.assert; | |
| 7321 var mixin = scope.mixin; | |
| 7322 var registerWrapper = scope.registerWrapper; | |
| 7323 var unwrap = scope.unwrap; | |
| 7324 var wrap = scope.wrap; | |
| 7325 | |
| 7326 var elementsWithFormProperty = [ | |
| 7327 'HTMLButtonElement', | |
| 7328 'HTMLFieldSetElement', | |
| 7329 'HTMLInputElement', | |
| 7330 'HTMLKeygenElement', | |
| 7331 'HTMLLabelElement', | |
| 7332 'HTMLLegendElement', | |
| 7333 'HTMLObjectElement', | |
| 7334 // HTMLOptionElement is handled in HTMLOptionElement.js | |
| 7335 'HTMLOutputElement', | |
| 7336 // HTMLSelectElement is handled in HTMLSelectElement.js | |
| 7337 'HTMLTextAreaElement', | |
| 7338 ]; | |
| 7339 | |
| 7340 function createWrapperConstructor(name) { | |
| 7341 if (!window[name]) | |
| 7342 return; | |
| 7343 | |
| 7344 // Ensure we are not overriding an already existing constructor. | |
| 7345 assert(!scope.wrappers[name]); | |
| 7346 | |
| 7347 var GeneratedWrapper = function(node) { | |
| 7348 // At this point all of them extend HTMLElement. | |
| 7349 HTMLElement.call(this, node); | |
| 7350 } | |
| 7351 GeneratedWrapper.prototype = Object.create(HTMLElement.prototype); | |
| 7352 mixin(GeneratedWrapper.prototype, { | |
| 7353 get form() { | |
| 7354 return wrap(unwrap(this).form); | |
| 7355 }, | |
| 7356 }); | |
| 7357 | |
| 7358 registerWrapper(window[name], GeneratedWrapper, | |
| 7359 document.createElement(name.slice(4, -7))); | |
| 7360 scope.wrappers[name] = GeneratedWrapper; | |
| 7361 } | |
| 7362 | |
| 7363 elementsWithFormProperty.forEach(createWrapperConstructor); | |
| 7364 | |
| 7365 })(window.ShadowDOMPolyfill); | |
| 7366 | |
| 7367 // Copyright 2014 The Polymer Authors. All rights reserved. | |
| 7368 // Use of this source code is goverened by a BSD-style | |
| 7369 // license that can be found in the LICENSE file. | |
| 7370 | |
| 7371 (function(scope) { | |
| 7372 'use strict'; | |
| 7373 | |
| 7374 var registerWrapper = scope.registerWrapper; | |
| 7375 var setWrapper = scope.setWrapper; | |
| 7376 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 7377 var unwrap = scope.unwrap; | |
| 7378 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 7379 var wrap = scope.wrap; | |
| 7380 | |
| 7381 var OriginalSelection = window.Selection; | |
| 7382 | |
| 7383 function Selection(impl) { | |
| 7384 setWrapper(impl, this); | |
| 7385 } | |
| 7386 Selection.prototype = { | |
| 7387 get anchorNode() { | |
| 7388 return wrap(unsafeUnwrap(this).anchorNode); | |
| 7389 }, | |
| 7390 get focusNode() { | |
| 7391 return wrap(unsafeUnwrap(this).focusNode); | |
| 7392 }, | |
| 7393 addRange: function(range) { | |
| 7394 unsafeUnwrap(this).addRange(unwrap(range)); | |
| 7395 }, | |
| 7396 collapse: function(node, index) { | |
| 7397 unsafeUnwrap(this).collapse(unwrapIfNeeded(node), index); | |
| 7398 }, | |
| 7399 containsNode: function(node, allowPartial) { | |
| 7400 return unsafeUnwrap(this).containsNode(unwrapIfNeeded(node), allowPartial)
; | |
| 7401 }, | |
| 7402 extend: function(node, offset) { | |
| 7403 unsafeUnwrap(this).extend(unwrapIfNeeded(node), offset); | |
| 7404 }, | |
| 7405 getRangeAt: function(index) { | |
| 7406 return wrap(unsafeUnwrap(this).getRangeAt(index)); | |
| 7407 }, | |
| 7408 removeRange: function(range) { | |
| 7409 unsafeUnwrap(this).removeRange(unwrap(range)); | |
| 7410 }, | |
| 7411 selectAllChildren: function(node) { | |
| 7412 unsafeUnwrap(this).selectAllChildren(unwrapIfNeeded(node)); | |
| 7413 }, | |
| 7414 toString: function() { | |
| 7415 return unsafeUnwrap(this).toString(); | |
| 7416 } | |
| 7417 }; | |
| 7418 | |
| 7419 // WebKit extensions. Not implemented. | |
| 7420 // readonly attribute Node baseNode; | |
| 7421 // readonly attribute long baseOffset; | |
| 7422 // readonly attribute Node extentNode; | |
| 7423 // readonly attribute long extentOffset; | |
| 7424 // [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node b
aseNode, | |
| 7425 // [Default=Undefined] optional long baseOffset, | |
| 7426 // [Default=Undefined] optional Node extentNode, | |
| 7427 // [Default=Undefined] optional long extentOffset); | |
| 7428 // [RaisesException, ImplementedAs=collapse] void setPosition([Default=Undefin
ed] optional Node node, | |
| 7429 // [Default=Undefined] optional long offset); | |
| 7430 | |
| 7431 registerWrapper(window.Selection, Selection, window.getSelection()); | |
| 7432 | |
| 7433 scope.wrappers.Selection = Selection; | |
| 7434 | |
| 7435 })(window.ShadowDOMPolyfill); | |
| 7436 | |
| 7437 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 7438 // Use of this source code is goverened by a BSD-style | |
| 7439 // license that can be found in the LICENSE file. | |
| 7440 | |
| 7441 (function(scope) { | |
| 7442 'use strict'; | |
| 7443 | |
| 7444 var GetElementsByInterface = scope.GetElementsByInterface; | |
| 7445 var Node = scope.wrappers.Node; | |
| 7446 var ParentNodeInterface = scope.ParentNodeInterface; | |
| 7447 var Selection = scope.wrappers.Selection; | |
| 7448 var SelectorsInterface = scope.SelectorsInterface; | |
| 7449 var ShadowRoot = scope.wrappers.ShadowRoot; | |
| 7450 var TreeScope = scope.TreeScope; | |
| 7451 var cloneNode = scope.cloneNode; | |
| 7452 var defineWrapGetter = scope.defineWrapGetter; | |
| 7453 var elementFromPoint = scope.elementFromPoint; | |
| 7454 var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; | |
| 7455 var matchesNames = scope.matchesNames; | |
| 7456 var mixin = scope.mixin; | |
| 7457 var registerWrapper = scope.registerWrapper; | |
| 7458 var renderAllPending = scope.renderAllPending; | |
| 7459 var rewrap = scope.rewrap; | |
| 7460 var setWrapper = scope.setWrapper; | |
| 7461 var unsafeUnwrap = scope.unsafeUnwrap; | |
| 7462 var unwrap = scope.unwrap; | |
| 7463 var wrap = scope.wrap; | |
| 7464 var wrapEventTargetMethods = scope.wrapEventTargetMethods; | |
| 7465 var wrapNodeList = scope.wrapNodeList; | |
| 7466 | |
| 7467 var implementationTable = new WeakMap(); | |
| 7468 | |
| 7469 function Document(node) { | |
| 7470 Node.call(this, node); | |
| 7471 this.treeScope_ = new TreeScope(this, null); | |
| 7472 } | |
| 7473 Document.prototype = Object.create(Node.prototype); | |
| 7474 | |
| 7475 defineWrapGetter(Document, 'documentElement'); | |
| 7476 | |
| 7477 // Conceptually both body and head can be in a shadow but suporting that seems | |
| 7478 // overkill at this point. | |
| 7479 defineWrapGetter(Document, 'body'); | |
| 7480 defineWrapGetter(Document, 'head'); | |
| 7481 | |
| 7482 // document cannot be overridden so we override a bunch of its methods | |
| 7483 // directly on the instance. | |
| 7484 | |
| 7485 function wrapMethod(name) { | |
| 7486 var original = document[name]; | |
| 7487 Document.prototype[name] = function() { | |
| 7488 return wrap(original.apply(unsafeUnwrap(this), arguments)); | |
| 7489 }; | |
| 7490 } | |
| 7491 | |
| 7492 [ | |
| 7493 'createComment', | |
| 7494 'createDocumentFragment', | |
| 7495 'createElement', | |
| 7496 'createElementNS', | |
| 7497 'createEvent', | |
| 7498 'createEventNS', | |
| 7499 'createRange', | |
| 7500 'createTextNode', | |
| 7501 'getElementById' | |
| 7502 ].forEach(wrapMethod); | |
| 7503 | |
| 7504 var originalAdoptNode = document.adoptNode; | |
| 7505 | |
| 7506 function adoptNodeNoRemove(node, doc) { | |
| 7507 originalAdoptNode.call(unsafeUnwrap(doc), unwrap(node)); | |
| 7508 adoptSubtree(node, doc); | |
| 7509 } | |
| 7510 | |
| 7511 function adoptSubtree(node, doc) { | |
| 7512 if (node.shadowRoot) | |
| 7513 doc.adoptNode(node.shadowRoot); | |
| 7514 if (node instanceof ShadowRoot) | |
| 7515 adoptOlderShadowRoots(node, doc); | |
| 7516 for (var child = node.firstChild; child; child = child.nextSibling) { | |
| 7517 adoptSubtree(child, doc); | |
| 7518 } | |
| 7519 } | |
| 7520 | |
| 7521 function adoptOlderShadowRoots(shadowRoot, doc) { | |
| 7522 var oldShadowRoot = shadowRoot.olderShadowRoot; | |
| 7523 if (oldShadowRoot) | |
| 7524 doc.adoptNode(oldShadowRoot); | |
| 7525 } | |
| 7526 | |
| 7527 var originalGetSelection = document.getSelection; | |
| 7528 | |
| 7529 mixin(Document.prototype, { | |
| 7530 adoptNode: function(node) { | |
| 7531 if (node.parentNode) | |
| 7532 node.parentNode.removeChild(node); | |
| 7533 adoptNodeNoRemove(node, this); | |
| 7534 return node; | |
| 7535 }, | |
| 7536 elementFromPoint: function(x, y) { | |
| 7537 return elementFromPoint(this, this, x, y); | |
| 7538 }, | |
| 7539 importNode: function(node, deep) { | |
| 7540 return cloneNode(node, deep, unsafeUnwrap(this)); | |
| 7541 }, | |
| 7542 getSelection: function() { | |
| 7543 renderAllPending(); | |
| 7544 return new Selection(originalGetSelection.call(unwrap(this))); | |
| 7545 }, | |
| 7546 getElementsByName: function(name) { | |
| 7547 return SelectorsInterface.querySelectorAll.call(this, | |
| 7548 '[name=' + JSON.stringify(String(name)) + ']'); | |
| 7549 } | |
| 7550 }); | |
| 7551 | |
| 7552 if (document.registerElement) { | |
| 7553 var originalRegisterElement = document.registerElement; | |
| 7554 Document.prototype.registerElement = function(tagName, object) { | |
| 7555 var prototype, extendsOption; | |
| 7556 if (object !== undefined) { | |
| 7557 prototype = object.prototype; | |
| 7558 extendsOption = object.extends; | |
| 7559 } | |
| 7560 | |
| 7561 if (!prototype) | |
| 7562 prototype = Object.create(HTMLElement.prototype); | |
| 7563 | |
| 7564 | |
| 7565 // If we already used the object as a prototype for another custom | |
| 7566 // element. | |
| 7567 if (scope.nativePrototypeTable.get(prototype)) { | |
| 7568 // TODO(arv): DOMException | |
| 7569 throw new Error('NotSupportedError'); | |
| 7570 } | |
| 7571 | |
| 7572 // Find first object on the prototype chain that already have a native | |
| 7573 // prototype. Keep track of all the objects before that so we can create | |
| 7574 // a similar structure for the native case. | |
| 7575 var proto = Object.getPrototypeOf(prototype); | |
| 7576 var nativePrototype; | |
| 7577 var prototypes = []; | |
| 7578 while (proto) { | |
| 7579 nativePrototype = scope.nativePrototypeTable.get(proto); | |
| 7580 if (nativePrototype) | |
| 7581 break; | |
| 7582 prototypes.push(proto); | |
| 7583 proto = Object.getPrototypeOf(proto); | |
| 7584 } | |
| 7585 | |
| 7586 if (!nativePrototype) { | |
| 7587 // TODO(arv): DOMException | |
| 7588 throw new Error('NotSupportedError'); | |
| 7589 } | |
| 7590 | |
| 7591 // This works by creating a new prototype object that is empty, but has | |
| 7592 // the native prototype as its proto. The original prototype object | |
| 7593 // passed into register is used as the wrapper prototype. | |
| 7594 | |
| 7595 var newPrototype = Object.create(nativePrototype); | |
| 7596 for (var i = prototypes.length - 1; i >= 0; i--) { | |
| 7597 newPrototype = Object.create(newPrototype); | |
| 7598 } | |
| 7599 | |
| 7600 // Add callbacks if present. | |
| 7601 // Names are taken from: | |
| 7602 // https://code.google.com/p/chromium/codesearch#chromium/src/third_part
y/WebKit/Source/bindings/v8/CustomElementConstructorBuilder.cpp&sq=package:chrom
ium&type=cs&l=156 | |
| 7603 // and not from the spec since the spec is out of date. | |
| 7604 [ | |
| 7605 'createdCallback', | |
| 7606 'attachedCallback', | |
| 7607 'detachedCallback', | |
| 7608 'attributeChangedCallback', | |
| 7609 ].forEach(function(name) { | |
| 7610 var f = prototype[name]; | |
| 7611 if (!f) | |
| 7612 return; | |
| 7613 newPrototype[name] = function() { | |
| 7614 // if this element has been wrapped prior to registration, | |
| 7615 // the wrapper is stale; in this case rewrap | |
| 7616 if (!(wrap(this) instanceof CustomElementConstructor)) { | |
| 7617 rewrap(this); | |
| 7618 } | |
| 7619 f.apply(wrap(this), arguments); | |
| 7620 }; | |
| 7621 }); | |
| 7622 | |
| 7623 var p = {prototype: newPrototype}; | |
| 7624 if (extendsOption) | |
| 7625 p.extends = extendsOption; | |
| 7626 | |
| 7627 function CustomElementConstructor(node) { | |
| 7628 if (!node) { | |
| 7629 if (extendsOption) { | |
| 7630 return document.createElement(extendsOption, tagName); | |
| 7631 } else { | |
| 7632 return document.createElement(tagName); | |
| 7633 } | |
| 7634 } | |
| 7635 setWrapper(node, this); | |
| 7636 } | |
| 7637 CustomElementConstructor.prototype = prototype; | |
| 7638 CustomElementConstructor.prototype.constructor = CustomElementConstructor; | |
| 7639 | |
| 7640 scope.constructorTable.set(newPrototype, CustomElementConstructor); | |
| 7641 scope.nativePrototypeTable.set(prototype, newPrototype); | |
| 7642 | |
| 7643 // registration is synchronous so do it last | |
| 7644 var nativeConstructor = originalRegisterElement.call(unwrap(this), | |
| 7645 tagName, p); | |
| 7646 return CustomElementConstructor; | |
| 7647 }; | |
| 7648 | |
| 7649 forwardMethodsToWrapper([ | |
| 7650 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocume
nt | |
| 7651 ], [ | |
| 7652 'registerElement', | |
| 7653 ]); | |
| 7654 } | |
| 7655 | |
| 7656 // We also override some of the methods on document.body and document.head | |
| 7657 // for convenience. | |
| 7658 forwardMethodsToWrapper([ | |
| 7659 window.HTMLBodyElement, | |
| 7660 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
| 7661 window.HTMLHeadElement, | |
| 7662 window.HTMLHtmlElement, | |
| 7663 ], [ | |
| 7664 'appendChild', | |
| 7665 'compareDocumentPosition', | |
| 7666 'contains', | |
| 7667 'getElementsByClassName', | |
| 7668 'getElementsByTagName', | |
| 7669 'getElementsByTagNameNS', | |
| 7670 'insertBefore', | |
| 7671 'querySelector', | |
| 7672 'querySelectorAll', | |
| 7673 'removeChild', | |
| 7674 'replaceChild', | |
| 7675 ].concat(matchesNames)); | |
| 7676 | |
| 7677 forwardMethodsToWrapper([ | |
| 7678 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
| 7679 ], [ | |
| 7680 'adoptNode', | |
| 7681 'importNode', | |
| 7682 'contains', | |
| 7683 'createComment', | |
| 7684 'createDocumentFragment', | |
| 7685 'createElement', | |
| 7686 'createElementNS', | |
| 7687 'createEvent', | |
| 7688 'createEventNS', | |
| 7689 'createRange', | |
| 7690 'createTextNode', | |
| 7691 'elementFromPoint', | |
| 7692 'getElementById', | |
| 7693 'getElementsByName', | |
| 7694 'getSelection', | |
| 7695 ]); | |
| 7696 | |
| 7697 mixin(Document.prototype, GetElementsByInterface); | |
| 7698 mixin(Document.prototype, ParentNodeInterface); | |
| 7699 mixin(Document.prototype, SelectorsInterface); | |
| 7700 | |
| 7701 mixin(Document.prototype, { | |
| 7702 get implementation() { | |
| 7703 var implementation = implementationTable.get(this); | |
| 7704 if (implementation) | |
| 7705 return implementation; | |
| 7706 implementation = | |
| 7707 new DOMImplementation(unwrap(this).implementation); | |
| 7708 implementationTable.set(this, implementation); | |
| 7709 return implementation; | |
| 7710 }, | |
| 7711 | |
| 7712 get defaultView() { | |
| 7713 return wrap(unwrap(this).defaultView); | |
| 7714 } | |
| 7715 }); | |
| 7716 | |
| 7717 registerWrapper(window.Document, Document, | |
| 7718 document.implementation.createHTMLDocument('')); | |
| 7719 | |
| 7720 // Both WebKit and Gecko uses HTMLDocument for document. HTML5/DOM only has | |
| 7721 // one Document interface and IE implements the standard correctly. | |
| 7722 if (window.HTMLDocument) | |
| 7723 registerWrapper(window.HTMLDocument, Document); | |
| 7724 | |
| 7725 wrapEventTargetMethods([ | |
| 7726 window.HTMLBodyElement, | |
| 7727 window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument | |
| 7728 window.HTMLHeadElement, | |
| 7729 ]); | |
| 7730 | |
| 7731 function DOMImplementation(impl) { | |
| 7732 setWrapper(impl, this); | |
| 7733 } | |
| 7734 | |
| 7735 function wrapImplMethod(constructor, name) { | |
| 7736 var original = document.implementation[name]; | |
| 7737 constructor.prototype[name] = function() { | |
| 7738 return wrap(original.apply(unsafeUnwrap(this), arguments)); | |
| 7739 }; | |
| 7740 } | |
| 7741 | |
| 7742 function forwardImplMethod(constructor, name) { | |
| 7743 var original = document.implementation[name]; | |
| 7744 constructor.prototype[name] = function() { | |
| 7745 return original.apply(unsafeUnwrap(this), arguments); | |
| 7746 }; | |
| 7747 } | |
| 7748 | |
| 7749 wrapImplMethod(DOMImplementation, 'createDocumentType'); | |
| 7750 wrapImplMethod(DOMImplementation, 'createDocument'); | |
| 7751 wrapImplMethod(DOMImplementation, 'createHTMLDocument'); | |
| 7752 forwardImplMethod(DOMImplementation, 'hasFeature'); | |
| 7753 | |
| 7754 registerWrapper(window.DOMImplementation, DOMImplementation); | |
| 7755 | |
| 7756 forwardMethodsToWrapper([ | |
| 7757 window.DOMImplementation, | |
| 7758 ], [ | |
| 7759 'createDocumentType', | |
| 7760 'createDocument', | |
| 7761 'createHTMLDocument', | |
| 7762 'hasFeature', | |
| 7763 ]); | |
| 7764 | |
| 7765 scope.adoptNodeNoRemove = adoptNodeNoRemove; | |
| 7766 scope.wrappers.DOMImplementation = DOMImplementation; | |
| 7767 scope.wrappers.Document = Document; | |
| 7768 | |
| 7769 })(window.ShadowDOMPolyfill); | |
| 7770 | |
| 7771 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 7772 // Use of this source code is goverened by a BSD-style | |
| 7773 // license that can be found in the LICENSE file. | |
| 7774 | |
| 7775 (function(scope) { | |
| 7776 'use strict'; | |
| 7777 | |
| 7778 var EventTarget = scope.wrappers.EventTarget; | |
| 7779 var Selection = scope.wrappers.Selection; | |
| 7780 var mixin = scope.mixin; | |
| 7781 var registerWrapper = scope.registerWrapper; | |
| 7782 var renderAllPending = scope.renderAllPending; | |
| 7783 var unwrap = scope.unwrap; | |
| 7784 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 7785 var wrap = scope.wrap; | |
| 7786 | |
| 7787 var OriginalWindow = window.Window; | |
| 7788 var originalGetComputedStyle = window.getComputedStyle; | |
| 7789 var originalGetDefaultComputedStyle = window.getDefaultComputedStyle; | |
| 7790 var originalGetSelection = window.getSelection; | |
| 7791 | |
| 7792 function Window(impl) { | |
| 7793 EventTarget.call(this, impl); | |
| 7794 } | |
| 7795 Window.prototype = Object.create(EventTarget.prototype); | |
| 7796 | |
| 7797 OriginalWindow.prototype.getComputedStyle = function(el, pseudo) { | |
| 7798 return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo); | |
| 7799 }; | |
| 7800 | |
| 7801 // Mozilla proprietary extension. | |
| 7802 if (originalGetDefaultComputedStyle) { | |
| 7803 OriginalWindow.prototype.getDefaultComputedStyle = function(el, pseudo) { | |
| 7804 return wrap(this || window).getDefaultComputedStyle( | |
| 7805 unwrapIfNeeded(el), pseudo); | |
| 7806 }; | |
| 7807 } | |
| 7808 | |
| 7809 OriginalWindow.prototype.getSelection = function() { | |
| 7810 return wrap(this || window).getSelection(); | |
| 7811 }; | |
| 7812 | |
| 7813 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065 | |
| 7814 delete window.getComputedStyle; | |
| 7815 delete window.getDefaultComputedStyle; | |
| 7816 delete window.getSelection; | |
| 7817 | |
| 7818 ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach( | |
| 7819 function(name) { | |
| 7820 OriginalWindow.prototype[name] = function() { | |
| 7821 var w = wrap(this || window); | |
| 7822 return w[name].apply(w, arguments); | |
| 7823 }; | |
| 7824 | |
| 7825 // Work around for https://bugzilla.mozilla.org/show_bug.cgi?id=943065 | |
| 7826 delete window[name]; | |
| 7827 }); | |
| 7828 | |
| 7829 mixin(Window.prototype, { | |
| 7830 getComputedStyle: function(el, pseudo) { | |
| 7831 renderAllPending(); | |
| 7832 return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), | |
| 7833 pseudo); | |
| 7834 }, | |
| 7835 getSelection: function() { | |
| 7836 renderAllPending(); | |
| 7837 return new Selection(originalGetSelection.call(unwrap(this))); | |
| 7838 }, | |
| 7839 | |
| 7840 get document() { | |
| 7841 return wrap(unwrap(this).document); | |
| 7842 } | |
| 7843 }); | |
| 7844 | |
| 7845 // Mozilla proprietary extension. | |
| 7846 if (originalGetDefaultComputedStyle) { | |
| 7847 Window.prototype.getDefaultComputedStyle = function(el, pseudo) { | |
| 7848 renderAllPending(); | |
| 7849 return originalGetDefaultComputedStyle.call(unwrap(this), | |
| 7850 unwrapIfNeeded(el),pseudo); | |
| 7851 }; | |
| 7852 } | |
| 7853 | |
| 7854 registerWrapper(OriginalWindow, Window, window); | |
| 7855 | |
| 7856 scope.wrappers.Window = Window; | |
| 7857 | |
| 7858 })(window.ShadowDOMPolyfill); | |
| 7859 | |
| 7860 /** | |
| 7861 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 7862 * Use of this source code is goverened by a BSD-style | |
| 7863 * license that can be found in the LICENSE file. | |
| 7864 */ | |
| 7865 | |
| 7866 (function(scope) { | |
| 7867 'use strict'; | |
| 7868 | |
| 7869 var unwrap = scope.unwrap; | |
| 7870 | |
| 7871 // DataTransfer (Clipboard in old Blink/WebKit) has a single method that | |
| 7872 // requires wrapping. Since it is only a method we do not need a real wrapper, | |
| 7873 // we can just override the method. | |
| 7874 | |
| 7875 var OriginalDataTransfer = window.DataTransfer || window.Clipboard; | |
| 7876 var OriginalDataTransferSetDragImage = | |
| 7877 OriginalDataTransfer.prototype.setDragImage; | |
| 7878 | |
| 7879 if (OriginalDataTransferSetDragImage) { | |
| 7880 OriginalDataTransfer.prototype.setDragImage = function(image, x, y) { | |
| 7881 OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y); | |
| 7882 }; | |
| 7883 } | |
| 7884 | |
| 7885 })(window.ShadowDOMPolyfill); | |
| 7886 | |
| 7887 /** | |
| 7888 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 7889 * Use of this source code is goverened by a BSD-style | |
| 7890 * license that can be found in the LICENSE file. | |
| 7891 */ | |
| 7892 | |
| 7893 (function(scope) { | |
| 7894 'use strict'; | |
| 7895 | |
| 7896 var registerWrapper = scope.registerWrapper; | |
| 7897 var setWrapper = scope.setWrapper; | |
| 7898 var unwrap = scope.unwrap; | |
| 7899 | |
| 7900 var OriginalFormData = window.FormData; | |
| 7901 if (!OriginalFormData) return; | |
| 7902 | |
| 7903 function FormData(formElement) { | |
| 7904 var impl; | |
| 7905 if (formElement instanceof OriginalFormData) { | |
| 7906 impl = formElement; | |
| 7907 } else { | |
| 7908 impl = new OriginalFormData(formElement && unwrap(formElement)); | |
| 7909 } | |
| 7910 setWrapper(impl, this); | |
| 7911 } | |
| 7912 | |
| 7913 registerWrapper(OriginalFormData, FormData, new OriginalFormData()); | |
| 7914 | |
| 7915 scope.wrappers.FormData = FormData; | |
| 7916 | |
| 7917 })(window.ShadowDOMPolyfill); | |
| 7918 | |
| 7919 /* | |
| 7920 * Copyright 2014 The Polymer Authors. All rights reserved. | |
| 7921 * Use of this source code is goverened by a BSD-style | |
| 7922 * license that can be found in the LICENSE file. | |
| 7923 */ | |
| 7924 | |
| 7925 (function(scope) { | |
| 7926 'use strict'; | |
| 7927 | |
| 7928 var unwrapIfNeeded = scope.unwrapIfNeeded; | |
| 7929 var originalSend = XMLHttpRequest.prototype.send; | |
| 7930 | |
| 7931 // Since we only need to adjust XHR.send, we just patch it instead of wrapping | |
| 7932 // the entire object. This happens when FormData is passed. | |
| 7933 XMLHttpRequest.prototype.send = function(obj) { | |
| 7934 return originalSend.call(this, unwrapIfNeeded(obj)); | |
| 7935 }; | |
| 7936 | |
| 7937 })(window.ShadowDOMPolyfill); | |
| 7938 | |
| 7939 // Copyright 2013 The Polymer Authors. All rights reserved. | |
| 7940 // Use of this source code is goverened by a BSD-style | |
| 7941 // license that can be found in the LICENSE file. | |
| 7942 | |
| 7943 (function(scope) { | |
| 7944 'use strict'; | |
| 7945 | |
| 7946 var isWrapperFor = scope.isWrapperFor; | |
| 7947 | |
| 7948 // This is a list of the elements we currently override the global constructor | |
| 7949 // for. | |
| 7950 var elements = { | |
| 7951 'a': 'HTMLAnchorElement', | |
| 7952 // Do not create an applet element by default since it shows a warning in | |
| 7953 // IE. | |
| 7954 // https://github.com/Polymer/polymer/issues/217 | |
| 7955 // 'applet': 'HTMLAppletElement', | |
| 7956 'area': 'HTMLAreaElement', | |
| 7957 'audio': 'HTMLAudioElement', | |
| 7958 'base': 'HTMLBaseElement', | |
| 7959 'body': 'HTMLBodyElement', | |
| 7960 'br': 'HTMLBRElement', | |
| 7961 'button': 'HTMLButtonElement', | |
| 7962 'canvas': 'HTMLCanvasElement', | |
| 7963 'caption': 'HTMLTableCaptionElement', | |
| 7964 'col': 'HTMLTableColElement', | |
| 7965 // 'command': 'HTMLCommandElement', // Not fully implemented in Gecko. | |
| 7966 'content': 'HTMLContentElement', | |
| 7967 'data': 'HTMLDataElement', | |
| 7968 'datalist': 'HTMLDataListElement', | |
| 7969 'del': 'HTMLModElement', | |
| 7970 'dir': 'HTMLDirectoryElement', | |
| 7971 'div': 'HTMLDivElement', | |
| 7972 'dl': 'HTMLDListElement', | |
| 7973 'embed': 'HTMLEmbedElement', | |
| 7974 'fieldset': 'HTMLFieldSetElement', | |
| 7975 'font': 'HTMLFontElement', | |
| 7976 'form': 'HTMLFormElement', | |
| 7977 'frame': 'HTMLFrameElement', | |
| 7978 'frameset': 'HTMLFrameSetElement', | |
| 7979 'h1': 'HTMLHeadingElement', | |
| 7980 'head': 'HTMLHeadElement', | |
| 7981 'hr': 'HTMLHRElement', | |
| 7982 'html': 'HTMLHtmlElement', | |
| 7983 'iframe': 'HTMLIFrameElement', | |
| 7984 'img': 'HTMLImageElement', | |
| 7985 'input': 'HTMLInputElement', | |
| 7986 'keygen': 'HTMLKeygenElement', | |
| 7987 'label': 'HTMLLabelElement', | |
| 7988 'legend': 'HTMLLegendElement', | |
| 7989 'li': 'HTMLLIElement', | |
| 7990 'link': 'HTMLLinkElement', | |
| 7991 'map': 'HTMLMapElement', | |
| 7992 'marquee': 'HTMLMarqueeElement', | |
| 7993 'menu': 'HTMLMenuElement', | |
| 7994 'menuitem': 'HTMLMenuItemElement', | |
| 7995 'meta': 'HTMLMetaElement', | |
| 7996 'meter': 'HTMLMeterElement', | |
| 7997 'object': 'HTMLObjectElement', | |
| 7998 'ol': 'HTMLOListElement', | |
| 7999 'optgroup': 'HTMLOptGroupElement', | |
| 8000 'option': 'HTMLOptionElement', | |
| 8001 'output': 'HTMLOutputElement', | |
| 8002 'p': 'HTMLParagraphElement', | |
| 8003 'param': 'HTMLParamElement', | |
| 8004 'pre': 'HTMLPreElement', | |
| 8005 'progress': 'HTMLProgressElement', | |
| 8006 'q': 'HTMLQuoteElement', | |
| 8007 'script': 'HTMLScriptElement', | |
| 8008 'select': 'HTMLSelectElement', | |
| 8009 'shadow': 'HTMLShadowElement', | |
| 8010 'source': 'HTMLSourceElement', | |
| 8011 'span': 'HTMLSpanElement', | |
| 8012 'style': 'HTMLStyleElement', | |
| 8013 'table': 'HTMLTableElement', | |
| 8014 'tbody': 'HTMLTableSectionElement', | |
| 8015 // WebKit and Moz are wrong: | |
| 8016 // https://bugs.webkit.org/show_bug.cgi?id=111469 | |
| 8017 // https://bugzilla.mozilla.org/show_bug.cgi?id=848096 | |
| 8018 // 'td': 'HTMLTableCellElement', | |
| 8019 'template': 'HTMLTemplateElement', | |
| 8020 'textarea': 'HTMLTextAreaElement', | |
| 8021 'thead': 'HTMLTableSectionElement', | |
| 8022 'time': 'HTMLTimeElement', | |
| 8023 'title': 'HTMLTitleElement', | |
| 8024 'tr': 'HTMLTableRowElement', | |
| 8025 'track': 'HTMLTrackElement', | |
| 8026 'ul': 'HTMLUListElement', | |
| 8027 'video': 'HTMLVideoElement', | |
| 8028 }; | |
| 8029 | |
| 8030 function overrideConstructor(tagName) { | |
| 8031 var nativeConstructorName = elements[tagName]; | |
| 8032 var nativeConstructor = window[nativeConstructorName]; | |
| 8033 if (!nativeConstructor) | |
| 8034 return; | |
| 8035 var element = document.createElement(tagName); | |
| 8036 var wrapperConstructor = element.constructor; | |
| 8037 window[nativeConstructorName] = wrapperConstructor; | |
| 8038 } | |
| 8039 | |
| 8040 Object.keys(elements).forEach(overrideConstructor); | |
| 8041 | |
| 8042 Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) { | |
| 8043 window[name] = scope.wrappers[name] | |
| 8044 }); | |
| 8045 | |
| 8046 })(window.ShadowDOMPolyfill); | |
| 8047 | |
| 8048 /* | |
| 8049 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 8050 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 8051 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 8052 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 8053 * Code distributed by Google as part of the polymer project is also | |
| 8054 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 8055 */ | |
| 8056 | |
| 8057 (function(scope) { | |
| 8058 | |
| 8059 // convenient global | |
| 8060 window.wrap = ShadowDOMPolyfill.wrapIfNeeded; | |
| 8061 window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded; | |
| 8062 | |
| 8063 // users may want to customize other types | |
| 8064 // TODO(sjmiles): 'button' is now supported by ShadowDOMPolyfill, but | |
| 8065 // I've left this code here in case we need to temporarily patch another | |
| 8066 // type | |
| 8067 /* | |
| 8068 (function() { | |
| 8069 var elts = {HTMLButtonElement: 'button'}; | |
| 8070 for (var c in elts) { | |
| 8071 window[c] = function() { throw 'Patched Constructor'; }; | |
| 8072 window[c].prototype = Object.getPrototypeOf( | |
| 8073 document.createElement(elts[c])); | |
| 8074 } | |
| 8075 })(); | |
| 8076 */ | |
| 8077 | |
| 8078 // patch in prefixed name | |
| 8079 Object.defineProperty(Element.prototype, 'webkitShadowRoot', | |
| 8080 Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot')); | |
| 8081 | |
| 8082 var originalCreateShadowRoot = Element.prototype.createShadowRoot; | |
| 8083 Element.prototype.createShadowRoot = function() { | |
| 8084 var root = originalCreateShadowRoot.call(this); | |
| 8085 CustomElements.watchShadow(this); | |
| 8086 return root; | |
| 8087 }; | |
| 8088 | |
| 8089 Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; | |
| 8090 | |
| 8091 function queryShadow(node, selector) { | |
| 8092 var m, el = node.firstElementChild; | |
| 8093 var shadows, sr, i; | |
| 8094 shadows = []; | |
| 8095 sr = node.shadowRoot; | |
| 8096 while(sr) { | |
| 8097 shadows.push(sr); | |
| 8098 sr = sr.olderShadowRoot; | |
| 8099 } | |
| 8100 for(i = shadows.length - 1; i >= 0; i--) { | |
| 8101 m = shadows[i].querySelector(selector); | |
| 8102 if (m) { | |
| 8103 return m; | |
| 8104 } | |
| 8105 } | |
| 8106 while(el) { | |
| 8107 m = queryShadow(el, selector); | |
| 8108 if (m) { | |
| 8109 return m; | |
| 8110 } | |
| 8111 el = el.nextElementSibling; | |
| 8112 } | |
| 8113 return null; | |
| 8114 } | |
| 8115 | |
| 8116 function queryAllShadows(node, selector, results) { | |
| 8117 var el = node.firstElementChild; | |
| 8118 var temp, sr, shadows, i, j; | |
| 8119 shadows = []; | |
| 8120 sr = node.shadowRoot; | |
| 8121 while(sr) { | |
| 8122 shadows.push(sr); | |
| 8123 sr = sr.olderShadowRoot; | |
| 8124 } | |
| 8125 for (i = shadows.length - 1; i >= 0; i--) { | |
| 8126 temp = shadows[i].querySelectorAll(selector); | |
| 8127 for(j = 0; j < temp.length; j++) { | |
| 8128 results.push(temp[j]); | |
| 8129 } | |
| 8130 } | |
| 8131 while (el) { | |
| 8132 queryAllShadows(el, selector, results); | |
| 8133 el = el.nextElementSibling; | |
| 8134 } | |
| 8135 return results; | |
| 8136 } | |
| 8137 | |
| 8138 scope.queryAllShadows = function(node, selector, all) { | |
| 8139 if (all) { | |
| 8140 return queryAllShadows(node, selector, []); | |
| 8141 } else { | |
| 8142 return queryShadow(node, selector); | |
| 8143 } | |
| 8144 }; | |
| 8145 })(window.Platform); | |
| 8146 | |
| 8147 /* | |
| 8148 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 8149 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 8150 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 8151 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 8152 * Code distributed by Google as part of the polymer project is also | |
| 8153 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 8154 */ | |
| 8155 | |
| 8156 /* | |
| 8157 This is a limited shim for ShadowDOM css styling. | |
| 8158 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#style
s | |
| 8159 | |
| 8160 The intention here is to support only the styling features which can be | |
| 8161 relatively simply implemented. The goal is to allow users to avoid the | |
| 8162 most obvious pitfalls and do so without compromising performance significantly
. | |
| 8163 For ShadowDOM styling that's not covered here, a set of best practices | |
| 8164 can be provided that should allow users to accomplish more complex styling. | |
| 8165 | |
| 8166 The following is a list of specific ShadowDOM styling features and a brief | |
| 8167 discussion of the approach used to shim. | |
| 8168 | |
| 8169 Shimmed features: | |
| 8170 | |
| 8171 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host | |
| 8172 element using the :host rule. To shim this feature, the :host styles are | |
| 8173 reformatted and prefixed with a given scope name and promoted to a | |
| 8174 document level stylesheet. | |
| 8175 For example, given a scope name of .foo, a rule like this: | |
| 8176 | |
| 8177 :host { | |
| 8178 background: red; | |
| 8179 } | |
| 8180 } | |
| 8181 | |
| 8182 becomes: | |
| 8183 | |
| 8184 .foo { | |
| 8185 background: red; | |
| 8186 } | |
| 8187 | |
| 8188 * encapsultion: Styles defined within ShadowDOM, apply only to | |
| 8189 dom inside the ShadowDOM. Polymer uses one of two techniques to imlement | |
| 8190 this feature. | |
| 8191 | |
| 8192 By default, rules are prefixed with the host element tag name | |
| 8193 as a descendant selector. This ensures styling does not leak out of the 'top' | |
| 8194 of the element's ShadowDOM. For example, | |
| 8195 | |
| 8196 div { | |
| 8197 font-weight: bold; | |
| 8198 } | |
| 8199 | |
| 8200 becomes: | |
| 8201 | |
| 8202 x-foo div { | |
| 8203 font-weight: bold; | |
| 8204 } | |
| 8205 | |
| 8206 becomes: | |
| 8207 | |
| 8208 | |
| 8209 Alternatively, if Platform.ShadowCSS.strictStyling is set to true then | |
| 8210 selectors are scoped by adding an attribute selector suffix to each | |
| 8211 simple selector that contains the host element tag name. Each element | |
| 8212 in the element's ShadowDOM template is also given the scope attribute. | |
| 8213 Thus, these rules match only elements that have the scope attribute. | |
| 8214 For example, given a scope name of x-foo, a rule like this: | |
| 8215 | |
| 8216 div { | |
| 8217 font-weight: bold; | |
| 8218 } | |
| 8219 | |
| 8220 becomes: | |
| 8221 | |
| 8222 div[x-foo] { | |
| 8223 font-weight: bold; | |
| 8224 } | |
| 8225 | |
| 8226 Note that elements that are dynamically added to a scope must have the scope | |
| 8227 selector added to them manually. | |
| 8228 | |
| 8229 * upper/lower bound encapsulation: Styles which are defined outside a | |
| 8230 shadowRoot should not cross the ShadowDOM boundary and should not apply | |
| 8231 inside a shadowRoot. | |
| 8232 | |
| 8233 This styling behavior is not emulated. Some possible ways to do this that | |
| 8234 were rejected due to complexity and/or performance concerns include: (1) reset | |
| 8235 every possible property for every possible selector for a given scope name; | |
| 8236 (2) re-implement css in javascript. | |
| 8237 | |
| 8238 As an alternative, users should make sure to use selectors | |
| 8239 specific to the scope in which they are working. | |
| 8240 | |
| 8241 * ::distributed: This behavior is not emulated. It's often not necessary | |
| 8242 to style the contents of a specific insertion point and instead, descendants | |
| 8243 of the host element can be styled selectively. Users can also create an | |
| 8244 extra node around an insertion point and style that node's contents | |
| 8245 via descendent selectors. For example, with a shadowRoot like this: | |
| 8246 | |
| 8247 <style> | |
| 8248 ::content(div) { | |
| 8249 background: red; | |
| 8250 } | |
| 8251 </style> | |
| 8252 <content></content> | |
| 8253 | |
| 8254 could become: | |
| 8255 | |
| 8256 <style> | |
| 8257 / *@polyfill .content-container div * / | |
| 8258 ::content(div) { | |
| 8259 background: red; | |
| 8260 } | |
| 8261 </style> | |
| 8262 <div class="content-container"> | |
| 8263 <content></content> | |
| 8264 </div> | |
| 8265 | |
| 8266 Note the use of @polyfill in the comment above a ShadowDOM specific style | |
| 8267 declaration. This is a directive to the styling shim to use the selector | |
| 8268 in comments in lieu of the next selector when running under polyfill. | |
| 8269 */ | |
| 8270 (function(scope) { | |
| 8271 | |
| 8272 var ShadowCSS = { | |
| 8273 strictStyling: false, | |
| 8274 registry: {}, | |
| 8275 // Shim styles for a given root associated with a name and extendsName | |
| 8276 // 1. cache root styles by name | |
| 8277 // 2. optionally tag root nodes with scope name | |
| 8278 // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */ | |
| 8279 // 4. shim :host and scoping | |
| 8280 shimStyling: function(root, name, extendsName) { | |
| 8281 var scopeStyles = this.prepareRoot(root, name, extendsName); | |
| 8282 var typeExtension = this.isTypeExtension(extendsName); | |
| 8283 var scopeSelector = this.makeScopeSelector(name, typeExtension); | |
| 8284 // use caching to make working with styles nodes easier and to facilitate | |
| 8285 // lookup of extendee | |
| 8286 var cssText = stylesToCssText(scopeStyles, true); | |
| 8287 cssText = this.scopeCssText(cssText, scopeSelector); | |
| 8288 // cache shimmed css on root for user extensibility | |
| 8289 if (root) { | |
| 8290 root.shimmedStyle = cssText; | |
| 8291 } | |
| 8292 // add style to document | |
| 8293 this.addCssToDocument(cssText, name); | |
| 8294 }, | |
| 8295 /* | |
| 8296 * Shim a style element with the given selector. Returns cssText that can | |
| 8297 * be included in the document via Platform.ShadowCSS.addCssToDocument(css). | |
| 8298 */ | |
| 8299 shimStyle: function(style, selector) { | |
| 8300 return this.shimCssText(style.textContent, selector); | |
| 8301 }, | |
| 8302 /* | |
| 8303 * Shim some cssText with the given selector. Returns cssText that can | |
| 8304 * be included in the document via Platform.ShadowCSS.addCssToDocument(css). | |
| 8305 */ | |
| 8306 shimCssText: function(cssText, selector) { | |
| 8307 cssText = this.insertDirectives(cssText); | |
| 8308 return this.scopeCssText(cssText, selector); | |
| 8309 }, | |
| 8310 makeScopeSelector: function(name, typeExtension) { | |
| 8311 if (name) { | |
| 8312 return typeExtension ? '[is=' + name + ']' : name; | |
| 8313 } | |
| 8314 return ''; | |
| 8315 }, | |
| 8316 isTypeExtension: function(extendsName) { | |
| 8317 return extendsName && extendsName.indexOf('-') < 0; | |
| 8318 }, | |
| 8319 prepareRoot: function(root, name, extendsName) { | |
| 8320 var def = this.registerRoot(root, name, extendsName); | |
| 8321 this.replaceTextInStyles(def.rootStyles, this.insertDirectives); | |
| 8322 // remove existing style elements | |
| 8323 this.removeStyles(root, def.rootStyles); | |
| 8324 // apply strict attr | |
| 8325 if (this.strictStyling) { | |
| 8326 this.applyScopeToContent(root, name); | |
| 8327 } | |
| 8328 return def.scopeStyles; | |
| 8329 }, | |
| 8330 removeStyles: function(root, styles) { | |
| 8331 for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { | |
| 8332 s.parentNode.removeChild(s); | |
| 8333 } | |
| 8334 }, | |
| 8335 registerRoot: function(root, name, extendsName) { | |
| 8336 var def = this.registry[name] = { | |
| 8337 root: root, | |
| 8338 name: name, | |
| 8339 extendsName: extendsName | |
| 8340 } | |
| 8341 var styles = this.findStyles(root); | |
| 8342 def.rootStyles = styles; | |
| 8343 def.scopeStyles = def.rootStyles; | |
| 8344 var extendee = this.registry[def.extendsName]; | |
| 8345 if (extendee) { | |
| 8346 def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles); | |
| 8347 } | |
| 8348 return def; | |
| 8349 }, | |
| 8350 findStyles: function(root) { | |
| 8351 if (!root) { | |
| 8352 return []; | |
| 8353 } | |
| 8354 var styles = root.querySelectorAll('style'); | |
| 8355 return Array.prototype.filter.call(styles, function(s) { | |
| 8356 return !s.hasAttribute(NO_SHIM_ATTRIBUTE); | |
| 8357 }); | |
| 8358 }, | |
| 8359 applyScopeToContent: function(root, name) { | |
| 8360 if (root) { | |
| 8361 // add the name attribute to each node in root. | |
| 8362 Array.prototype.forEach.call(root.querySelectorAll('*'), | |
| 8363 function(node) { | |
| 8364 node.setAttribute(name, ''); | |
| 8365 }); | |
| 8366 // and template contents too | |
| 8367 Array.prototype.forEach.call(root.querySelectorAll('template'), | |
| 8368 function(template) { | |
| 8369 this.applyScopeToContent(template.content, name); | |
| 8370 }, | |
| 8371 this); | |
| 8372 } | |
| 8373 }, | |
| 8374 insertDirectives: function(cssText) { | |
| 8375 cssText = this.insertPolyfillDirectivesInCssText(cssText); | |
| 8376 return this.insertPolyfillRulesInCssText(cssText); | |
| 8377 }, | |
| 8378 /* | |
| 8379 * Process styles to convert native ShadowDOM rules that will trip | |
| 8380 * up the css parser; we rely on decorating the stylesheet with inert rules. | |
| 8381 * | |
| 8382 * For example, we convert this rule: | |
| 8383 * | |
| 8384 * polyfill-next-selector { content: ':host menu-item'; } | |
| 8385 * ::content menu-item { | |
| 8386 * | |
| 8387 * to this: | |
| 8388 * | |
| 8389 * scopeName menu-item { | |
| 8390 * | |
| 8391 **/ | |
| 8392 insertPolyfillDirectivesInCssText: function(cssText) { | |
| 8393 // TODO(sorvell): remove either content or comment | |
| 8394 cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) { | |
| 8395 // remove end comment delimiter and add block start | |
| 8396 return p1.slice(0, -2) + '{'; | |
| 8397 }); | |
| 8398 return cssText.replace(cssContentNextSelectorRe, function(match, p1) { | |
| 8399 return p1 + ' {'; | |
| 8400 }); | |
| 8401 }, | |
| 8402 /* | |
| 8403 * Process styles to add rules which will only apply under the polyfill | |
| 8404 * | |
| 8405 * For example, we convert this rule: | |
| 8406 * | |
| 8407 * polyfill-rule { | |
| 8408 * content: ':host menu-item'; | |
| 8409 * ... | |
| 8410 * } | |
| 8411 * | |
| 8412 * to this: | |
| 8413 * | |
| 8414 * scopeName menu-item {...} | |
| 8415 * | |
| 8416 **/ | |
| 8417 insertPolyfillRulesInCssText: function(cssText) { | |
| 8418 // TODO(sorvell): remove either content or comment | |
| 8419 cssText = cssText.replace(cssCommentRuleRe, function(match, p1) { | |
| 8420 // remove end comment delimiter | |
| 8421 return p1.slice(0, -1); | |
| 8422 }); | |
| 8423 return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) { | |
| 8424 var rule = match.replace(p1, '').replace(p2, ''); | |
| 8425 return p3 + rule; | |
| 8426 }); | |
| 8427 }, | |
| 8428 /* Ensure styles are scoped. Pseudo-scoping takes a rule like: | |
| 8429 * | |
| 8430 * .foo {... } | |
| 8431 * | |
| 8432 * and converts this to | |
| 8433 * | |
| 8434 * scopeName .foo { ... } | |
| 8435 */ | |
| 8436 scopeCssText: function(cssText, scopeSelector) { | |
| 8437 var unscoped = this.extractUnscopedRulesFromCssText(cssText); | |
| 8438 cssText = this.insertPolyfillHostInCssText(cssText); | |
| 8439 cssText = this.convertColonHost(cssText); | |
| 8440 cssText = this.convertColonHostContext(cssText); | |
| 8441 cssText = this.convertShadowDOMSelectors(cssText); | |
| 8442 if (scopeSelector) { | |
| 8443 var self = this, cssText; | |
| 8444 withCssRules(cssText, function(rules) { | |
| 8445 cssText = self.scopeRules(rules, scopeSelector); | |
| 8446 }); | |
| 8447 | |
| 8448 } | |
| 8449 cssText = cssText + '\n' + unscoped; | |
| 8450 return cssText.trim(); | |
| 8451 }, | |
| 8452 /* | |
| 8453 * Process styles to add rules which will only apply under the polyfill | |
| 8454 * and do not process via CSSOM. (CSSOM is destructive to rules on rare | |
| 8455 * occasions, e.g. -webkit-calc on Safari.) | |
| 8456 * For example, we convert this rule: | |
| 8457 * | |
| 8458 * (comment start) @polyfill-unscoped-rule menu-item { | |
| 8459 * ... } (comment end) | |
| 8460 * | |
| 8461 * to this: | |
| 8462 * | |
| 8463 * menu-item {...} | |
| 8464 * | |
| 8465 **/ | |
| 8466 extractUnscopedRulesFromCssText: function(cssText) { | |
| 8467 // TODO(sorvell): remove either content or comment | |
| 8468 var r = '', m; | |
| 8469 while (m = cssCommentUnscopedRuleRe.exec(cssText)) { | |
| 8470 r += m[1].slice(0, -1) + '\n\n'; | |
| 8471 } | |
| 8472 while (m = cssContentUnscopedRuleRe.exec(cssText)) { | |
| 8473 r += m[0].replace(m[2], '').replace(m[1], m[3]) + '\n\n'; | |
| 8474 } | |
| 8475 return r; | |
| 8476 }, | |
| 8477 /* | |
| 8478 * convert a rule like :host(.foo) > .bar { } | |
| 8479 * | |
| 8480 * to | |
| 8481 * | |
| 8482 * scopeName.foo > .bar | |
| 8483 */ | |
| 8484 convertColonHost: function(cssText) { | |
| 8485 return this.convertColonRule(cssText, cssColonHostRe, | |
| 8486 this.colonHostPartReplacer); | |
| 8487 }, | |
| 8488 /* | |
| 8489 * convert a rule like :host-context(.foo) > .bar { } | |
| 8490 * | |
| 8491 * to | |
| 8492 * | |
| 8493 * scopeName.foo > .bar, .foo scopeName > .bar { } | |
| 8494 * | |
| 8495 * and | |
| 8496 * | |
| 8497 * :host-context(.foo:host) .bar { ... } | |
| 8498 * | |
| 8499 * to | |
| 8500 * | |
| 8501 * scopeName.foo .bar { ... } | |
| 8502 */ | |
| 8503 convertColonHostContext: function(cssText) { | |
| 8504 return this.convertColonRule(cssText, cssColonHostContextRe, | |
| 8505 this.colonHostContextPartReplacer); | |
| 8506 }, | |
| 8507 convertColonRule: function(cssText, regExp, partReplacer) { | |
| 8508 // p1 = :host, p2 = contents of (), p3 rest of rule | |
| 8509 return cssText.replace(regExp, function(m, p1, p2, p3) { | |
| 8510 p1 = polyfillHostNoCombinator; | |
| 8511 if (p2) { | |
| 8512 var parts = p2.split(','), r = []; | |
| 8513 for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) { | |
| 8514 p = p.trim(); | |
| 8515 r.push(partReplacer(p1, p, p3)); | |
| 8516 } | |
| 8517 return r.join(','); | |
| 8518 } else { | |
| 8519 return p1 + p3; | |
| 8520 } | |
| 8521 }); | |
| 8522 }, | |
| 8523 colonHostContextPartReplacer: function(host, part, suffix) { | |
| 8524 if (part.match(polyfillHost)) { | |
| 8525 return this.colonHostPartReplacer(host, part, suffix); | |
| 8526 } else { | |
| 8527 return host + part + suffix + ', ' + part + ' ' + host + suffix; | |
| 8528 } | |
| 8529 }, | |
| 8530 colonHostPartReplacer: function(host, part, suffix) { | |
| 8531 return host + part.replace(polyfillHost, '') + suffix; | |
| 8532 }, | |
| 8533 /* | |
| 8534 * Convert combinators like ::shadow and pseudo-elements like ::content | |
| 8535 * by replacing with space. | |
| 8536 */ | |
| 8537 convertShadowDOMSelectors: function(cssText) { | |
| 8538 for (var i=0; i < shadowDOMSelectorsRe.length; i++) { | |
| 8539 cssText = cssText.replace(shadowDOMSelectorsRe[i], ' '); | |
| 8540 } | |
| 8541 return cssText; | |
| 8542 }, | |
| 8543 // change a selector like 'div' to 'name div' | |
| 8544 scopeRules: function(cssRules, scopeSelector) { | |
| 8545 var cssText = ''; | |
| 8546 if (cssRules) { | |
| 8547 Array.prototype.forEach.call(cssRules, function(rule) { | |
| 8548 if (rule.selectorText && (rule.style && rule.style.cssText !== undefined
)) { | |
| 8549 cssText += this.scopeSelector(rule.selectorText, scopeSelector, | |
| 8550 this.strictStyling) + ' {\n\t'; | |
| 8551 cssText += this.propertiesFromRule(rule) + '\n}\n\n'; | |
| 8552 } else if (rule.type === CSSRule.MEDIA_RULE) { | |
| 8553 cssText += '@media ' + rule.media.mediaText + ' {\n'; | |
| 8554 cssText += this.scopeRules(rule.cssRules, scopeSelector); | |
| 8555 cssText += '\n}\n\n'; | |
| 8556 } else { | |
| 8557 // KEYFRAMES_RULE in IE throws when we query cssText | |
| 8558 // when it contains a -webkit- property. | |
| 8559 // if this happens, we fallback to constructing the rule | |
| 8560 // from the CSSRuleSet | |
| 8561 // https://connect.microsoft.com/IE/feedbackdetail/view/955703/accessi
ng-csstext-of-a-keyframe-rule-that-contains-a-webkit-property-via-cssom-generate
s-exception | |
| 8562 try { | |
| 8563 if (rule.cssText) { | |
| 8564 cssText += rule.cssText + '\n\n'; | |
| 8565 } | |
| 8566 } catch(x) { | |
| 8567 if (rule.type === CSSRule.KEYFRAMES_RULE && rule.cssRules) { | |
| 8568 cssText += this.ieSafeCssTextFromKeyFrameRule(rule); | |
| 8569 } | |
| 8570 } | |
| 8571 } | |
| 8572 }, this); | |
| 8573 } | |
| 8574 return cssText; | |
| 8575 }, | |
| 8576 ieSafeCssTextFromKeyFrameRule: function(rule) { | |
| 8577 var cssText = '@keyframes ' + rule.name + ' {'; | |
| 8578 Array.prototype.forEach.call(rule.cssRules, function(rule) { | |
| 8579 cssText += ' ' + rule.keyText + ' {' + rule.style.cssText + '}'; | |
| 8580 }); | |
| 8581 cssText += ' }'; | |
| 8582 return cssText; | |
| 8583 }, | |
| 8584 scopeSelector: function(selector, scopeSelector, strict) { | |
| 8585 var r = [], parts = selector.split(','); | |
| 8586 parts.forEach(function(p) { | |
| 8587 p = p.trim(); | |
| 8588 if (this.selectorNeedsScoping(p, scopeSelector)) { | |
| 8589 p = (strict && !p.match(polyfillHostNoCombinator)) ? | |
| 8590 this.applyStrictSelectorScope(p, scopeSelector) : | |
| 8591 this.applySelectorScope(p, scopeSelector); | |
| 8592 } | |
| 8593 r.push(p); | |
| 8594 }, this); | |
| 8595 return r.join(', '); | |
| 8596 }, | |
| 8597 selectorNeedsScoping: function(selector, scopeSelector) { | |
| 8598 if (Array.isArray(scopeSelector)) { | |
| 8599 return true; | |
| 8600 } | |
| 8601 var re = this.makeScopeMatcher(scopeSelector); | |
| 8602 return !selector.match(re); | |
| 8603 }, | |
| 8604 makeScopeMatcher: function(scopeSelector) { | |
| 8605 scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); | |
| 8606 return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); | |
| 8607 }, | |
| 8608 applySelectorScope: function(selector, selectorScope) { | |
| 8609 return Array.isArray(selectorScope) ? | |
| 8610 this.applySelectorScopeList(selector, selectorScope) : | |
| 8611 this.applySimpleSelectorScope(selector, selectorScope); | |
| 8612 }, | |
| 8613 // apply an array of selectors | |
| 8614 applySelectorScopeList: function(selector, scopeSelectorList) { | |
| 8615 var r = []; | |
| 8616 for (var i=0, s; (s=scopeSelectorList[i]); i++) { | |
| 8617 r.push(this.applySimpleSelectorScope(selector, s)); | |
| 8618 } | |
| 8619 return r.join(', '); | |
| 8620 }, | |
| 8621 // scope via name and [is=name] | |
| 8622 applySimpleSelectorScope: function(selector, scopeSelector) { | |
| 8623 if (selector.match(polyfillHostRe)) { | |
| 8624 selector = selector.replace(polyfillHostNoCombinator, scopeSelector); | |
| 8625 return selector.replace(polyfillHostRe, scopeSelector + ' '); | |
| 8626 } else { | |
| 8627 return scopeSelector + ' ' + selector; | |
| 8628 } | |
| 8629 }, | |
| 8630 // return a selector with [name] suffix on each simple selector | |
| 8631 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] | |
| 8632 applyStrictSelectorScope: function(selector, scopeSelector) { | |
| 8633 scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, '$1'); | |
| 8634 var splits = [' ', '>', '+', '~'], | |
| 8635 scoped = selector, | |
| 8636 attrName = '[' + scopeSelector + ']'; | |
| 8637 splits.forEach(function(sep) { | |
| 8638 var parts = scoped.split(sep); | |
| 8639 scoped = parts.map(function(p) { | |
| 8640 // remove :host since it should be unnecessary | |
| 8641 var t = p.trim().replace(polyfillHostRe, ''); | |
| 8642 if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) { | |
| 8643 p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3') | |
| 8644 } | |
| 8645 return p; | |
| 8646 }).join(sep); | |
| 8647 }); | |
| 8648 return scoped; | |
| 8649 }, | |
| 8650 insertPolyfillHostInCssText: function(selector) { | |
| 8651 return selector.replace(colonHostContextRe, polyfillHostContext).replace( | |
| 8652 colonHostRe, polyfillHost); | |
| 8653 }, | |
| 8654 propertiesFromRule: function(rule) { | |
| 8655 var cssText = rule.style.cssText; | |
| 8656 // TODO(sorvell): Safari cssom incorrectly removes quotes from the content | |
| 8657 // property. (https://bugs.webkit.org/show_bug.cgi?id=118045) | |
| 8658 // don't replace attr rules | |
| 8659 if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) { | |
| 8660 cssText = cssText.replace(/content:[^;]*;/g, 'content: \'' + | |
| 8661 rule.style.content + '\';'); | |
| 8662 } | |
| 8663 // TODO(sorvell): we can workaround this issue here, but we need a list | |
| 8664 // of troublesome properties to fix https://github.com/Polymer/platform/issu
es/53 | |
| 8665 // | |
| 8666 // inherit rules can be omitted from cssText | |
| 8667 // TODO(sorvell): remove when Blink bug is fixed: | |
| 8668 // https://code.google.com/p/chromium/issues/detail?id=358273 | |
| 8669 var style = rule.style; | |
| 8670 for (var i in style) { | |
| 8671 if (style[i] === 'initial') { | |
| 8672 cssText += i + ': initial; '; | |
| 8673 } | |
| 8674 } | |
| 8675 return cssText; | |
| 8676 }, | |
| 8677 replaceTextInStyles: function(styles, action) { | |
| 8678 if (styles && action) { | |
| 8679 if (!(styles instanceof Array)) { | |
| 8680 styles = [styles]; | |
| 8681 } | |
| 8682 Array.prototype.forEach.call(styles, function(s) { | |
| 8683 s.textContent = action.call(this, s.textContent); | |
| 8684 }, this); | |
| 8685 } | |
| 8686 }, | |
| 8687 addCssToDocument: function(cssText, name) { | |
| 8688 if (cssText.match('@import')) { | |
| 8689 addOwnSheet(cssText, name); | |
| 8690 } else { | |
| 8691 addCssToDocument(cssText); | |
| 8692 } | |
| 8693 } | |
| 8694 }; | |
| 8695 | |
| 8696 var selectorRe = /([^{]*)({[\s\S]*?})/gim, | |
| 8697 cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, | |
| 8698 // TODO(sorvell): remove either content or comment | |
| 8699 cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^
{]*?){/gim, | |
| 8700 cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*?['"](.
*?)['"][;\s]*}([^{]*?){/gim, | |
| 8701 // TODO(sorvell): remove either content or comment | |
| 8702 cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, | |
| 8703 cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[
^}]*}/gim, | |
| 8704 // TODO(sorvell): remove either content or comment | |
| 8705 cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]
*\*+)*)\//gim, | |
| 8706 cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"]
(.*?)['"])[;\s]*[^}]*}/gim, | |
| 8707 cssPseudoRe = /::(x-[^\s{,(]*)/gim, | |
| 8708 cssPartRe = /::part\(([^)]*)\)/gim, | |
| 8709 // note: :host pre-processed to -shadowcsshost. | |
| 8710 polyfillHost = '-shadowcsshost', | |
| 8711 // note: :host-context pre-processed to -shadowcsshostcontext. | |
| 8712 polyfillHostContext = '-shadowcsscontext', | |
| 8713 parenSuffix = ')(?:\\((' + | |
| 8714 '(?:\\([^)(]*\\)|[^)(]*)+?' + | |
| 8715 ')\\))?([^,{]*)'; | |
| 8716 cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'), | |
| 8717 cssColonHostContextRe = new RegExp('(' + polyfillHostContext + parenSuffix,
'gim'), | |
| 8718 selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$', | |
| 8719 colonHostRe = /\:host/gim, | |
| 8720 colonHostContextRe = /\:host-context/gim, | |
| 8721 /* host name without combinator */ | |
| 8722 polyfillHostNoCombinator = polyfillHost + '-no-combinator', | |
| 8723 polyfillHostRe = new RegExp(polyfillHost, 'gim'), | |
| 8724 polyfillHostContextRe = new RegExp(polyfillHostContext, 'gim'), | |
| 8725 shadowDOMSelectorsRe = [ | |
| 8726 /\^\^/g, | |
| 8727 /\^/g, | |
| 8728 /\/shadow\//g, | |
| 8729 /\/shadow-deep\//g, | |
| 8730 /::shadow/g, | |
| 8731 /\/deep\//g, | |
| 8732 /::content/g | |
| 8733 ]; | |
| 8734 | |
| 8735 function stylesToCssText(styles, preserveComments) { | |
| 8736 var cssText = ''; | |
| 8737 Array.prototype.forEach.call(styles, function(s) { | |
| 8738 cssText += s.textContent + '\n\n'; | |
| 8739 }); | |
| 8740 // strip comments for easier processing | |
| 8741 if (!preserveComments) { | |
| 8742 cssText = cssText.replace(cssCommentRe, ''); | |
| 8743 } | |
| 8744 return cssText; | |
| 8745 } | |
| 8746 | |
| 8747 function cssTextToStyle(cssText) { | |
| 8748 var style = document.createElement('style'); | |
| 8749 style.textContent = cssText; | |
| 8750 return style; | |
| 8751 } | |
| 8752 | |
| 8753 function cssToRules(cssText) { | |
| 8754 var style = cssTextToStyle(cssText); | |
| 8755 document.head.appendChild(style); | |
| 8756 var rules = []; | |
| 8757 if (style.sheet) { | |
| 8758 // TODO(sorvell): Firefox throws when accessing the rules of a stylesheet | |
| 8759 // with an @import | |
| 8760 // https://bugzilla.mozilla.org/show_bug.cgi?id=625013 | |
| 8761 try { | |
| 8762 rules = style.sheet.cssRules; | |
| 8763 } catch(e) { | |
| 8764 // | |
| 8765 } | |
| 8766 } else { | |
| 8767 console.warn('sheet not found', style); | |
| 8768 } | |
| 8769 style.parentNode.removeChild(style); | |
| 8770 return rules; | |
| 8771 } | |
| 8772 | |
| 8773 var frame = document.createElement('iframe'); | |
| 8774 frame.style.display = 'none'; | |
| 8775 | |
| 8776 function initFrame() { | |
| 8777 frame.initialized = true; | |
| 8778 document.body.appendChild(frame); | |
| 8779 var doc = frame.contentDocument; | |
| 8780 var base = doc.createElement('base'); | |
| 8781 base.href = document.baseURI; | |
| 8782 doc.head.appendChild(base); | |
| 8783 } | |
| 8784 | |
| 8785 function inFrame(fn) { | |
| 8786 if (!frame.initialized) { | |
| 8787 initFrame(); | |
| 8788 } | |
| 8789 document.body.appendChild(frame); | |
| 8790 fn(frame.contentDocument); | |
| 8791 document.body.removeChild(frame); | |
| 8792 } | |
| 8793 | |
| 8794 // TODO(sorvell): use an iframe if the cssText contains an @import to workaround | |
| 8795 // https://code.google.com/p/chromium/issues/detail?id=345114 | |
| 8796 var isChrome = navigator.userAgent.match('Chrome'); | |
| 8797 function withCssRules(cssText, callback) { | |
| 8798 if (!callback) { | |
| 8799 return; | |
| 8800 } | |
| 8801 var rules; | |
| 8802 if (cssText.match('@import') && isChrome) { | |
| 8803 var style = cssTextToStyle(cssText); | |
| 8804 inFrame(function(doc) { | |
| 8805 doc.head.appendChild(style.impl); | |
| 8806 rules = Array.prototype.slice.call(style.sheet.cssRules, 0); | |
| 8807 callback(rules); | |
| 8808 }); | |
| 8809 } else { | |
| 8810 rules = cssToRules(cssText); | |
| 8811 callback(rules); | |
| 8812 } | |
| 8813 } | |
| 8814 | |
| 8815 function rulesToCss(cssRules) { | |
| 8816 for (var i=0, css=[]; i < cssRules.length; i++) { | |
| 8817 css.push(cssRules[i].cssText); | |
| 8818 } | |
| 8819 return css.join('\n\n'); | |
| 8820 } | |
| 8821 | |
| 8822 function addCssToDocument(cssText) { | |
| 8823 if (cssText) { | |
| 8824 getSheet().appendChild(document.createTextNode(cssText)); | |
| 8825 } | |
| 8826 } | |
| 8827 | |
| 8828 function addOwnSheet(cssText, name) { | |
| 8829 var style = cssTextToStyle(cssText); | |
| 8830 style.setAttribute(name, ''); | |
| 8831 style.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
| 8832 document.head.appendChild(style); | |
| 8833 } | |
| 8834 | |
| 8835 var SHIM_ATTRIBUTE = 'shim-shadowdom'; | |
| 8836 var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css'; | |
| 8837 var NO_SHIM_ATTRIBUTE = 'no-shim'; | |
| 8838 | |
| 8839 var sheet; | |
| 8840 function getSheet() { | |
| 8841 if (!sheet) { | |
| 8842 sheet = document.createElement("style"); | |
| 8843 sheet.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
| 8844 sheet[SHIMMED_ATTRIBUTE] = true; | |
| 8845 } | |
| 8846 return sheet; | |
| 8847 } | |
| 8848 | |
| 8849 // add polyfill stylesheet to document | |
| 8850 if (window.ShadowDOMPolyfill) { | |
| 8851 addCssToDocument('style { display: none !important; }\n'); | |
| 8852 var doc = wrap(document); | |
| 8853 var head = doc.querySelector('head'); | |
| 8854 head.insertBefore(getSheet(), head.childNodes[0]); | |
| 8855 | |
| 8856 // TODO(sorvell): monkey-patching HTMLImports is abusive; | |
| 8857 // consider a better solution. | |
| 8858 document.addEventListener('DOMContentLoaded', function() { | |
| 8859 var urlResolver = scope.urlResolver; | |
| 8860 | |
| 8861 if (window.HTMLImports && !HTMLImports.useNative) { | |
| 8862 var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]' + | |
| 8863 '[' + SHIM_ATTRIBUTE + ']'; | |
| 8864 var SHIM_STYLE_SELECTOR = 'style[' + SHIM_ATTRIBUTE + ']'; | |
| 8865 HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR
; | |
| 8866 HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR; | |
| 8867 | |
| 8868 HTMLImports.parser.documentSelectors = [ | |
| 8869 HTMLImports.parser.documentSelectors, | |
| 8870 SHIM_SHEET_SELECTOR, | |
| 8871 SHIM_STYLE_SELECTOR | |
| 8872 ].join(','); | |
| 8873 | |
| 8874 var originalParseGeneric = HTMLImports.parser.parseGeneric; | |
| 8875 | |
| 8876 HTMLImports.parser.parseGeneric = function(elt) { | |
| 8877 if (elt[SHIMMED_ATTRIBUTE]) { | |
| 8878 return; | |
| 8879 } | |
| 8880 var style = elt.__importElement || elt; | |
| 8881 if (!style.hasAttribute(SHIM_ATTRIBUTE)) { | |
| 8882 originalParseGeneric.call(this, elt); | |
| 8883 return; | |
| 8884 } | |
| 8885 if (elt.__resource) { | |
| 8886 style = elt.ownerDocument.createElement('style'); | |
| 8887 style.textContent = elt.__resource; | |
| 8888 } | |
| 8889 // relay on HTMLImports for path fixup | |
| 8890 HTMLImports.path.resolveUrlsInStyle(style); | |
| 8891 style.textContent = ShadowCSS.shimStyle(style); | |
| 8892 style.removeAttribute(SHIM_ATTRIBUTE, ''); | |
| 8893 style.setAttribute(SHIMMED_ATTRIBUTE, ''); | |
| 8894 style[SHIMMED_ATTRIBUTE] = true; | |
| 8895 // place in document | |
| 8896 if (style.parentNode !== head) { | |
| 8897 // replace links in head | |
| 8898 if (elt.parentNode === head) { | |
| 8899 head.replaceChild(style, elt); | |
| 8900 } else { | |
| 8901 this.addElementToDocument(style); | |
| 8902 } | |
| 8903 } | |
| 8904 style.__importParsed = true; | |
| 8905 this.markParsingComplete(elt); | |
| 8906 this.parseNext(); | |
| 8907 } | |
| 8908 | |
| 8909 var hasResource = HTMLImports.parser.hasResource; | |
| 8910 HTMLImports.parser.hasResource = function(node) { | |
| 8911 if (node.localName === 'link' && node.rel === 'stylesheet' && | |
| 8912 node.hasAttribute(SHIM_ATTRIBUTE)) { | |
| 8913 return (node.__resource); | |
| 8914 } else { | |
| 8915 return hasResource.call(this, node); | |
| 8916 } | |
| 8917 } | |
| 8918 | |
| 8919 } | |
| 8920 }); | |
| 8921 } | |
| 8922 | |
| 8923 // exports | |
| 8924 scope.ShadowCSS = ShadowCSS; | |
| 8925 | |
| 8926 })(window.Platform); | |
| 8927 | |
| 8928 } else { | |
| 8929 /* | |
| 8930 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 8931 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 8932 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 8933 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 8934 * Code distributed by Google as part of the polymer project is also | |
| 8935 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 8936 */ | |
| 8937 | |
| 8938 (function(scope) { | |
| 8939 | |
| 8940 // so we can call wrap/unwrap without testing for ShadowDOMPolyfill | |
| 8941 window.wrap = window.unwrap = function(n){ | |
| 8942 return n; | |
| 8943 } | |
| 8944 | |
| 8945 addEventListener('DOMContentLoaded', function() { | |
| 8946 if (CustomElements.useNative === false) { | |
| 8947 var originalCreateShadowRoot = Element.prototype.createShadowRoot; | |
| 8948 Element.prototype.createShadowRoot = function() { | |
| 8949 var root = originalCreateShadowRoot.call(this); | |
| 8950 CustomElements.watchShadow(this); | |
| 8951 return root; | |
| 8952 }; | |
| 8953 } | |
| 8954 }); | |
| 8955 | |
| 8956 })(window.Platform); | |
| 8957 | |
| 8958 } | |
| 8959 /* Any copyright is dedicated to the Public Domain. | |
| 8960 * http://creativecommons.org/publicdomain/zero/1.0/ */ | |
| 8961 | |
| 8962 (function(scope) { | |
| 8963 'use strict'; | |
| 8964 | |
| 8965 // feature detect for URL constructor | |
| 8966 var hasWorkingUrl = false; | |
| 8967 if (!scope.forceJURL) { | |
| 8968 try { | |
| 8969 var u = new URL('b', 'http://a'); | |
| 8970 hasWorkingUrl = u.href === 'http://a/b'; | |
| 8971 } catch(e) {} | |
| 8972 } | |
| 8973 | |
| 8974 if (hasWorkingUrl) | |
| 8975 return; | |
| 8976 | |
| 8977 var relative = Object.create(null); | |
| 8978 relative['ftp'] = 21; | |
| 8979 relative['file'] = 0; | |
| 8980 relative['gopher'] = 70; | |
| 8981 relative['http'] = 80; | |
| 8982 relative['https'] = 443; | |
| 8983 relative['ws'] = 80; | |
| 8984 relative['wss'] = 443; | |
| 8985 | |
| 8986 var relativePathDotMapping = Object.create(null); | |
| 8987 relativePathDotMapping['%2e'] = '.'; | |
| 8988 relativePathDotMapping['.%2e'] = '..'; | |
| 8989 relativePathDotMapping['%2e.'] = '..'; | |
| 8990 relativePathDotMapping['%2e%2e'] = '..'; | |
| 8991 | |
| 8992 function isRelativeScheme(scheme) { | |
| 8993 return relative[scheme] !== undefined; | |
| 8994 } | |
| 8995 | |
| 8996 function invalid() { | |
| 8997 clear.call(this); | |
| 8998 this._isInvalid = true; | |
| 8999 } | |
| 9000 | |
| 9001 function IDNAToASCII(h) { | |
| 9002 if ('' == h) { | |
| 9003 invalid.call(this) | |
| 9004 } | |
| 9005 // XXX | |
| 9006 return h.toLowerCase() | |
| 9007 } | |
| 9008 | |
| 9009 function percentEscape(c) { | |
| 9010 var unicode = c.charCodeAt(0); | |
| 9011 if (unicode > 0x20 && | |
| 9012 unicode < 0x7F && | |
| 9013 // " # < > ? ` | |
| 9014 [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1 | |
| 9015 ) { | |
| 9016 return c; | |
| 9017 } | |
| 9018 return encodeURIComponent(c); | |
| 9019 } | |
| 9020 | |
| 9021 function percentEscapeQuery(c) { | |
| 9022 // XXX This actually needs to encode c using encoding and then | |
| 9023 // convert the bytes one-by-one. | |
| 9024 | |
| 9025 var unicode = c.charCodeAt(0); | |
| 9026 if (unicode > 0x20 && | |
| 9027 unicode < 0x7F && | |
| 9028 // " # < > ` (do not escape '?') | |
| 9029 [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1 | |
| 9030 ) { | |
| 9031 return c; | |
| 9032 } | |
| 9033 return encodeURIComponent(c); | |
| 9034 } | |
| 9035 | |
| 9036 var EOF = undefined, | |
| 9037 ALPHA = /[a-zA-Z]/, | |
| 9038 ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/; | |
| 9039 | |
| 9040 function parse(input, stateOverride, base) { | |
| 9041 function err(message) { | |
| 9042 errors.push(message) | |
| 9043 } | |
| 9044 | |
| 9045 var state = stateOverride || 'scheme start', | |
| 9046 cursor = 0, | |
| 9047 buffer = '', | |
| 9048 seenAt = false, | |
| 9049 seenBracket = false, | |
| 9050 errors = []; | |
| 9051 | |
| 9052 loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid)
{ | |
| 9053 var c = input[cursor]; | |
| 9054 switch (state) { | |
| 9055 case 'scheme start': | |
| 9056 if (c && ALPHA.test(c)) { | |
| 9057 buffer += c.toLowerCase(); // ASCII-safe | |
| 9058 state = 'scheme'; | |
| 9059 } else if (!stateOverride) { | |
| 9060 buffer = ''; | |
| 9061 state = 'no scheme'; | |
| 9062 continue; | |
| 9063 } else { | |
| 9064 err('Invalid scheme.'); | |
| 9065 break loop; | |
| 9066 } | |
| 9067 break; | |
| 9068 | |
| 9069 case 'scheme': | |
| 9070 if (c && ALPHANUMERIC.test(c)) { | |
| 9071 buffer += c.toLowerCase(); // ASCII-safe | |
| 9072 } else if (':' == c) { | |
| 9073 this._scheme = buffer; | |
| 9074 buffer = ''; | |
| 9075 if (stateOverride) { | |
| 9076 break loop; | |
| 9077 } | |
| 9078 if (isRelativeScheme(this._scheme)) { | |
| 9079 this._isRelative = true; | |
| 9080 } | |
| 9081 if ('file' == this._scheme) { | |
| 9082 state = 'relative'; | |
| 9083 } else if (this._isRelative && base && base._scheme == this._scheme)
{ | |
| 9084 state = 'relative or authority'; | |
| 9085 } else if (this._isRelative) { | |
| 9086 state = 'authority first slash'; | |
| 9087 } else { | |
| 9088 state = 'scheme data'; | |
| 9089 } | |
| 9090 } else if (!stateOverride) { | |
| 9091 buffer = ''; | |
| 9092 cursor = 0; | |
| 9093 state = 'no scheme'; | |
| 9094 continue; | |
| 9095 } else if (EOF == c) { | |
| 9096 break loop; | |
| 9097 } else { | |
| 9098 err('Code point not allowed in scheme: ' + c) | |
| 9099 break loop; | |
| 9100 } | |
| 9101 break; | |
| 9102 | |
| 9103 case 'scheme data': | |
| 9104 if ('?' == c) { | |
| 9105 query = '?'; | |
| 9106 state = 'query'; | |
| 9107 } else if ('#' == c) { | |
| 9108 this._fragment = '#'; | |
| 9109 state = 'fragment'; | |
| 9110 } else { | |
| 9111 // XXX error handling | |
| 9112 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
| 9113 this._schemeData += percentEscape(c); | |
| 9114 } | |
| 9115 } | |
| 9116 break; | |
| 9117 | |
| 9118 case 'no scheme': | |
| 9119 if (!base || !(isRelativeScheme(base._scheme))) { | |
| 9120 err('Missing scheme.'); | |
| 9121 invalid.call(this); | |
| 9122 } else { | |
| 9123 state = 'relative'; | |
| 9124 continue; | |
| 9125 } | |
| 9126 break; | |
| 9127 | |
| 9128 case 'relative or authority': | |
| 9129 if ('/' == c && '/' == input[cursor+1]) { | |
| 9130 state = 'authority ignore slashes'; | |
| 9131 } else { | |
| 9132 err('Expected /, got: ' + c); | |
| 9133 state = 'relative'; | |
| 9134 continue | |
| 9135 } | |
| 9136 break; | |
| 9137 | |
| 9138 case 'relative': | |
| 9139 this._isRelative = true; | |
| 9140 if ('file' != this._scheme) | |
| 9141 this._scheme = base._scheme; | |
| 9142 if (EOF == c) { | |
| 9143 this._host = base._host; | |
| 9144 this._port = base._port; | |
| 9145 this._path = base._path.slice(); | |
| 9146 this._query = base._query; | |
| 9147 break loop; | |
| 9148 } else if ('/' == c || '\\' == c) { | |
| 9149 if ('\\' == c) | |
| 9150 err('\\ is an invalid code point.'); | |
| 9151 state = 'relative slash'; | |
| 9152 } else if ('?' == c) { | |
| 9153 this._host = base._host; | |
| 9154 this._port = base._port; | |
| 9155 this._path = base._path.slice(); | |
| 9156 this._query = '?'; | |
| 9157 state = 'query'; | |
| 9158 } else if ('#' == c) { | |
| 9159 this._host = base._host; | |
| 9160 this._port = base._port; | |
| 9161 this._path = base._path.slice(); | |
| 9162 this._query = base._query; | |
| 9163 this._fragment = '#'; | |
| 9164 state = 'fragment'; | |
| 9165 } else { | |
| 9166 var nextC = input[cursor+1] | |
| 9167 var nextNextC = input[cursor+2] | |
| 9168 if ( | |
| 9169 'file' != this._scheme || !ALPHA.test(c) || | |
| 9170 (nextC != ':' && nextC != '|') || | |
| 9171 (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?'
!= nextNextC && '#' != nextNextC)) { | |
| 9172 this._host = base._host; | |
| 9173 this._port = base._port; | |
| 9174 this._path = base._path.slice(); | |
| 9175 this._path.pop(); | |
| 9176 } | |
| 9177 state = 'relative path'; | |
| 9178 continue; | |
| 9179 } | |
| 9180 break; | |
| 9181 | |
| 9182 case 'relative slash': | |
| 9183 if ('/' == c || '\\' == c) { | |
| 9184 if ('\\' == c) { | |
| 9185 err('\\ is an invalid code point.'); | |
| 9186 } | |
| 9187 if ('file' == this._scheme) { | |
| 9188 state = 'file host'; | |
| 9189 } else { | |
| 9190 state = 'authority ignore slashes'; | |
| 9191 } | |
| 9192 } else { | |
| 9193 if ('file' != this._scheme) { | |
| 9194 this._host = base._host; | |
| 9195 this._port = base._port; | |
| 9196 } | |
| 9197 state = 'relative path'; | |
| 9198 continue; | |
| 9199 } | |
| 9200 break; | |
| 9201 | |
| 9202 case 'authority first slash': | |
| 9203 if ('/' == c) { | |
| 9204 state = 'authority second slash'; | |
| 9205 } else { | |
| 9206 err("Expected '/', got: " + c); | |
| 9207 state = 'authority ignore slashes'; | |
| 9208 continue; | |
| 9209 } | |
| 9210 break; | |
| 9211 | |
| 9212 case 'authority second slash': | |
| 9213 state = 'authority ignore slashes'; | |
| 9214 if ('/' != c) { | |
| 9215 err("Expected '/', got: " + c); | |
| 9216 continue; | |
| 9217 } | |
| 9218 break; | |
| 9219 | |
| 9220 case 'authority ignore slashes': | |
| 9221 if ('/' != c && '\\' != c) { | |
| 9222 state = 'authority'; | |
| 9223 continue; | |
| 9224 } else { | |
| 9225 err('Expected authority, got: ' + c); | |
| 9226 } | |
| 9227 break; | |
| 9228 | |
| 9229 case 'authority': | |
| 9230 if ('@' == c) { | |
| 9231 if (seenAt) { | |
| 9232 err('@ already seen.'); | |
| 9233 buffer += '%40'; | |
| 9234 } | |
| 9235 seenAt = true; | |
| 9236 for (var i = 0; i < buffer.length; i++) { | |
| 9237 var cp = buffer[i]; | |
| 9238 if ('\t' == cp || '\n' == cp || '\r' == cp) { | |
| 9239 err('Invalid whitespace in authority.'); | |
| 9240 continue; | |
| 9241 } | |
| 9242 // XXX check URL code points | |
| 9243 if (':' == cp && null === this._password) { | |
| 9244 this._password = ''; | |
| 9245 continue; | |
| 9246 } | |
| 9247 var tempC = percentEscape(cp); | |
| 9248 (null !== this._password) ? this._password += tempC : this._userna
me += tempC; | |
| 9249 } | |
| 9250 buffer = ''; | |
| 9251 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c)
{ | |
| 9252 cursor -= buffer.length; | |
| 9253 buffer = ''; | |
| 9254 state = 'host'; | |
| 9255 continue; | |
| 9256 } else { | |
| 9257 buffer += c; | |
| 9258 } | |
| 9259 break; | |
| 9260 | |
| 9261 case 'file host': | |
| 9262 if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { | |
| 9263 if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':'
|| buffer[1] == '|')) { | |
| 9264 state = 'relative path'; | |
| 9265 } else if (buffer.length == 0) { | |
| 9266 state = 'relative path start'; | |
| 9267 } else { | |
| 9268 this._host = IDNAToASCII.call(this, buffer); | |
| 9269 buffer = ''; | |
| 9270 state = 'relative path start'; | |
| 9271 } | |
| 9272 continue; | |
| 9273 } else if ('\t' == c || '\n' == c || '\r' == c) { | |
| 9274 err('Invalid whitespace in file host.'); | |
| 9275 } else { | |
| 9276 buffer += c; | |
| 9277 } | |
| 9278 break; | |
| 9279 | |
| 9280 case 'host': | |
| 9281 case 'hostname': | |
| 9282 if (':' == c && !seenBracket) { | |
| 9283 // XXX host parsing | |
| 9284 this._host = IDNAToASCII.call(this, buffer); | |
| 9285 buffer = ''; | |
| 9286 state = 'port'; | |
| 9287 if ('hostname' == stateOverride) { | |
| 9288 break loop; | |
| 9289 } | |
| 9290 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c)
{ | |
| 9291 this._host = IDNAToASCII.call(this, buffer); | |
| 9292 buffer = ''; | |
| 9293 state = 'relative path start'; | |
| 9294 if (stateOverride) { | |
| 9295 break loop; | |
| 9296 } | |
| 9297 continue; | |
| 9298 } else if ('\t' != c && '\n' != c && '\r' != c) { | |
| 9299 if ('[' == c) { | |
| 9300 seenBracket = true; | |
| 9301 } else if (']' == c) { | |
| 9302 seenBracket = false; | |
| 9303 } | |
| 9304 buffer += c; | |
| 9305 } else { | |
| 9306 err('Invalid code point in host/hostname: ' + c); | |
| 9307 } | |
| 9308 break; | |
| 9309 | |
| 9310 case 'port': | |
| 9311 if (/[0-9]/.test(c)) { | |
| 9312 buffer += c; | |
| 9313 } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c |
| stateOverride) { | |
| 9314 if ('' != buffer) { | |
| 9315 var temp = parseInt(buffer, 10); | |
| 9316 if (temp != relative[this._scheme]) { | |
| 9317 this._port = temp + ''; | |
| 9318 } | |
| 9319 buffer = ''; | |
| 9320 } | |
| 9321 if (stateOverride) { | |
| 9322 break loop; | |
| 9323 } | |
| 9324 state = 'relative path start'; | |
| 9325 continue; | |
| 9326 } else if ('\t' == c || '\n' == c || '\r' == c) { | |
| 9327 err('Invalid code point in port: ' + c); | |
| 9328 } else { | |
| 9329 invalid.call(this); | |
| 9330 } | |
| 9331 break; | |
| 9332 | |
| 9333 case 'relative path start': | |
| 9334 if ('\\' == c) | |
| 9335 err("'\\' not allowed in path."); | |
| 9336 state = 'relative path'; | |
| 9337 if ('/' != c && '\\' != c) { | |
| 9338 continue; | |
| 9339 } | |
| 9340 break; | |
| 9341 | |
| 9342 case 'relative path': | |
| 9343 if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c
|| '#' == c))) { | |
| 9344 if ('\\' == c) { | |
| 9345 err('\\ not allowed in relative path.'); | |
| 9346 } | |
| 9347 var tmp; | |
| 9348 if (tmp = relativePathDotMapping[buffer.toLowerCase()]) { | |
| 9349 buffer = tmp; | |
| 9350 } | |
| 9351 if ('..' == buffer) { | |
| 9352 this._path.pop(); | |
| 9353 if ('/' != c && '\\' != c) { | |
| 9354 this._path.push(''); | |
| 9355 } | |
| 9356 } else if ('.' == buffer && '/' != c && '\\' != c) { | |
| 9357 this._path.push(''); | |
| 9358 } else if ('.' != buffer) { | |
| 9359 if ('file' == this._scheme && this._path.length == 0 && buffer.len
gth == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') { | |
| 9360 buffer = buffer[0] + ':'; | |
| 9361 } | |
| 9362 this._path.push(buffer); | |
| 9363 } | |
| 9364 buffer = ''; | |
| 9365 if ('?' == c) { | |
| 9366 this._query = '?'; | |
| 9367 state = 'query'; | |
| 9368 } else if ('#' == c) { | |
| 9369 this._fragment = '#'; | |
| 9370 state = 'fragment'; | |
| 9371 } | |
| 9372 } else if ('\t' != c && '\n' != c && '\r' != c) { | |
| 9373 buffer += percentEscape(c); | |
| 9374 } | |
| 9375 break; | |
| 9376 | |
| 9377 case 'query': | |
| 9378 if (!stateOverride && '#' == c) { | |
| 9379 this._fragment = '#'; | |
| 9380 state = 'fragment'; | |
| 9381 } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
| 9382 this._query += percentEscapeQuery(c); | |
| 9383 } | |
| 9384 break; | |
| 9385 | |
| 9386 case 'fragment': | |
| 9387 if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | |
| 9388 this._fragment += c; | |
| 9389 } | |
| 9390 break; | |
| 9391 } | |
| 9392 | |
| 9393 cursor++; | |
| 9394 } | |
| 9395 } | |
| 9396 | |
| 9397 function clear() { | |
| 9398 this._scheme = ''; | |
| 9399 this._schemeData = ''; | |
| 9400 this._username = ''; | |
| 9401 this._password = null; | |
| 9402 this._host = ''; | |
| 9403 this._port = ''; | |
| 9404 this._path = []; | |
| 9405 this._query = ''; | |
| 9406 this._fragment = ''; | |
| 9407 this._isInvalid = false; | |
| 9408 this._isRelative = false; | |
| 9409 } | |
| 9410 | |
| 9411 // Does not process domain names or IP addresses. | |
| 9412 // Does not handle encoding for the query parameter. | |
| 9413 function jURL(url, base /* , encoding */) { | |
| 9414 if (base !== undefined && !(base instanceof jURL)) | |
| 9415 base = new jURL(String(base)); | |
| 9416 | |
| 9417 this._url = url; | |
| 9418 clear.call(this); | |
| 9419 | |
| 9420 var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, ''); | |
| 9421 // encoding = encoding || 'utf-8' | |
| 9422 | |
| 9423 parse.call(this, input, null, base); | |
| 9424 } | |
| 9425 | |
| 9426 jURL.prototype = { | |
| 9427 get href() { | |
| 9428 if (this._isInvalid) | |
| 9429 return this._url; | |
| 9430 | |
| 9431 var authority = ''; | |
| 9432 if ('' != this._username || null != this._password) { | |
| 9433 authority = this._username + | |
| 9434 (null != this._password ? ':' + this._password : '') + '@'; | |
| 9435 } | |
| 9436 | |
| 9437 return this.protocol + | |
| 9438 (this._isRelative ? '//' + authority + this.host : '') + | |
| 9439 this.pathname + this._query + this._fragment; | |
| 9440 }, | |
| 9441 set href(href) { | |
| 9442 clear.call(this); | |
| 9443 parse.call(this, href); | |
| 9444 }, | |
| 9445 | |
| 9446 get protocol() { | |
| 9447 return this._scheme + ':'; | |
| 9448 }, | |
| 9449 set protocol(protocol) { | |
| 9450 if (this._isInvalid) | |
| 9451 return; | |
| 9452 parse.call(this, protocol + ':', 'scheme start'); | |
| 9453 }, | |
| 9454 | |
| 9455 get host() { | |
| 9456 return this._isInvalid ? '' : this._port ? | |
| 9457 this._host + ':' + this._port : this._host; | |
| 9458 }, | |
| 9459 set host(host) { | |
| 9460 if (this._isInvalid || !this._isRelative) | |
| 9461 return; | |
| 9462 parse.call(this, host, 'host'); | |
| 9463 }, | |
| 9464 | |
| 9465 get hostname() { | |
| 9466 return this._host; | |
| 9467 }, | |
| 9468 set hostname(hostname) { | |
| 9469 if (this._isInvalid || !this._isRelative) | |
| 9470 return; | |
| 9471 parse.call(this, hostname, 'hostname'); | |
| 9472 }, | |
| 9473 | |
| 9474 get port() { | |
| 9475 return this._port; | |
| 9476 }, | |
| 9477 set port(port) { | |
| 9478 if (this._isInvalid || !this._isRelative) | |
| 9479 return; | |
| 9480 parse.call(this, port, 'port'); | |
| 9481 }, | |
| 9482 | |
| 9483 get pathname() { | |
| 9484 return this._isInvalid ? '' : this._isRelative ? | |
| 9485 '/' + this._path.join('/') : this._schemeData; | |
| 9486 }, | |
| 9487 set pathname(pathname) { | |
| 9488 if (this._isInvalid || !this._isRelative) | |
| 9489 return; | |
| 9490 this._path = []; | |
| 9491 parse.call(this, pathname, 'relative path start'); | |
| 9492 }, | |
| 9493 | |
| 9494 get search() { | |
| 9495 return this._isInvalid || !this._query || '?' == this._query ? | |
| 9496 '' : this._query; | |
| 9497 }, | |
| 9498 set search(search) { | |
| 9499 if (this._isInvalid || !this._isRelative) | |
| 9500 return; | |
| 9501 this._query = '?'; | |
| 9502 if ('?' == search[0]) | |
| 9503 search = search.slice(1); | |
| 9504 parse.call(this, search, 'query'); | |
| 9505 }, | |
| 9506 | |
| 9507 get hash() { | |
| 9508 return this._isInvalid || !this._fragment || '#' == this._fragment ? | |
| 9509 '' : this._fragment; | |
| 9510 }, | |
| 9511 set hash(hash) { | |
| 9512 if (this._isInvalid) | |
| 9513 return; | |
| 9514 this._fragment = '#'; | |
| 9515 if ('#' == hash[0]) | |
| 9516 hash = hash.slice(1); | |
| 9517 parse.call(this, hash, 'fragment'); | |
| 9518 }, | |
| 9519 | |
| 9520 get origin() { | |
| 9521 var host; | |
| 9522 if (this._isInvalid || !this._scheme) { | |
| 9523 return ''; | |
| 9524 } | |
| 9525 // javascript: Gecko returns String(""), WebKit/Blink String("null") | |
| 9526 // Gecko throws error for "data://" | |
| 9527 // data: Gecko returns "", Blink returns "data://", WebKit returns "null" | |
| 9528 // Gecko returns String("") for file: mailto: | |
| 9529 // WebKit/Blink returns String("SCHEME://") for file: mailto: | |
| 9530 switch (this._scheme) { | |
| 9531 case 'data': | |
| 9532 case 'file': | |
| 9533 case 'javascript': | |
| 9534 case 'mailto': | |
| 9535 return 'null'; | |
| 9536 } | |
| 9537 host = this.host; | |
| 9538 if (!host) { | |
| 9539 return ''; | |
| 9540 } | |
| 9541 return this._scheme + '://' + host; | |
| 9542 } | |
| 9543 }; | |
| 9544 | |
| 9545 // Copy over the static methods | |
| 9546 var OriginalURL = scope.URL; | |
| 9547 if (OriginalURL) { | |
| 9548 jURL.createObjectURL = function(blob) { | |
| 9549 // IE extension allows a second optional options argument. | |
| 9550 // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx | |
| 9551 return OriginalURL.createObjectURL.apply(OriginalURL, arguments); | |
| 9552 }; | |
| 9553 jURL.revokeObjectURL = function(url) { | |
| 9554 OriginalURL.revokeObjectURL(url); | |
| 9555 }; | |
| 9556 } | |
| 9557 | |
| 9558 scope.URL = jURL; | |
| 9559 | |
| 9560 })(this); | |
| 9561 | |
| 9562 /* | |
| 9563 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 9564 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 9565 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 9566 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 9567 * Code distributed by Google as part of the polymer project is also | |
| 9568 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 9569 */ | |
| 9570 | |
| 9571 (function(scope) { | |
| 9572 | |
| 9573 // Old versions of iOS do not have bind. | |
| 9574 | |
| 9575 if (!Function.prototype.bind) { | |
| 9576 Function.prototype.bind = function(scope) { | |
| 9577 var self = this; | |
| 9578 var args = Array.prototype.slice.call(arguments, 1); | |
| 9579 return function() { | |
| 9580 var args2 = args.slice(); | |
| 9581 args2.push.apply(args2, arguments); | |
| 9582 return self.apply(scope, args2); | |
| 9583 }; | |
| 9584 }; | |
| 9585 } | |
| 9586 | |
| 9587 })(window.Platform); | |
| 9588 | |
| 9589 /* | |
| 9590 * Copyright 2012 The Polymer Authors. All rights reserved. | |
| 9591 * Use of this source code is goverened by a BSD-style | |
| 9592 * license that can be found in the LICENSE file. | |
| 9593 */ | |
| 9594 | |
| 9595 (function(global) { | |
| 9596 | |
| 9597 var registrationsTable = new WeakMap(); | |
| 9598 | |
| 9599 // We use setImmediate or postMessage for our future callback. | |
| 9600 var setImmediate = window.msSetImmediate; | |
| 9601 | |
| 9602 // Use post message to emulate setImmediate. | |
| 9603 if (!setImmediate) { | |
| 9604 var setImmediateQueue = []; | |
| 9605 var sentinel = String(Math.random()); | |
| 9606 window.addEventListener('message', function(e) { | |
| 9607 if (e.data === sentinel) { | |
| 9608 var queue = setImmediateQueue; | |
| 9609 setImmediateQueue = []; | |
| 9610 queue.forEach(function(func) { | |
| 9611 func(); | |
| 9612 }); | |
| 9613 } | |
| 9614 }); | |
| 9615 setImmediate = function(func) { | |
| 9616 setImmediateQueue.push(func); | |
| 9617 window.postMessage(sentinel, '*'); | |
| 9618 }; | |
| 9619 } | |
| 9620 | |
| 9621 // This is used to ensure that we never schedule 2 callas to setImmediate | |
| 9622 var isScheduled = false; | |
| 9623 | |
| 9624 // Keep track of observers that needs to be notified next time. | |
| 9625 var scheduledObservers = []; | |
| 9626 | |
| 9627 /** | |
| 9628 * Schedules |dispatchCallback| to be called in the future. | |
| 9629 * @param {MutationObserver} observer | |
| 9630 */ | |
| 9631 function scheduleCallback(observer) { | |
| 9632 scheduledObservers.push(observer); | |
| 9633 if (!isScheduled) { | |
| 9634 isScheduled = true; | |
| 9635 setImmediate(dispatchCallbacks); | |
| 9636 } | |
| 9637 } | |
| 9638 | |
| 9639 function wrapIfNeeded(node) { | |
| 9640 return window.ShadowDOMPolyfill && | |
| 9641 window.ShadowDOMPolyfill.wrapIfNeeded(node) || | |
| 9642 node; | |
| 9643 } | |
| 9644 | |
| 9645 function dispatchCallbacks() { | |
| 9646 // http://dom.spec.whatwg.org/#mutation-observers | |
| 9647 | |
| 9648 isScheduled = false; // Used to allow a new setImmediate call above. | |
| 9649 | |
| 9650 var observers = scheduledObservers; | |
| 9651 scheduledObservers = []; | |
| 9652 // Sort observers based on their creation UID (incremental). | |
| 9653 observers.sort(function(o1, o2) { | |
| 9654 return o1.uid_ - o2.uid_; | |
| 9655 }); | |
| 9656 | |
| 9657 var anyNonEmpty = false; | |
| 9658 observers.forEach(function(observer) { | |
| 9659 | |
| 9660 // 2.1, 2.2 | |
| 9661 var queue = observer.takeRecords(); | |
| 9662 // 2.3. Remove all transient registered observers whose observer is mo. | |
| 9663 removeTransientObserversFor(observer); | |
| 9664 | |
| 9665 // 2.4 | |
| 9666 if (queue.length) { | |
| 9667 observer.callback_(queue, observer); | |
| 9668 anyNonEmpty = true; | |
| 9669 } | |
| 9670 }); | |
| 9671 | |
| 9672 // 3. | |
| 9673 if (anyNonEmpty) | |
| 9674 dispatchCallbacks(); | |
| 9675 } | |
| 9676 | |
| 9677 function removeTransientObserversFor(observer) { | |
| 9678 observer.nodes_.forEach(function(node) { | |
| 9679 var registrations = registrationsTable.get(node); | |
| 9680 if (!registrations) | |
| 9681 return; | |
| 9682 registrations.forEach(function(registration) { | |
| 9683 if (registration.observer === observer) | |
| 9684 registration.removeTransientObservers(); | |
| 9685 }); | |
| 9686 }); | |
| 9687 } | |
| 9688 | |
| 9689 /** | |
| 9690 * This function is used for the "For each registered observer observer (with | |
| 9691 * observer's options as options) in target's list of registered observers, | |
| 9692 * run these substeps:" and the "For each ancestor ancestor of target, and for | |
| 9693 * each registered observer observer (with options options) in ancestor's list | |
| 9694 * of registered observers, run these substeps:" part of the algorithms. The | |
| 9695 * |options.subtree| is checked to ensure that the callback is called | |
| 9696 * correctly. | |
| 9697 * | |
| 9698 * @param {Node} target | |
| 9699 * @param {function(MutationObserverInit):MutationRecord} callback | |
| 9700 */ | |
| 9701 function forEachAncestorAndObserverEnqueueRecord(target, callback) { | |
| 9702 for (var node = target; node; node = node.parentNode) { | |
| 9703 var registrations = registrationsTable.get(node); | |
| 9704 | |
| 9705 if (registrations) { | |
| 9706 for (var j = 0; j < registrations.length; j++) { | |
| 9707 var registration = registrations[j]; | |
| 9708 var options = registration.options; | |
| 9709 | |
| 9710 // Only target ignores subtree. | |
| 9711 if (node !== target && !options.subtree) | |
| 9712 continue; | |
| 9713 | |
| 9714 var record = callback(options); | |
| 9715 if (record) | |
| 9716 registration.enqueue(record); | |
| 9717 } | |
| 9718 } | |
| 9719 } | |
| 9720 } | |
| 9721 | |
| 9722 var uidCounter = 0; | |
| 9723 | |
| 9724 /** | |
| 9725 * The class that maps to the DOM MutationObserver interface. | |
| 9726 * @param {Function} callback. | |
| 9727 * @constructor | |
| 9728 */ | |
| 9729 function JsMutationObserver(callback) { | |
| 9730 this.callback_ = callback; | |
| 9731 this.nodes_ = []; | |
| 9732 this.records_ = []; | |
| 9733 this.uid_ = ++uidCounter; | |
| 9734 } | |
| 9735 | |
| 9736 JsMutationObserver.prototype = { | |
| 9737 observe: function(target, options) { | |
| 9738 target = wrapIfNeeded(target); | |
| 9739 | |
| 9740 // 1.1 | |
| 9741 if (!options.childList && !options.attributes && !options.characterData || | |
| 9742 | |
| 9743 // 1.2 | |
| 9744 options.attributeOldValue && !options.attributes || | |
| 9745 | |
| 9746 // 1.3 | |
| 9747 options.attributeFilter && options.attributeFilter.length && | |
| 9748 !options.attributes || | |
| 9749 | |
| 9750 // 1.4 | |
| 9751 options.characterDataOldValue && !options.characterData) { | |
| 9752 | |
| 9753 throw new SyntaxError(); | |
| 9754 } | |
| 9755 | |
| 9756 var registrations = registrationsTable.get(target); | |
| 9757 if (!registrations) | |
| 9758 registrationsTable.set(target, registrations = []); | |
| 9759 | |
| 9760 // 2 | |
| 9761 // If target's list of registered observers already includes a registered | |
| 9762 // observer associated with the context object, replace that registered | |
| 9763 // observer's options with options. | |
| 9764 var registration; | |
| 9765 for (var i = 0; i < registrations.length; i++) { | |
| 9766 if (registrations[i].observer === this) { | |
| 9767 registration = registrations[i]; | |
| 9768 registration.removeListeners(); | |
| 9769 registration.options = options; | |
| 9770 break; | |
| 9771 } | |
| 9772 } | |
| 9773 | |
| 9774 // 3. | |
| 9775 // Otherwise, add a new registered observer to target's list of registered | |
| 9776 // observers with the context object as the observer and options as the | |
| 9777 // options, and add target to context object's list of nodes on which it | |
| 9778 // is registered. | |
| 9779 if (!registration) { | |
| 9780 registration = new Registration(this, target, options); | |
| 9781 registrations.push(registration); | |
| 9782 this.nodes_.push(target); | |
| 9783 } | |
| 9784 | |
| 9785 registration.addListeners(); | |
| 9786 }, | |
| 9787 | |
| 9788 disconnect: function() { | |
| 9789 this.nodes_.forEach(function(node) { | |
| 9790 var registrations = registrationsTable.get(node); | |
| 9791 for (var i = 0; i < registrations.length; i++) { | |
| 9792 var registration = registrations[i]; | |
| 9793 if (registration.observer === this) { | |
| 9794 registration.removeListeners(); | |
| 9795 registrations.splice(i, 1); | |
| 9796 // Each node can only have one registered observer associated with | |
| 9797 // this observer. | |
| 9798 break; | |
| 9799 } | |
| 9800 } | |
| 9801 }, this); | |
| 9802 this.records_ = []; | |
| 9803 }, | |
| 9804 | |
| 9805 takeRecords: function() { | |
| 9806 var copyOfRecords = this.records_; | |
| 9807 this.records_ = []; | |
| 9808 return copyOfRecords; | |
| 9809 } | |
| 9810 }; | |
| 9811 | |
| 9812 /** | |
| 9813 * @param {string} type | |
| 9814 * @param {Node} target | |
| 9815 * @constructor | |
| 9816 */ | |
| 9817 function MutationRecord(type, target) { | |
| 9818 this.type = type; | |
| 9819 this.target = target; | |
| 9820 this.addedNodes = []; | |
| 9821 this.removedNodes = []; | |
| 9822 this.previousSibling = null; | |
| 9823 this.nextSibling = null; | |
| 9824 this.attributeName = null; | |
| 9825 this.attributeNamespace = null; | |
| 9826 this.oldValue = null; | |
| 9827 } | |
| 9828 | |
| 9829 function copyMutationRecord(original) { | |
| 9830 var record = new MutationRecord(original.type, original.target); | |
| 9831 record.addedNodes = original.addedNodes.slice(); | |
| 9832 record.removedNodes = original.removedNodes.slice(); | |
| 9833 record.previousSibling = original.previousSibling; | |
| 9834 record.nextSibling = original.nextSibling; | |
| 9835 record.attributeName = original.attributeName; | |
| 9836 record.attributeNamespace = original.attributeNamespace; | |
| 9837 record.oldValue = original.oldValue; | |
| 9838 return record; | |
| 9839 }; | |
| 9840 | |
| 9841 // We keep track of the two (possibly one) records used in a single mutation. | |
| 9842 var currentRecord, recordWithOldValue; | |
| 9843 | |
| 9844 /** | |
| 9845 * Creates a record without |oldValue| and caches it as |currentRecord| for | |
| 9846 * later use. | |
| 9847 * @param {string} oldValue | |
| 9848 * @return {MutationRecord} | |
| 9849 */ | |
| 9850 function getRecord(type, target) { | |
| 9851 return currentRecord = new MutationRecord(type, target); | |
| 9852 } | |
| 9853 | |
| 9854 /** | |
| 9855 * Gets or creates a record with |oldValue| based in the |currentRecord| | |
| 9856 * @param {string} oldValue | |
| 9857 * @return {MutationRecord} | |
| 9858 */ | |
| 9859 function getRecordWithOldValue(oldValue) { | |
| 9860 if (recordWithOldValue) | |
| 9861 return recordWithOldValue; | |
| 9862 recordWithOldValue = copyMutationRecord(currentRecord); | |
| 9863 recordWithOldValue.oldValue = oldValue; | |
| 9864 return recordWithOldValue; | |
| 9865 } | |
| 9866 | |
| 9867 function clearRecords() { | |
| 9868 currentRecord = recordWithOldValue = undefined; | |
| 9869 } | |
| 9870 | |
| 9871 /** | |
| 9872 * @param {MutationRecord} record | |
| 9873 * @return {boolean} Whether the record represents a record from the current | |
| 9874 * mutation event. | |
| 9875 */ | |
| 9876 function recordRepresentsCurrentMutation(record) { | |
| 9877 return record === recordWithOldValue || record === currentRecord; | |
| 9878 } | |
| 9879 | |
| 9880 /** | |
| 9881 * Selects which record, if any, to replace the last record in the queue. | |
| 9882 * This returns |null| if no record should be replaced. | |
| 9883 * | |
| 9884 * @param {MutationRecord} lastRecord | |
| 9885 * @param {MutationRecord} newRecord | |
| 9886 * @param {MutationRecord} | |
| 9887 */ | |
| 9888 function selectRecord(lastRecord, newRecord) { | |
| 9889 if (lastRecord === newRecord) | |
| 9890 return lastRecord; | |
| 9891 | |
| 9892 // Check if the the record we are adding represents the same record. If | |
| 9893 // so, we keep the one with the oldValue in it. | |
| 9894 if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) | |
| 9895 return recordWithOldValue; | |
| 9896 | |
| 9897 return null; | |
| 9898 } | |
| 9899 | |
| 9900 /** | |
| 9901 * Class used to represent a registered observer. | |
| 9902 * @param {MutationObserver} observer | |
| 9903 * @param {Node} target | |
| 9904 * @param {MutationObserverInit} options | |
| 9905 * @constructor | |
| 9906 */ | |
| 9907 function Registration(observer, target, options) { | |
| 9908 this.observer = observer; | |
| 9909 this.target = target; | |
| 9910 this.options = options; | |
| 9911 this.transientObservedNodes = []; | |
| 9912 } | |
| 9913 | |
| 9914 Registration.prototype = { | |
| 9915 enqueue: function(record) { | |
| 9916 var records = this.observer.records_; | |
| 9917 var length = records.length; | |
| 9918 | |
| 9919 // There are cases where we replace the last record with the new record. | |
| 9920 // For example if the record represents the same mutation we need to use | |
| 9921 // the one with the oldValue. If we get same record (this can happen as we | |
| 9922 // walk up the tree) we ignore the new record. | |
| 9923 if (records.length > 0) { | |
| 9924 var lastRecord = records[length - 1]; | |
| 9925 var recordToReplaceLast = selectRecord(lastRecord, record); | |
| 9926 if (recordToReplaceLast) { | |
| 9927 records[length - 1] = recordToReplaceLast; | |
| 9928 return; | |
| 9929 } | |
| 9930 } else { | |
| 9931 scheduleCallback(this.observer); | |
| 9932 } | |
| 9933 | |
| 9934 records[length] = record; | |
| 9935 }, | |
| 9936 | |
| 9937 addListeners: function() { | |
| 9938 this.addListeners_(this.target); | |
| 9939 }, | |
| 9940 | |
| 9941 addListeners_: function(node) { | |
| 9942 var options = this.options; | |
| 9943 if (options.attributes) | |
| 9944 node.addEventListener('DOMAttrModified', this, true); | |
| 9945 | |
| 9946 if (options.characterData) | |
| 9947 node.addEventListener('DOMCharacterDataModified', this, true); | |
| 9948 | |
| 9949 if (options.childList) | |
| 9950 node.addEventListener('DOMNodeInserted', this, true); | |
| 9951 | |
| 9952 if (options.childList || options.subtree) | |
| 9953 node.addEventListener('DOMNodeRemoved', this, true); | |
| 9954 }, | |
| 9955 | |
| 9956 removeListeners: function() { | |
| 9957 this.removeListeners_(this.target); | |
| 9958 }, | |
| 9959 | |
| 9960 removeListeners_: function(node) { | |
| 9961 var options = this.options; | |
| 9962 if (options.attributes) | |
| 9963 node.removeEventListener('DOMAttrModified', this, true); | |
| 9964 | |
| 9965 if (options.characterData) | |
| 9966 node.removeEventListener('DOMCharacterDataModified', this, true); | |
| 9967 | |
| 9968 if (options.childList) | |
| 9969 node.removeEventListener('DOMNodeInserted', this, true); | |
| 9970 | |
| 9971 if (options.childList || options.subtree) | |
| 9972 node.removeEventListener('DOMNodeRemoved', this, true); | |
| 9973 }, | |
| 9974 | |
| 9975 /** | |
| 9976 * Adds a transient observer on node. The transient observer gets removed | |
| 9977 * next time we deliver the change records. | |
| 9978 * @param {Node} node | |
| 9979 */ | |
| 9980 addTransientObserver: function(node) { | |
| 9981 // Don't add transient observers on the target itself. We already have all | |
| 9982 // the required listeners set up on the target. | |
| 9983 if (node === this.target) | |
| 9984 return; | |
| 9985 | |
| 9986 this.addListeners_(node); | |
| 9987 this.transientObservedNodes.push(node); | |
| 9988 var registrations = registrationsTable.get(node); | |
| 9989 if (!registrations) | |
| 9990 registrationsTable.set(node, registrations = []); | |
| 9991 | |
| 9992 // We know that registrations does not contain this because we already | |
| 9993 // checked if node === this.target. | |
| 9994 registrations.push(this); | |
| 9995 }, | |
| 9996 | |
| 9997 removeTransientObservers: function() { | |
| 9998 var transientObservedNodes = this.transientObservedNodes; | |
| 9999 this.transientObservedNodes = []; | |
| 10000 | |
| 10001 transientObservedNodes.forEach(function(node) { | |
| 10002 // Transient observers are never added to the target. | |
| 10003 this.removeListeners_(node); | |
| 10004 | |
| 10005 var registrations = registrationsTable.get(node); | |
| 10006 for (var i = 0; i < registrations.length; i++) { | |
| 10007 if (registrations[i] === this) { | |
| 10008 registrations.splice(i, 1); | |
| 10009 // Each node can only have one registered observer associated with | |
| 10010 // this observer. | |
| 10011 break; | |
| 10012 } | |
| 10013 } | |
| 10014 }, this); | |
| 10015 }, | |
| 10016 | |
| 10017 handleEvent: function(e) { | |
| 10018 // Stop propagation since we are managing the propagation manually. | |
| 10019 // This means that other mutation events on the page will not work | |
| 10020 // correctly but that is by design. | |
| 10021 e.stopImmediatePropagation(); | |
| 10022 | |
| 10023 switch (e.type) { | |
| 10024 case 'DOMAttrModified': | |
| 10025 // http://dom.spec.whatwg.org/#concept-mo-queue-attributes | |
| 10026 | |
| 10027 var name = e.attrName; | |
| 10028 var namespace = e.relatedNode.namespaceURI; | |
| 10029 var target = e.target; | |
| 10030 | |
| 10031 // 1. | |
| 10032 var record = new getRecord('attributes', target); | |
| 10033 record.attributeName = name; | |
| 10034 record.attributeNamespace = namespace; | |
| 10035 | |
| 10036 // 2. | |
| 10037 var oldValue = | |
| 10038 e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; | |
| 10039 | |
| 10040 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 10041 // 3.1, 4.2 | |
| 10042 if (!options.attributes) | |
| 10043 return; | |
| 10044 | |
| 10045 // 3.2, 4.3 | |
| 10046 if (options.attributeFilter && options.attributeFilter.length && | |
| 10047 options.attributeFilter.indexOf(name) === -1 && | |
| 10048 options.attributeFilter.indexOf(namespace) === -1) { | |
| 10049 return; | |
| 10050 } | |
| 10051 // 3.3, 4.4 | |
| 10052 if (options.attributeOldValue) | |
| 10053 return getRecordWithOldValue(oldValue); | |
| 10054 | |
| 10055 // 3.4, 4.5 | |
| 10056 return record; | |
| 10057 }); | |
| 10058 | |
| 10059 break; | |
| 10060 | |
| 10061 case 'DOMCharacterDataModified': | |
| 10062 // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata | |
| 10063 var target = e.target; | |
| 10064 | |
| 10065 // 1. | |
| 10066 var record = getRecord('characterData', target); | |
| 10067 | |
| 10068 // 2. | |
| 10069 var oldValue = e.prevValue; | |
| 10070 | |
| 10071 | |
| 10072 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 10073 // 3.1, 4.2 | |
| 10074 if (!options.characterData) | |
| 10075 return; | |
| 10076 | |
| 10077 // 3.2, 4.3 | |
| 10078 if (options.characterDataOldValue) | |
| 10079 return getRecordWithOldValue(oldValue); | |
| 10080 | |
| 10081 // 3.3, 4.4 | |
| 10082 return record; | |
| 10083 }); | |
| 10084 | |
| 10085 break; | |
| 10086 | |
| 10087 case 'DOMNodeRemoved': | |
| 10088 this.addTransientObserver(e.target); | |
| 10089 // Fall through. | |
| 10090 case 'DOMNodeInserted': | |
| 10091 // http://dom.spec.whatwg.org/#concept-mo-queue-childlist | |
| 10092 var target = e.relatedNode; | |
| 10093 var changedNode = e.target; | |
| 10094 var addedNodes, removedNodes; | |
| 10095 if (e.type === 'DOMNodeInserted') { | |
| 10096 addedNodes = [changedNode]; | |
| 10097 removedNodes = []; | |
| 10098 } else { | |
| 10099 | |
| 10100 addedNodes = []; | |
| 10101 removedNodes = [changedNode]; | |
| 10102 } | |
| 10103 var previousSibling = changedNode.previousSibling; | |
| 10104 var nextSibling = changedNode.nextSibling; | |
| 10105 | |
| 10106 // 1. | |
| 10107 var record = getRecord('childList', target); | |
| 10108 record.addedNodes = addedNodes; | |
| 10109 record.removedNodes = removedNodes; | |
| 10110 record.previousSibling = previousSibling; | |
| 10111 record.nextSibling = nextSibling; | |
| 10112 | |
| 10113 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 10114 // 2.1, 3.2 | |
| 10115 if (!options.childList) | |
| 10116 return; | |
| 10117 | |
| 10118 // 2.2, 3.3 | |
| 10119 return record; | |
| 10120 }); | |
| 10121 | |
| 10122 } | |
| 10123 | |
| 10124 clearRecords(); | |
| 10125 } | |
| 10126 }; | |
| 10127 | |
| 10128 global.JsMutationObserver = JsMutationObserver; | |
| 10129 | |
| 10130 if (!global.MutationObserver) | |
| 10131 global.MutationObserver = JsMutationObserver; | |
| 10132 | |
| 10133 | |
| 10134 })(this); | |
| 10135 | |
| 10136 /* | |
| 10137 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 10138 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 10139 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 10140 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 10141 * Code distributed by Google as part of the polymer project is also | |
| 10142 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 10143 */ | |
| 10144 window.HTMLImports = window.HTMLImports || {flags:{}}; | |
| 10145 /* | |
| 10146 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 10147 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 10148 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 10149 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 10150 * Code distributed by Google as part of the polymer project is also | |
| 10151 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 10152 */ | |
| 10153 | |
| 10154 (function(scope) { | |
| 10155 | |
| 10156 var IMPORT_LINK_TYPE = 'import'; | |
| 10157 var hasNative = (IMPORT_LINK_TYPE in document.createElement('link')); | |
| 10158 var useNative = hasNative; | |
| 10159 var isIE = /Trident/.test(navigator.userAgent); | |
| 10160 | |
| 10161 // TODO(sorvell): SD polyfill intrusion | |
| 10162 var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill); | |
| 10163 var wrap = function(node) { | |
| 10164 return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node; | |
| 10165 }; | |
| 10166 | |
| 10167 var rootDocument = wrap(document); | |
| 10168 | |
| 10169 // NOTE: We cannot polyfill document.currentScript because it's not possible | |
| 10170 // both to override and maintain the ability to capture the native value; | |
| 10171 // therefore we choose to expose _currentScript both when native imports | |
| 10172 // and the polyfill are in use. | |
| 10173 var currentScriptDescriptor = { | |
| 10174 get: function() { | |
| 10175 var script = HTMLImports.currentScript || document.currentScript || | |
| 10176 // NOTE: only works when called in synchronously executing code. | |
| 10177 // readyState should check if `loading` but IE10 is | |
| 10178 // interactive when scripts run so we cheat. | |
| 10179 (document.readyState !== 'complete' ? | |
| 10180 document.scripts[document.scripts.length - 1] : null); | |
| 10181 return wrap(script); | |
| 10182 }, | |
| 10183 configurable: true | |
| 10184 }; | |
| 10185 | |
| 10186 Object.defineProperty(document, '_currentScript', currentScriptDescriptor); | |
| 10187 Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor); | |
| 10188 | |
| 10189 // call a callback when all HTMLImports in the document at call (or at least | |
| 10190 // document ready) time have loaded. | |
| 10191 // 1. ensure the document is in a ready state (has dom), then | |
| 10192 // 2. watch for loading of imports and call callback when done | |
| 10193 function whenReady(callback, doc) { | |
| 10194 doc = doc || rootDocument; | |
| 10195 // if document is loading, wait and try again | |
| 10196 whenDocumentReady(function() { | |
| 10197 watchImportsLoad(callback, doc); | |
| 10198 }, doc); | |
| 10199 } | |
| 10200 | |
| 10201 // call the callback when the document is in a ready state (has dom) | |
| 10202 var requiredReadyState = isIE ? 'complete' : 'interactive'; | |
| 10203 var READY_EVENT = 'readystatechange'; | |
| 10204 function isDocumentReady(doc) { | |
| 10205 return (doc.readyState === 'complete' || | |
| 10206 doc.readyState === requiredReadyState); | |
| 10207 } | |
| 10208 | |
| 10209 // call <callback> when we ensure the document is in a ready state | |
| 10210 function whenDocumentReady(callback, doc) { | |
| 10211 if (!isDocumentReady(doc)) { | |
| 10212 var checkReady = function() { | |
| 10213 if (doc.readyState === 'complete' || | |
| 10214 doc.readyState === requiredReadyState) { | |
| 10215 doc.removeEventListener(READY_EVENT, checkReady); | |
| 10216 whenDocumentReady(callback, doc); | |
| 10217 } | |
| 10218 }; | |
| 10219 doc.addEventListener(READY_EVENT, checkReady); | |
| 10220 } else if (callback) { | |
| 10221 callback(); | |
| 10222 } | |
| 10223 } | |
| 10224 | |
| 10225 function markTargetLoaded(event) { | |
| 10226 event.target.__loaded = true; | |
| 10227 } | |
| 10228 | |
| 10229 // call <callback> when we ensure all imports have loaded | |
| 10230 function watchImportsLoad(callback, doc) { | |
| 10231 var imports = doc.querySelectorAll('link[rel=import]'); | |
| 10232 var loaded = 0, l = imports.length; | |
| 10233 function checkDone(d) { | |
| 10234 if ((loaded == l) && callback) { | |
| 10235 callback(); | |
| 10236 } | |
| 10237 } | |
| 10238 function loadedImport(e) { | |
| 10239 markTargetLoaded(e); | |
| 10240 loaded++; | |
| 10241 checkDone(); | |
| 10242 } | |
| 10243 if (l) { | |
| 10244 for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { | |
| 10245 if (isImportLoaded(imp)) { | |
| 10246 loadedImport.call(imp, {target: imp}); | |
| 10247 } else { | |
| 10248 imp.addEventListener('load', loadedImport); | |
| 10249 imp.addEventListener('error', loadedImport); | |
| 10250 } | |
| 10251 } | |
| 10252 } else { | |
| 10253 checkDone(); | |
| 10254 } | |
| 10255 } | |
| 10256 | |
| 10257 // NOTE: test for native imports loading is based on explicitly watching | |
| 10258 // all imports (see below). | |
| 10259 // We cannot rely on this entirely without watching the entire document | |
| 10260 // for import links. For perf reasons, currently only head is watched. | |
| 10261 // Instead, we fallback to checking if the import property is available | |
| 10262 // and the document is not itself loading. | |
| 10263 function isImportLoaded(link) { | |
| 10264 return useNative ? link.__loaded || | |
| 10265 (link.import && link.import.readyState !== 'loading') : | |
| 10266 link.__importParsed; | |
| 10267 } | |
| 10268 | |
| 10269 // TODO(sorvell): Workaround for | |
| 10270 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when | |
| 10271 // this bug is addressed. | |
| 10272 // (1) Install a mutation observer to see when HTMLImports have loaded | |
| 10273 // (2) if this script is run during document load it will watch any existing | |
| 10274 // imports for loading. | |
| 10275 // | |
| 10276 // NOTE: The workaround has restricted functionality: (1) it's only compatible | |
| 10277 // with imports that are added to document.head since the mutation observer | |
| 10278 // watches only head for perf reasons, (2) it requires this script | |
| 10279 // to run before any imports have completed loading. | |
| 10280 if (useNative) { | |
| 10281 new MutationObserver(function(mxns) { | |
| 10282 for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) { | |
| 10283 if (m.addedNodes) { | |
| 10284 handleImports(m.addedNodes); | |
| 10285 } | |
| 10286 } | |
| 10287 }).observe(document.head, {childList: true}); | |
| 10288 | |
| 10289 function handleImports(nodes) { | |
| 10290 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { | |
| 10291 if (isImport(n)) { | |
| 10292 handleImport(n); | |
| 10293 } | |
| 10294 } | |
| 10295 } | |
| 10296 | |
| 10297 function isImport(element) { | |
| 10298 return element.localName === 'link' && element.rel === 'import'; | |
| 10299 } | |
| 10300 | |
| 10301 function handleImport(element) { | |
| 10302 var loaded = element.import; | |
| 10303 if (loaded) { | |
| 10304 markTargetLoaded({target: element}); | |
| 10305 } else { | |
| 10306 element.addEventListener('load', markTargetLoaded); | |
| 10307 element.addEventListener('error', markTargetLoaded); | |
| 10308 } | |
| 10309 } | |
| 10310 | |
| 10311 // make sure to catch any imports that are in the process of loading | |
| 10312 // when this script is run. | |
| 10313 (function() { | |
| 10314 if (document.readyState === 'loading') { | |
| 10315 var imports = document.querySelectorAll('link[rel=import]'); | |
| 10316 for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) { | |
| 10317 handleImport(imp); | |
| 10318 } | |
| 10319 } | |
| 10320 })(); | |
| 10321 | |
| 10322 } | |
| 10323 | |
| 10324 // Fire the 'HTMLImportsLoaded' event when imports in document at load time | |
| 10325 // have loaded. This event is required to simulate the script blocking | |
| 10326 // behavior of native imports. A main document script that needs to be sure | |
| 10327 // imports have loaded should wait for this event. | |
| 10328 whenReady(function() { | |
| 10329 HTMLImports.ready = true; | |
| 10330 HTMLImports.readyTime = new Date().getTime(); | |
| 10331 rootDocument.dispatchEvent( | |
| 10332 new CustomEvent('HTMLImportsLoaded', {bubbles: true}) | |
| 10333 ); | |
| 10334 }); | |
| 10335 | |
| 10336 // exports | |
| 10337 scope.useNative = useNative; | |
| 10338 scope.isImportLoaded = isImportLoaded; | |
| 10339 scope.whenReady = whenReady; | |
| 10340 scope.rootDocument = rootDocument; | |
| 10341 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
| 10342 scope.isIE = isIE; | |
| 10343 | |
| 10344 })(window.HTMLImports); | |
| 10345 /* | |
| 10346 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 10347 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 10348 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 10349 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 10350 * Code distributed by Google as part of the polymer project is also | |
| 10351 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 10352 */ | |
| 10353 (function(scope) { | |
| 10354 | |
| 10355 // imports | |
| 10356 var path = scope.path; | |
| 10357 var xhr = scope.xhr; | |
| 10358 var flags = scope.flags; | |
| 10359 | |
| 10360 // TODO(sorvell): this loader supports a dynamic list of urls | |
| 10361 // and an oncomplete callback that is called when the loader is done. | |
| 10362 // The polyfill currently does *not* need this dynamism or the onComplete | |
| 10363 // concept. Because of this, the loader could be simplified quite a bit. | |
| 10364 var Loader = function(onLoad, onComplete) { | |
| 10365 this.cache = {}; | |
| 10366 this.onload = onLoad; | |
| 10367 this.oncomplete = onComplete; | |
| 10368 this.inflight = 0; | |
| 10369 this.pending = {}; | |
| 10370 }; | |
| 10371 | |
| 10372 Loader.prototype = { | |
| 10373 | |
| 10374 addNodes: function(nodes) { | |
| 10375 // number of transactions to complete | |
| 10376 this.inflight += nodes.length; | |
| 10377 // commence transactions | |
| 10378 for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { | |
| 10379 this.require(n); | |
| 10380 } | |
| 10381 // anything to do? | |
| 10382 this.checkDone(); | |
| 10383 }, | |
| 10384 | |
| 10385 addNode: function(node) { | |
| 10386 // number of transactions to complete | |
| 10387 this.inflight++; | |
| 10388 // commence transactions | |
| 10389 this.require(node); | |
| 10390 // anything to do? | |
| 10391 this.checkDone(); | |
| 10392 }, | |
| 10393 | |
| 10394 require: function(elt) { | |
| 10395 var url = elt.src || elt.href; | |
| 10396 // ensure we have a standard url that can be used | |
| 10397 // reliably for deduping. | |
| 10398 // TODO(sjmiles): ad-hoc | |
| 10399 elt.__nodeUrl = url; | |
| 10400 // deduplication | |
| 10401 if (!this.dedupe(url, elt)) { | |
| 10402 // fetch this resource | |
| 10403 this.fetch(url, elt); | |
| 10404 } | |
| 10405 }, | |
| 10406 | |
| 10407 dedupe: function(url, elt) { | |
| 10408 if (this.pending[url]) { | |
| 10409 // add to list of nodes waiting for inUrl | |
| 10410 this.pending[url].push(elt); | |
| 10411 // don't need fetch | |
| 10412 return true; | |
| 10413 } | |
| 10414 var resource; | |
| 10415 if (this.cache[url]) { | |
| 10416 this.onload(url, elt, this.cache[url]); | |
| 10417 // finished this transaction | |
| 10418 this.tail(); | |
| 10419 // don't need fetch | |
| 10420 return true; | |
| 10421 } | |
| 10422 // first node waiting for inUrl | |
| 10423 this.pending[url] = [elt]; | |
| 10424 // need fetch (not a dupe) | |
| 10425 return false; | |
| 10426 }, | |
| 10427 | |
| 10428 fetch: function(url, elt) { | |
| 10429 flags.load && console.log('fetch', url, elt); | |
| 10430 if (url.match(/^data:/)) { | |
| 10431 // Handle Data URI Scheme | |
| 10432 var pieces = url.split(','); | |
| 10433 var header = pieces[0]; | |
| 10434 var body = pieces[1]; | |
| 10435 if(header.indexOf(';base64') > -1) { | |
| 10436 body = atob(body); | |
| 10437 } else { | |
| 10438 body = decodeURIComponent(body); | |
| 10439 } | |
| 10440 setTimeout(function() { | |
| 10441 this.receive(url, elt, null, body); | |
| 10442 }.bind(this), 0); | |
| 10443 } else { | |
| 10444 var receiveXhr = function(err, resource, redirectedUrl) { | |
| 10445 this.receive(url, elt, err, resource, redirectedUrl); | |
| 10446 }.bind(this); | |
| 10447 xhr.load(url, receiveXhr); | |
| 10448 // TODO(sorvell): blocked on) | |
| 10449 // https://code.google.com/p/chromium/issues/detail?id=257221 | |
| 10450 // xhr'ing for a document makes scripts in imports runnable; otherwise | |
| 10451 // they are not; however, it requires that we have doctype=html in | |
| 10452 // the import which is unacceptable. This is only needed on Chrome | |
| 10453 // to avoid the bug above. | |
| 10454 /* | |
| 10455 if (isDocumentLink(elt)) { | |
| 10456 xhr.loadDocument(url, receiveXhr); | |
| 10457 } else { | |
| 10458 xhr.load(url, receiveXhr); | |
| 10459 } | |
| 10460 */ | |
| 10461 } | |
| 10462 }, | |
| 10463 | |
| 10464 receive: function(url, elt, err, resource, redirectedUrl) { | |
| 10465 this.cache[url] = resource; | |
| 10466 var $p = this.pending[url]; | |
| 10467 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { | |
| 10468 // If url was redirected, use the redirected location so paths are | |
| 10469 // calculated relative to that. | |
| 10470 this.onload(url, p, resource, err, redirectedUrl); | |
| 10471 this.tail(); | |
| 10472 } | |
| 10473 this.pending[url] = null; | |
| 10474 }, | |
| 10475 | |
| 10476 tail: function() { | |
| 10477 --this.inflight; | |
| 10478 this.checkDone(); | |
| 10479 }, | |
| 10480 | |
| 10481 checkDone: function() { | |
| 10482 if (!this.inflight) { | |
| 10483 this.oncomplete(); | |
| 10484 } | |
| 10485 } | |
| 10486 | |
| 10487 }; | |
| 10488 | |
| 10489 xhr = xhr || { | |
| 10490 async: true, | |
| 10491 | |
| 10492 ok: function(request) { | |
| 10493 return (request.status >= 200 && request.status < 300) | |
| 10494 || (request.status === 304) | |
| 10495 || (request.status === 0); | |
| 10496 }, | |
| 10497 | |
| 10498 load: function(url, next, nextContext) { | |
| 10499 var request = new XMLHttpRequest(); | |
| 10500 if (scope.flags.debug || scope.flags.bust) { | |
| 10501 url += '?' + Math.random(); | |
| 10502 } | |
| 10503 request.open('GET', url, xhr.async); | |
| 10504 request.addEventListener('readystatechange', function(e) { | |
| 10505 if (request.readyState === 4) { | |
| 10506 // Servers redirecting an import can add a Location header to help us | |
| 10507 // polyfill correctly. | |
| 10508 var locationHeader = request.getResponseHeader("Location"); | |
| 10509 var redirectedUrl = null; | |
| 10510 if (locationHeader) { | |
| 10511 var redirectedUrl = (locationHeader.substr( 0, 1 ) === "/") | |
| 10512 ? location.origin + locationHeader // Location is a relative path | |
| 10513 : locationHeader; // Full path | |
| 10514 } | |
| 10515 next.call(nextContext, !xhr.ok(request) && request, | |
| 10516 request.response || request.responseText, redirectedUrl); | |
| 10517 } | |
| 10518 }); | |
| 10519 request.send(); | |
| 10520 return request; | |
| 10521 }, | |
| 10522 | |
| 10523 loadDocument: function(url, next, nextContext) { | |
| 10524 this.load(url, next, nextContext).responseType = 'document'; | |
| 10525 } | |
| 10526 | |
| 10527 }; | |
| 10528 | |
| 10529 // exports | |
| 10530 scope.xhr = xhr; | |
| 10531 scope.Loader = Loader; | |
| 10532 | |
| 10533 })(window.HTMLImports); | |
| 10534 | |
| 10535 /* | |
| 10536 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 10537 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 10538 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 10539 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 10540 * Code distributed by Google as part of the polymer project is also | |
| 10541 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 10542 */ | |
| 10543 (function(scope) { | |
| 10544 | |
| 10545 // imports | |
| 10546 var rootDocument = scope.rootDocument; | |
| 10547 var flags = scope.flags; | |
| 10548 var isIE = scope.isIE; | |
| 10549 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
| 10550 | |
| 10551 // importParser | |
| 10552 // highlander object to manage parsing of imports | |
| 10553 // parses import related elements | |
| 10554 // and ensures proper parse order | |
| 10555 // parse order is enforced by crawling the tree and monitoring which elements | |
| 10556 // have been parsed; async parsing is also supported. | |
| 10557 | |
| 10558 // highlander object for parsing a document tree | |
| 10559 var importParser = { | |
| 10560 | |
| 10561 // parse selectors for main document elements | |
| 10562 documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
| 10563 | |
| 10564 // parse selectors for import document elements | |
| 10565 importsSelectors: [ | |
| 10566 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
| 10567 'link[rel=stylesheet]', | |
| 10568 'style', | |
| 10569 'script:not([type])', | |
| 10570 'script[type="text/javascript"]' | |
| 10571 ].join(','), | |
| 10572 | |
| 10573 map: { | |
| 10574 link: 'parseLink', | |
| 10575 script: 'parseScript', | |
| 10576 style: 'parseStyle' | |
| 10577 }, | |
| 10578 | |
| 10579 dynamicElements: [], | |
| 10580 | |
| 10581 // try to parse the next import in the tree | |
| 10582 parseNext: function() { | |
| 10583 var next = this.nextToParse(); | |
| 10584 if (next) { | |
| 10585 this.parse(next); | |
| 10586 } | |
| 10587 }, | |
| 10588 | |
| 10589 parse: function(elt) { | |
| 10590 if (this.isParsed(elt)) { | |
| 10591 flags.parse && console.log('[%s] is already parsed', elt.localName); | |
| 10592 return; | |
| 10593 } | |
| 10594 var fn = this[this.map[elt.localName]]; | |
| 10595 if (fn) { | |
| 10596 this.markParsing(elt); | |
| 10597 fn.call(this, elt); | |
| 10598 } | |
| 10599 }, | |
| 10600 | |
| 10601 parseDynamic: function(elt, quiet) { | |
| 10602 this.dynamicElements.push(elt); | |
| 10603 if (!quiet) { | |
| 10604 this.parseNext(); | |
| 10605 } | |
| 10606 }, | |
| 10607 | |
| 10608 // only 1 element may be parsed at a time; parsing is async so each | |
| 10609 // parsing implementation must inform the system that parsing is complete | |
| 10610 // via markParsingComplete. | |
| 10611 // To prompt the system to parse the next element, parseNext should then be | |
| 10612 // called. | |
| 10613 // Note, parseNext used to be included at the end of markParsingComplete, but | |
| 10614 // we must not do this so that, for example, we can (1) mark parsing complete | |
| 10615 // then (2) fire an import load event, and then (3) parse the next resource. | |
| 10616 markParsing: function(elt) { | |
| 10617 flags.parse && console.log('parsing', elt); | |
| 10618 this.parsingElement = elt; | |
| 10619 }, | |
| 10620 | |
| 10621 markParsingComplete: function(elt) { | |
| 10622 elt.__importParsed = true; | |
| 10623 this.markDynamicParsingComplete(elt); | |
| 10624 if (elt.__importElement) { | |
| 10625 elt.__importElement.__importParsed = true; | |
| 10626 this.markDynamicParsingComplete(elt.__importElement); | |
| 10627 } | |
| 10628 this.parsingElement = null; | |
| 10629 flags.parse && console.log('completed', elt); | |
| 10630 }, | |
| 10631 | |
| 10632 markDynamicParsingComplete: function(elt) { | |
| 10633 var i = this.dynamicElements.indexOf(elt); | |
| 10634 if (i >= 0) { | |
| 10635 this.dynamicElements.splice(i, 1); | |
| 10636 } | |
| 10637 }, | |
| 10638 | |
| 10639 parseImport: function(elt) { | |
| 10640 // TODO(sorvell): consider if there's a better way to do this; | |
| 10641 // expose an imports parsing hook; this is needed, for example, by the | |
| 10642 // CustomElements polyfill. | |
| 10643 if (HTMLImports.__importsParsingHook) { | |
| 10644 HTMLImports.__importsParsingHook(elt); | |
| 10645 } | |
| 10646 if (elt.import) { | |
| 10647 elt.import.__importParsed = true; | |
| 10648 } | |
| 10649 this.markParsingComplete(elt); | |
| 10650 // fire load event | |
| 10651 if (elt.__resource && !elt.__error) { | |
| 10652 elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); | |
| 10653 } else { | |
| 10654 elt.dispatchEvent(new CustomEvent('error', {bubbles: false})); | |
| 10655 } | |
| 10656 // TODO(sorvell): workaround for Safari addEventListener not working | |
| 10657 // for elements not in the main document. | |
| 10658 if (elt.__pending) { | |
| 10659 var fn; | |
| 10660 while (elt.__pending.length) { | |
| 10661 fn = elt.__pending.shift(); | |
| 10662 if (fn) { | |
| 10663 fn({target: elt}); | |
| 10664 } | |
| 10665 } | |
| 10666 } | |
| 10667 this.parseNext(); | |
| 10668 }, | |
| 10669 | |
| 10670 parseLink: function(linkElt) { | |
| 10671 if (nodeIsImport(linkElt)) { | |
| 10672 this.parseImport(linkElt); | |
| 10673 } else { | |
| 10674 // make href absolute | |
| 10675 linkElt.href = linkElt.href; | |
| 10676 this.parseGeneric(linkElt); | |
| 10677 } | |
| 10678 }, | |
| 10679 | |
| 10680 parseStyle: function(elt) { | |
| 10681 // TODO(sorvell): style element load event can just not fire so clone styles | |
| 10682 var src = elt; | |
| 10683 elt = cloneStyle(elt); | |
| 10684 elt.__importElement = src; | |
| 10685 this.parseGeneric(elt); | |
| 10686 }, | |
| 10687 | |
| 10688 parseGeneric: function(elt) { | |
| 10689 this.trackElement(elt); | |
| 10690 this.addElementToDocument(elt); | |
| 10691 }, | |
| 10692 | |
| 10693 rootImportForElement: function(elt) { | |
| 10694 var n = elt; | |
| 10695 while (n.ownerDocument.__importLink) { | |
| 10696 n = n.ownerDocument.__importLink; | |
| 10697 } | |
| 10698 return n; | |
| 10699 }, | |
| 10700 | |
| 10701 addElementToDocument: function(elt) { | |
| 10702 var port = this.rootImportForElement(elt.__importElement || elt); | |
| 10703 var l = port.__insertedElements = port.__insertedElements || 0; | |
| 10704 var refNode = port.nextElementSibling; | |
| 10705 for (var i=0; i < l; i++) { | |
| 10706 refNode = refNode && refNode.nextElementSibling; | |
| 10707 } | |
| 10708 port.parentNode.insertBefore(elt, refNode); | |
| 10709 }, | |
| 10710 | |
| 10711 // tracks when a loadable element has loaded | |
| 10712 trackElement: function(elt, callback) { | |
| 10713 var self = this; | |
| 10714 var done = function(e) { | |
| 10715 if (callback) { | |
| 10716 callback(e); | |
| 10717 } | |
| 10718 self.markParsingComplete(elt); | |
| 10719 self.parseNext(); | |
| 10720 }; | |
| 10721 elt.addEventListener('load', done); | |
| 10722 elt.addEventListener('error', done); | |
| 10723 | |
| 10724 // NOTE: IE does not fire "load" event for styles that have already loaded | |
| 10725 // This is in violation of the spec, so we try our hardest to work around it | |
| 10726 if (isIE && elt.localName === 'style') { | |
| 10727 var fakeLoad = false; | |
| 10728 // If there's not @import in the textContent, assume it has loaded | |
| 10729 if (elt.textContent.indexOf('@import') == -1) { | |
| 10730 fakeLoad = true; | |
| 10731 // if we have a sheet, we have been parsed | |
| 10732 } else if (elt.sheet) { | |
| 10733 fakeLoad = true; | |
| 10734 var csr = elt.sheet.cssRules; | |
| 10735 var len = csr ? csr.length : 0; | |
| 10736 // search the rules for @import's | |
| 10737 for (var i = 0, r; (i < len) && (r = csr[i]); i++) { | |
| 10738 if (r.type === CSSRule.IMPORT_RULE) { | |
| 10739 // if every @import has resolved, fake the load | |
| 10740 fakeLoad = fakeLoad && Boolean(r.styleSheet); | |
| 10741 } | |
| 10742 } | |
| 10743 } | |
| 10744 // dispatch a fake load event and continue parsing | |
| 10745 if (fakeLoad) { | |
| 10746 elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); | |
| 10747 } | |
| 10748 } | |
| 10749 }, | |
| 10750 | |
| 10751 // NOTE: execute scripts by injecting them and watching for the load/error | |
| 10752 // event. Inline scripts are handled via dataURL's because browsers tend to | |
| 10753 // provide correct parsing errors in this case. If this has any compatibility | |
| 10754 // issues, we can switch to injecting the inline script with textContent. | |
| 10755 // Scripts with dataURL's do not appear to generate load events and therefore | |
| 10756 // we assume they execute synchronously. | |
| 10757 parseScript: function(scriptElt) { | |
| 10758 var script = document.createElement('script'); | |
| 10759 script.__importElement = scriptElt; | |
| 10760 script.src = scriptElt.src ? scriptElt.src : | |
| 10761 generateScriptDataUrl(scriptElt); | |
| 10762 scope.currentScript = scriptElt; | |
| 10763 this.trackElement(script, function(e) { | |
| 10764 script.parentNode.removeChild(script); | |
| 10765 scope.currentScript = null; | |
| 10766 }); | |
| 10767 this.addElementToDocument(script); | |
| 10768 }, | |
| 10769 | |
| 10770 // determine the next element in the tree which should be parsed | |
| 10771 nextToParse: function() { | |
| 10772 this._mayParse = []; | |
| 10773 return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || | |
| 10774 this.nextToParseDynamic()); | |
| 10775 }, | |
| 10776 | |
| 10777 nextToParseInDoc: function(doc, link) { | |
| 10778 // use `marParse` list to avoid looping into the same document again | |
| 10779 // since it could cause an iloop. | |
| 10780 if (doc && this._mayParse.indexOf(doc) < 0) { | |
| 10781 this._mayParse.push(doc); | |
| 10782 var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc)); | |
| 10783 for (var i=0, l=nodes.length, p=0, n; (i<l) && (n=nodes[i]); i++) { | |
| 10784 if (!this.isParsed(n)) { | |
| 10785 if (this.hasResource(n)) { | |
| 10786 return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n; | |
| 10787 } else { | |
| 10788 return; | |
| 10789 } | |
| 10790 } | |
| 10791 } | |
| 10792 } | |
| 10793 // all nodes have been parsed, ready to parse import, if any | |
| 10794 return link; | |
| 10795 }, | |
| 10796 | |
| 10797 nextToParseDynamic: function() { | |
| 10798 return this.dynamicElements[0]; | |
| 10799 }, | |
| 10800 | |
| 10801 // return the set of parse selectors relevant for this node. | |
| 10802 parseSelectorsForNode: function(node) { | |
| 10803 var doc = node.ownerDocument || node; | |
| 10804 return doc === rootDocument ? this.documentSelectors : | |
| 10805 this.importsSelectors; | |
| 10806 }, | |
| 10807 | |
| 10808 isParsed: function(node) { | |
| 10809 return node.__importParsed; | |
| 10810 }, | |
| 10811 | |
| 10812 needsDynamicParsing: function(elt) { | |
| 10813 return (this.dynamicElements.indexOf(elt) >= 0); | |
| 10814 }, | |
| 10815 | |
| 10816 hasResource: function(node) { | |
| 10817 if (nodeIsImport(node) && (node.import === undefined)) { | |
| 10818 return false; | |
| 10819 } | |
| 10820 return true; | |
| 10821 } | |
| 10822 | |
| 10823 }; | |
| 10824 | |
| 10825 function nodeIsImport(elt) { | |
| 10826 return (elt.localName === 'link') && (elt.rel === IMPORT_LINK_TYPE); | |
| 10827 } | |
| 10828 | |
| 10829 function generateScriptDataUrl(script) { | |
| 10830 var scriptContent = generateScriptContent(script); | |
| 10831 return 'data:text/javascript;charset=utf-8,' + encodeURIComponent(scriptConten
t); | |
| 10832 } | |
| 10833 | |
| 10834 function generateScriptContent(script) { | |
| 10835 return script.textContent + generateSourceMapHint(script); | |
| 10836 } | |
| 10837 | |
| 10838 // calculate source map hint | |
| 10839 function generateSourceMapHint(script) { | |
| 10840 var moniker = script.__nodeUrl; | |
| 10841 if (!moniker) { | |
| 10842 moniker = script.ownerDocument.baseURI; | |
| 10843 // there could be more than one script this url | |
| 10844 var tag = '[' + Math.floor((Math.random()+1)*1000) + ']'; | |
| 10845 // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow | |
| 10846 // this sort of thing | |
| 10847 var matches = script.textContent.match(/Polymer\(['"]([^'"]*)/); | |
| 10848 tag = matches && matches[1] || tag; | |
| 10849 // tag the moniker | |
| 10850 moniker += '/' + tag + '.js'; | |
| 10851 } | |
| 10852 return '\n//# sourceURL=' + moniker + '\n'; | |
| 10853 } | |
| 10854 | |
| 10855 // style/stylesheet handling | |
| 10856 | |
| 10857 // clone style with proper path resolution for main document | |
| 10858 // NOTE: styles are the only elements that require direct path fixup. | |
| 10859 function cloneStyle(style) { | |
| 10860 var clone = style.ownerDocument.createElement('style'); | |
| 10861 clone.textContent = style.textContent; | |
| 10862 path.resolveUrlsInStyle(clone); | |
| 10863 return clone; | |
| 10864 } | |
| 10865 | |
| 10866 // path fixup: style elements in imports must be made relative to the main | |
| 10867 // document. We fixup url's in url() and @import. | |
| 10868 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; | |
| 10869 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; | |
| 10870 | |
| 10871 var path = { | |
| 10872 | |
| 10873 resolveUrlsInStyle: function(style) { | |
| 10874 var doc = style.ownerDocument; | |
| 10875 var resolver = doc.createElement('a'); | |
| 10876 style.textContent = this.resolveUrlsInCssText(style.textContent, resolver); | |
| 10877 return style; | |
| 10878 }, | |
| 10879 | |
| 10880 resolveUrlsInCssText: function(cssText, urlObj) { | |
| 10881 var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP); | |
| 10882 r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP); | |
| 10883 return r; | |
| 10884 }, | |
| 10885 | |
| 10886 replaceUrls: function(text, urlObj, regexp) { | |
| 10887 return text.replace(regexp, function(m, pre, url, post) { | |
| 10888 var urlPath = url.replace(/["']/g, ''); | |
| 10889 urlObj.href = urlPath; | |
| 10890 urlPath = urlObj.href; | |
| 10891 return pre + '\'' + urlPath + '\'' + post; | |
| 10892 }); | |
| 10893 } | |
| 10894 | |
| 10895 }; | |
| 10896 | |
| 10897 // exports | |
| 10898 scope.parser = importParser; | |
| 10899 scope.path = path; | |
| 10900 | |
| 10901 })(HTMLImports); | |
| 10902 | |
| 10903 /* | |
| 10904 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 10905 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 10906 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 10907 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 10908 * Code distributed by Google as part of the polymer project is also | |
| 10909 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 10910 */ | |
| 10911 (function(scope) { | |
| 10912 | |
| 10913 // imports | |
| 10914 var useNative = scope.useNative; | |
| 10915 var flags = scope.flags; | |
| 10916 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
| 10917 | |
| 10918 if (!useNative) { | |
| 10919 | |
| 10920 // imports | |
| 10921 var rootDocument = scope.rootDocument; | |
| 10922 var xhr = scope.xhr; | |
| 10923 var Loader = scope.Loader; | |
| 10924 var parser = scope.parser; | |
| 10925 | |
| 10926 // importer | |
| 10927 // highlander object to manage loading of imports | |
| 10928 | |
| 10929 // for any document, importer: | |
| 10930 // - loads any linked import documents (with deduping) | |
| 10931 | |
| 10932 var importer = { | |
| 10933 | |
| 10934 documents: {}, | |
| 10935 | |
| 10936 // nodes to load in the mian document | |
| 10937 documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', | |
| 10938 | |
| 10939 // nodes to load in imports | |
| 10940 importsPreloadSelectors: [ | |
| 10941 'link[rel=' + IMPORT_LINK_TYPE + ']' | |
| 10942 ].join(','), | |
| 10943 | |
| 10944 loadNode: function(node) { | |
| 10945 importLoader.addNode(node); | |
| 10946 }, | |
| 10947 | |
| 10948 // load all loadable elements within the parent element | |
| 10949 loadSubtree: function(parent) { | |
| 10950 var nodes = this.marshalNodes(parent); | |
| 10951 // add these nodes to loader's queue | |
| 10952 importLoader.addNodes(nodes); | |
| 10953 }, | |
| 10954 | |
| 10955 marshalNodes: function(parent) { | |
| 10956 // all preloadable nodes in inDocument | |
| 10957 return parent.querySelectorAll(this.loadSelectorsForNode(parent)); | |
| 10958 }, | |
| 10959 | |
| 10960 // find the proper set of load selectors for a given node | |
| 10961 loadSelectorsForNode: function(node) { | |
| 10962 var doc = node.ownerDocument || node; | |
| 10963 return doc === rootDocument ? this.documentPreloadSelectors : | |
| 10964 this.importsPreloadSelectors; | |
| 10965 }, | |
| 10966 | |
| 10967 loaded: function(url, elt, resource, err, redirectedUrl) { | |
| 10968 flags.load && console.log('loaded', url, elt); | |
| 10969 // store generic resource | |
| 10970 // TODO(sorvell): fails for nodes inside <template>.content | |
| 10971 // see https://code.google.com/p/chromium/issues/detail?id=249381. | |
| 10972 elt.__resource = resource; | |
| 10973 elt.__error = err; | |
| 10974 if (isDocumentLink(elt)) { | |
| 10975 var doc = this.documents[url]; | |
| 10976 // if we've never seen a document at this url | |
| 10977 if (doc === undefined) { | |
| 10978 // generate an HTMLDocument from data | |
| 10979 doc = err ? null : makeDocument(resource, redirectedUrl || url); | |
| 10980 if (doc) { | |
| 10981 doc.__importLink = elt; | |
| 10982 // note, we cannot use MO to detect parsed nodes because | |
| 10983 // SD polyfill does not report these as mutations. | |
| 10984 this.bootDocument(doc); | |
| 10985 } | |
| 10986 // cache document | |
| 10987 this.documents[url] = doc; | |
| 10988 } | |
| 10989 // don't store import record until we're actually loaded | |
| 10990 // store document resource | |
| 10991 elt.import = doc; | |
| 10992 } | |
| 10993 parser.parseNext(); | |
| 10994 }, | |
| 10995 | |
| 10996 bootDocument: function(doc) { | |
| 10997 this.loadSubtree(doc); | |
| 10998 this.observe(doc); | |
| 10999 parser.parseNext(); | |
| 11000 }, | |
| 11001 | |
| 11002 loadedAll: function() { | |
| 11003 parser.parseNext(); | |
| 11004 } | |
| 11005 | |
| 11006 }; | |
| 11007 | |
| 11008 // loader singleton | |
| 11009 var importLoader = new Loader(importer.loaded.bind(importer), | |
| 11010 importer.loadedAll.bind(importer)); | |
| 11011 | |
| 11012 function isDocumentLink(elt) { | |
| 11013 return isLinkRel(elt, IMPORT_LINK_TYPE); | |
| 11014 } | |
| 11015 | |
| 11016 function isLinkRel(elt, rel) { | |
| 11017 return elt.localName === 'link' && elt.getAttribute('rel') === rel; | |
| 11018 } | |
| 11019 | |
| 11020 function isScript(elt) { | |
| 11021 return elt.localName === 'script'; | |
| 11022 } | |
| 11023 | |
| 11024 function makeDocument(resource, url) { | |
| 11025 // create a new HTML document | |
| 11026 var doc = resource; | |
| 11027 if (!(doc instanceof Document)) { | |
| 11028 doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE); | |
| 11029 } | |
| 11030 // cache the new document's source url | |
| 11031 doc._URL = url; | |
| 11032 // establish a relative path via <base> | |
| 11033 var base = doc.createElement('base'); | |
| 11034 base.setAttribute('href', url); | |
| 11035 // add baseURI support to browsers (IE) that lack it. | |
| 11036 if (!doc.baseURI) { | |
| 11037 doc.baseURI = url; | |
| 11038 } | |
| 11039 // ensure UTF-8 charset | |
| 11040 var meta = doc.createElement('meta'); | |
| 11041 meta.setAttribute('charset', 'utf-8'); | |
| 11042 | |
| 11043 doc.head.appendChild(meta); | |
| 11044 doc.head.appendChild(base); | |
| 11045 // install HTML last as it may trigger CustomElement upgrades | |
| 11046 // TODO(sjmiles): problem wrt to template boostrapping below, | |
| 11047 // template bootstrapping must (?) come before element upgrade | |
| 11048 // but we cannot bootstrap templates until they are in a document | |
| 11049 // which is too late | |
| 11050 if (!(resource instanceof Document)) { | |
| 11051 // install html | |
| 11052 doc.body.innerHTML = resource; | |
| 11053 } | |
| 11054 // TODO(sorvell): ideally this code is not aware of Template polyfill, | |
| 11055 // but for now the polyfill needs help to bootstrap these templates | |
| 11056 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { | |
| 11057 HTMLTemplateElement.bootstrap(doc); | |
| 11058 } | |
| 11059 return doc; | |
| 11060 } | |
| 11061 | |
| 11062 // Polyfill document.baseURI for browsers without it. | |
| 11063 if (!document.baseURI) { | |
| 11064 var baseURIDescriptor = { | |
| 11065 get: function() { | |
| 11066 var base = document.querySelector('base'); | |
| 11067 return base ? base.href : window.location.href; | |
| 11068 }, | |
| 11069 configurable: true | |
| 11070 }; | |
| 11071 | |
| 11072 Object.defineProperty(document, 'baseURI', baseURIDescriptor); | |
| 11073 Object.defineProperty(rootDocument, 'baseURI', baseURIDescriptor); | |
| 11074 } | |
| 11075 | |
| 11076 // IE shim for CustomEvent | |
| 11077 if (typeof window.CustomEvent !== 'function') { | |
| 11078 window.CustomEvent = function(inType, dictionary) { | |
| 11079 var e = document.createEvent('HTMLEvents'); | |
| 11080 e.initEvent(inType, | |
| 11081 dictionary.bubbles === false ? false : true, | |
| 11082 dictionary.cancelable === false ? false : true, | |
| 11083 dictionary.detail); | |
| 11084 return e; | |
| 11085 }; | |
| 11086 } | |
| 11087 | |
| 11088 } else { | |
| 11089 // do nothing if using native imports | |
| 11090 var importer = {}; | |
| 11091 } | |
| 11092 | |
| 11093 // exports | |
| 11094 scope.importer = importer; | |
| 11095 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
| 11096 scope.importLoader = importLoader; | |
| 11097 | |
| 11098 })(window.HTMLImports); | |
| 11099 | |
| 11100 /* | |
| 11101 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 11102 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 11103 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 11104 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 11105 * Code distributed by Google as part of the polymer project is also | |
| 11106 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 11107 */ | |
| 11108 (function(scope){ | |
| 11109 | |
| 11110 // imports | |
| 11111 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
| 11112 var importer = scope.importer; | |
| 11113 var parser = scope.parser; | |
| 11114 | |
| 11115 var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; | |
| 11116 | |
| 11117 | |
| 11118 // we track mutations for addedNodes, looking for imports | |
| 11119 function handler(mutations) { | |
| 11120 for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) { | |
| 11121 if (m.type === 'childList' && m.addedNodes.length) { | |
| 11122 addedNodes(m.addedNodes); | |
| 11123 } | |
| 11124 } | |
| 11125 } | |
| 11126 | |
| 11127 // find loadable elements and add them to the importer | |
| 11128 // IFF the owning document has already parsed, then parsable elements | |
| 11129 // need to be marked for dynamic parsing. | |
| 11130 function addedNodes(nodes) { | |
| 11131 var owner, parsed; | |
| 11132 for (var i=0, l=nodes.length, n, loading; (i<l) && (n=nodes[i]); i++) { | |
| 11133 if (!owner) { | |
| 11134 owner = n.ownerDocument; | |
| 11135 parsed = parser.isParsed(owner); | |
| 11136 } | |
| 11137 // note: the act of loading kicks the parser, so we use parseDynamic's | |
| 11138 // 2nd argument to control if this added node needs to kick the parser. | |
| 11139 loading = shouldLoadNode(n); | |
| 11140 if (loading) { | |
| 11141 importer.loadNode(n); | |
| 11142 } | |
| 11143 if (shouldParseNode(n) && parsed) { | |
| 11144 parser.parseDynamic(n, loading); | |
| 11145 } | |
| 11146 if (n.children && n.children.length) { | |
| 11147 addedNodes(n.children); | |
| 11148 } | |
| 11149 } | |
| 11150 } | |
| 11151 | |
| 11152 function shouldLoadNode(node) { | |
| 11153 return (node.nodeType === 1) && matches.call(node, | |
| 11154 importer.loadSelectorsForNode(node)); | |
| 11155 } | |
| 11156 | |
| 11157 function shouldParseNode(node) { | |
| 11158 return (node.nodeType === 1) && matches.call(node, | |
| 11159 parser.parseSelectorsForNode(node)); | |
| 11160 } | |
| 11161 | |
| 11162 // x-plat matches | |
| 11163 var matches = HTMLElement.prototype.matches || | |
| 11164 HTMLElement.prototype.matchesSelector || | |
| 11165 HTMLElement.prototype.webkitMatchesSelector || | |
| 11166 HTMLElement.prototype.mozMatchesSelector || | |
| 11167 HTMLElement.prototype.msMatchesSelector; | |
| 11168 | |
| 11169 var observer = new MutationObserver(handler); | |
| 11170 | |
| 11171 // observe the given root for loadable elements | |
| 11172 function observe(root) { | |
| 11173 observer.observe(root, {childList: true, subtree: true}); | |
| 11174 } | |
| 11175 | |
| 11176 // exports | |
| 11177 // TODO(sorvell): factor so can put on scope | |
| 11178 scope.observe = observe; | |
| 11179 importer.observe = observe; | |
| 11180 | |
| 11181 })(HTMLImports); | |
| 11182 | |
| 11183 /* | |
| 11184 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 11185 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 11186 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 11187 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 11188 * Code distributed by Google as part of the polymer project is also | |
| 11189 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 11190 */ | |
| 11191 (function(){ | |
| 11192 | |
| 11193 // bootstrap | |
| 11194 | |
| 11195 // TODO(sorvell): SD polyfill intrusion | |
| 11196 var doc = window.ShadowDOMPolyfill ? | |
| 11197 window.ShadowDOMPolyfill.wrapIfNeeded(document) : document; | |
| 11198 | |
| 11199 // no need to bootstrap the polyfill when native imports is available. | |
| 11200 if (!HTMLImports.useNative) { | |
| 11201 function bootstrap() { | |
| 11202 HTMLImports.importer.bootDocument(doc); | |
| 11203 } | |
| 11204 | |
| 11205 // TODO(sorvell): SD polyfill does *not* generate mutations for nodes added | |
| 11206 // by the parser. For this reason, we must wait until the dom exists to | |
| 11207 // bootstrap. | |
| 11208 if (document.readyState === 'complete' || | |
| 11209 (document.readyState === 'interactive' && !window.attachEvent)) { | |
| 11210 bootstrap(); | |
| 11211 } else { | |
| 11212 document.addEventListener('DOMContentLoaded', bootstrap); | |
| 11213 } | |
| 11214 } | |
| 11215 | |
| 11216 })(); | |
| 11217 | |
| 11218 /* | |
| 11219 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 11220 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 11221 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 11222 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 11223 * Code distributed by Google as part of the polymer project is also | |
| 11224 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 11225 */ | |
| 11226 window.CustomElements = window.CustomElements || {flags:{}}; | |
| 11227 /* | |
| 11228 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 11229 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 11230 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 11231 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 11232 * Code distributed by Google as part of the polymer project is also | |
| 11233 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 11234 */ | |
| 11235 | |
| 11236 (function(scope){ | |
| 11237 | |
| 11238 var logFlags = window.logFlags || {}; | |
| 11239 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none
'; | |
| 11240 | |
| 11241 // walk the subtree rooted at node, applying 'find(element, data)' function | |
| 11242 // to each element | |
| 11243 // if 'find' returns true for 'element', do not search element's subtree | |
| 11244 function findAll(node, find, data) { | |
| 11245 var e = node.firstElementChild; | |
| 11246 if (!e) { | |
| 11247 e = node.firstChild; | |
| 11248 while (e && e.nodeType !== Node.ELEMENT_NODE) { | |
| 11249 e = e.nextSibling; | |
| 11250 } | |
| 11251 } | |
| 11252 while (e) { | |
| 11253 if (find(e, data) !== true) { | |
| 11254 findAll(e, find, data); | |
| 11255 } | |
| 11256 e = e.nextElementSibling; | |
| 11257 } | |
| 11258 return null; | |
| 11259 } | |
| 11260 | |
| 11261 // walk all shadowRoots on a given node. | |
| 11262 function forRoots(node, cb) { | |
| 11263 var root = node.shadowRoot; | |
| 11264 while(root) { | |
| 11265 forSubtree(root, cb); | |
| 11266 root = root.olderShadowRoot; | |
| 11267 } | |
| 11268 } | |
| 11269 | |
| 11270 // walk the subtree rooted at node, including descent into shadow-roots, | |
| 11271 // applying 'cb' to each element | |
| 11272 function forSubtree(node, cb) { | |
| 11273 //logFlags.dom && node.childNodes && node.childNodes.length && console.group('
subTree: ', node); | |
| 11274 findAll(node, function(e) { | |
| 11275 if (cb(e)) { | |
| 11276 return true; | |
| 11277 } | |
| 11278 forRoots(e, cb); | |
| 11279 }); | |
| 11280 forRoots(node, cb); | |
| 11281 //logFlags.dom && node.childNodes && node.childNodes.length && console.groupEn
d(); | |
| 11282 } | |
| 11283 | |
| 11284 // manage lifecycle on added node | |
| 11285 function added(node) { | |
| 11286 if (upgrade(node)) { | |
| 11287 insertedNode(node); | |
| 11288 return true; | |
| 11289 } | |
| 11290 inserted(node); | |
| 11291 } | |
| 11292 | |
| 11293 // manage lifecycle on added node's subtree only | |
| 11294 function addedSubtree(node) { | |
| 11295 forSubtree(node, function(e) { | |
| 11296 if (added(e)) { | |
| 11297 return true; | |
| 11298 } | |
| 11299 }); | |
| 11300 } | |
| 11301 | |
| 11302 // manage lifecycle on added node and it's subtree | |
| 11303 function addedNode(node) { | |
| 11304 return added(node) || addedSubtree(node); | |
| 11305 } | |
| 11306 | |
| 11307 // upgrade custom elements at node, if applicable | |
| 11308 function upgrade(node) { | |
| 11309 if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) { | |
| 11310 var type = node.getAttribute('is') || node.localName; | |
| 11311 var definition = scope.registry[type]; | |
| 11312 if (definition) { | |
| 11313 logFlags.dom && console.group('upgrade:', node.localName); | |
| 11314 scope.upgrade(node); | |
| 11315 logFlags.dom && console.groupEnd(); | |
| 11316 return true; | |
| 11317 } | |
| 11318 } | |
| 11319 } | |
| 11320 | |
| 11321 function insertedNode(node) { | |
| 11322 inserted(node); | |
| 11323 if (inDocument(node)) { | |
| 11324 forSubtree(node, function(e) { | |
| 11325 inserted(e); | |
| 11326 }); | |
| 11327 } | |
| 11328 } | |
| 11329 | |
| 11330 // TODO(sorvell): on platforms without MutationObserver, mutations may not be | |
| 11331 // reliable and therefore attached/detached are not reliable. | |
| 11332 // To make these callbacks less likely to fail, we defer all inserts and removes | |
| 11333 // to give a chance for elements to be inserted into dom. | |
| 11334 // This ensures attachedCallback fires for elements that are created and | |
| 11335 // immediately added to dom. | |
| 11336 var hasPolyfillMutations = (!window.MutationObserver || | |
| 11337 (window.MutationObserver === window.JsMutationObserver)); | |
| 11338 scope.hasPolyfillMutations = hasPolyfillMutations; | |
| 11339 | |
| 11340 var isPendingMutations = false; | |
| 11341 var pendingMutations = []; | |
| 11342 function deferMutation(fn) { | |
| 11343 pendingMutations.push(fn); | |
| 11344 if (!isPendingMutations) { | |
| 11345 isPendingMutations = true; | |
| 11346 var async = (window.Platform && window.Platform.endOfMicrotask) || | |
| 11347 setTimeout; | |
| 11348 async(takeMutations); | |
| 11349 } | |
| 11350 } | |
| 11351 | |
| 11352 function takeMutations() { | |
| 11353 isPendingMutations = false; | |
| 11354 var $p = pendingMutations; | |
| 11355 for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { | |
| 11356 p(); | |
| 11357 } | |
| 11358 pendingMutations = []; | |
| 11359 } | |
| 11360 | |
| 11361 function inserted(element) { | |
| 11362 if (hasPolyfillMutations) { | |
| 11363 deferMutation(function() { | |
| 11364 _inserted(element); | |
| 11365 }); | |
| 11366 } else { | |
| 11367 _inserted(element); | |
| 11368 } | |
| 11369 } | |
| 11370 | |
| 11371 // TODO(sjmiles): if there are descents into trees that can never have inDocumen
t(*) true, fix this | |
| 11372 function _inserted(element) { | |
| 11373 // TODO(sjmiles): it's possible we were inserted and removed in the space | |
| 11374 // of one microtask, in which case we won't be 'inDocument' here | |
| 11375 // But there are other cases where we are testing for inserted without | |
| 11376 // specific knowledge of mutations, and must test 'inDocument' to determine | |
| 11377 // whether to call inserted | |
| 11378 // If we can factor these cases into separate code paths we can have | |
| 11379 // better diagnostics. | |
| 11380 // TODO(sjmiles): when logging, do work on all custom elements so we can | |
| 11381 // track behavior even when callbacks not defined | |
| 11382 //console.log('inserted: ', element.localName); | |
| 11383 if (element.attachedCallback || element.detachedCallback || (element.__upgrade
d__ && logFlags.dom)) { | |
| 11384 logFlags.dom && console.group('inserted:', element.localName); | |
| 11385 if (inDocument(element)) { | |
| 11386 element.__inserted = (element.__inserted || 0) + 1; | |
| 11387 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state | |
| 11388 if (element.__inserted < 1) { | |
| 11389 element.__inserted = 1; | |
| 11390 } | |
| 11391 // if we are 'over inserted', squelch the callback | |
| 11392 if (element.__inserted > 1) { | |
| 11393 logFlags.dom && console.warn('inserted:', element.localName, | |
| 11394 'insert/remove count:', element.__inserted) | |
| 11395 } else if (element.attachedCallback) { | |
| 11396 logFlags.dom && console.log('inserted:', element.localName); | |
| 11397 element.attachedCallback(); | |
| 11398 } | |
| 11399 } | |
| 11400 logFlags.dom && console.groupEnd(); | |
| 11401 } | |
| 11402 } | |
| 11403 | |
| 11404 function removedNode(node) { | |
| 11405 removed(node); | |
| 11406 forSubtree(node, function(e) { | |
| 11407 removed(e); | |
| 11408 }); | |
| 11409 } | |
| 11410 | |
| 11411 function removed(element) { | |
| 11412 if (hasPolyfillMutations) { | |
| 11413 deferMutation(function() { | |
| 11414 _removed(element); | |
| 11415 }); | |
| 11416 } else { | |
| 11417 _removed(element); | |
| 11418 } | |
| 11419 } | |
| 11420 | |
| 11421 function _removed(element) { | |
| 11422 // TODO(sjmiles): temporary: do work on all custom elements so we can track | |
| 11423 // behavior even when callbacks not defined | |
| 11424 if (element.attachedCallback || element.detachedCallback || (element.__upgrade
d__ && logFlags.dom)) { | |
| 11425 logFlags.dom && console.group('removed:', element.localName); | |
| 11426 if (!inDocument(element)) { | |
| 11427 element.__inserted = (element.__inserted || 0) - 1; | |
| 11428 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state | |
| 11429 if (element.__inserted > 0) { | |
| 11430 element.__inserted = 0; | |
| 11431 } | |
| 11432 // if we are 'over removed', squelch the callback | |
| 11433 if (element.__inserted < 0) { | |
| 11434 logFlags.dom && console.warn('removed:', element.localName, | |
| 11435 'insert/remove count:', element.__inserted) | |
| 11436 } else if (element.detachedCallback) { | |
| 11437 element.detachedCallback(); | |
| 11438 } | |
| 11439 } | |
| 11440 logFlags.dom && console.groupEnd(); | |
| 11441 } | |
| 11442 } | |
| 11443 | |
| 11444 // SD polyfill intrustion due mainly to the fact that 'document' | |
| 11445 // is not entirely wrapped | |
| 11446 function wrapIfNeeded(node) { | |
| 11447 return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) | |
| 11448 : node; | |
| 11449 } | |
| 11450 | |
| 11451 function inDocument(element) { | |
| 11452 var p = element; | |
| 11453 var doc = wrapIfNeeded(document); | |
| 11454 while (p) { | |
| 11455 if (p == doc) { | |
| 11456 return true; | |
| 11457 } | |
| 11458 p = p.parentNode || p.host; | |
| 11459 } | |
| 11460 } | |
| 11461 | |
| 11462 function watchShadow(node) { | |
| 11463 if (node.shadowRoot && !node.shadowRoot.__watched) { | |
| 11464 logFlags.dom && console.log('watching shadow-root for: ', node.localName); | |
| 11465 // watch all unwatched roots... | |
| 11466 var root = node.shadowRoot; | |
| 11467 while (root) { | |
| 11468 watchRoot(root); | |
| 11469 root = root.olderShadowRoot; | |
| 11470 } | |
| 11471 } | |
| 11472 } | |
| 11473 | |
| 11474 function watchRoot(root) { | |
| 11475 observe(root); | |
| 11476 } | |
| 11477 | |
| 11478 function handler(mutations) { | |
| 11479 // | |
| 11480 if (logFlags.dom) { | |
| 11481 var mx = mutations[0]; | |
| 11482 if (mx && mx.type === 'childList' && mx.addedNodes) { | |
| 11483 if (mx.addedNodes) { | |
| 11484 var d = mx.addedNodes[0]; | |
| 11485 while (d && d !== document && !d.host) { | |
| 11486 d = d.parentNode; | |
| 11487 } | |
| 11488 var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || ''; | |
| 11489 u = u.split('/?').shift().split('/').pop(); | |
| 11490 } | |
| 11491 } | |
| 11492 console.group('mutations (%d) [%s]', mutations.length, u || ''); | |
| 11493 } | |
| 11494 // | |
| 11495 mutations.forEach(function(mx) { | |
| 11496 //logFlags.dom && console.group('mutation'); | |
| 11497 if (mx.type === 'childList') { | |
| 11498 forEach(mx.addedNodes, function(n) { | |
| 11499 //logFlags.dom && console.log(n.localName); | |
| 11500 if (!n.localName) { | |
| 11501 return; | |
| 11502 } | |
| 11503 // nodes added may need lifecycle management | |
| 11504 addedNode(n); | |
| 11505 }); | |
| 11506 // removed nodes may need lifecycle management | |
| 11507 forEach(mx.removedNodes, function(n) { | |
| 11508 //logFlags.dom && console.log(n.localName); | |
| 11509 if (!n.localName) { | |
| 11510 return; | |
| 11511 } | |
| 11512 removedNode(n); | |
| 11513 }); | |
| 11514 } | |
| 11515 //logFlags.dom && console.groupEnd(); | |
| 11516 }); | |
| 11517 logFlags.dom && console.groupEnd(); | |
| 11518 }; | |
| 11519 | |
| 11520 function takeRecords(node) { | |
| 11521 // If the optional node is not supplied, assume we mean the whole document. | |
| 11522 if (!node) node = wrapIfNeeded(document); | |
| 11523 | |
| 11524 // Find the root of the tree, which will be an Document or ShadowRoot. | |
| 11525 while (node.parentNode) { | |
| 11526 node = node.parentNode; | |
| 11527 } | |
| 11528 | |
| 11529 var observer = node.__observer; | |
| 11530 if (observer) { | |
| 11531 handler(observer.takeRecords()); | |
| 11532 takeMutations(); | |
| 11533 } | |
| 11534 } | |
| 11535 | |
| 11536 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | |
| 11537 | |
| 11538 function observe(inRoot) { | |
| 11539 if (inRoot.__observer) return; | |
| 11540 | |
| 11541 // For each ShadowRoot, we create a new MutationObserver, so the root can be | |
| 11542 // garbage collected once all references to the `inRoot` node are gone. | |
| 11543 var observer = new MutationObserver(handler); | |
| 11544 observer.observe(inRoot, {childList: true, subtree: true}); | |
| 11545 inRoot.__observer = observer; | |
| 11546 } | |
| 11547 | |
| 11548 function observeDocument(doc) { | |
| 11549 observe(doc); | |
| 11550 } | |
| 11551 | |
| 11552 function upgradeDocument(doc) { | |
| 11553 logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po
p()); | |
| 11554 addedNode(doc); | |
| 11555 logFlags.dom && console.groupEnd(); | |
| 11556 } | |
| 11557 | |
| 11558 /* | |
| 11559 This method is intended to be called when the document tree (including imports) | |
| 11560 has pending custom elements to upgrade. It can be called multiple times and | |
| 11561 should do nothing if no elements are in need of upgrade. | |
| 11562 | |
| 11563 Note that the import tree can consume itself and therefore special care | |
| 11564 must be taken to avoid recursion. | |
| 11565 */ | |
| 11566 var upgradedDocuments; | |
| 11567 function upgradeDocumentTree(doc) { | |
| 11568 upgradedDocuments = []; | |
| 11569 _upgradeDocumentTree(doc); | |
| 11570 upgradedDocuments = null; | |
| 11571 } | |
| 11572 | |
| 11573 | |
| 11574 function _upgradeDocumentTree(doc) { | |
| 11575 doc = wrapIfNeeded(doc); | |
| 11576 if (upgradedDocuments.indexOf(doc) >= 0) { | |
| 11577 return; | |
| 11578 } | |
| 11579 upgradedDocuments.push(doc); | |
| 11580 //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop()); | |
| 11581 // upgrade contained imported documents | |
| 11582 var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']'); | |
| 11583 for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) { | |
| 11584 if (n.import && n.import.__parsed) { | |
| 11585 _upgradeDocumentTree(n.import); | |
| 11586 } | |
| 11587 } | |
| 11588 upgradeDocument(doc); | |
| 11589 } | |
| 11590 | |
| 11591 // exports | |
| 11592 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
| 11593 scope.watchShadow = watchShadow; | |
| 11594 scope.upgradeDocumentTree = upgradeDocumentTree; | |
| 11595 scope.upgradeAll = addedNode; | |
| 11596 scope.upgradeSubtree = addedSubtree; | |
| 11597 scope.insertedNode = insertedNode; | |
| 11598 | |
| 11599 scope.observeDocument = observeDocument; | |
| 11600 scope.upgradeDocument = upgradeDocument; | |
| 11601 | |
| 11602 scope.takeRecords = takeRecords; | |
| 11603 | |
| 11604 })(window.CustomElements); | |
| 11605 | |
| 11606 /* | |
| 11607 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 11608 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 11609 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 11610 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 11611 * Code distributed by Google as part of the polymer project is also | |
| 11612 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 11613 */ | |
| 11614 | |
| 11615 /** | |
| 11616 * Implements `document.registerElement` | |
| 11617 * @module CustomElements | |
| 11618 */ | |
| 11619 | |
| 11620 /** | |
| 11621 * Polyfilled extensions to the `document` object. | |
| 11622 * @class Document | |
| 11623 */ | |
| 11624 | |
| 11625 (function(scope) { | |
| 11626 | |
| 11627 // imports | |
| 11628 | |
| 11629 if (!scope) { | |
| 11630 scope = window.CustomElements = {flags:{}}; | |
| 11631 } | |
| 11632 var flags = scope.flags; | |
| 11633 | |
| 11634 // native document.registerElement? | |
| 11635 | |
| 11636 var hasNative = Boolean(document.registerElement); | |
| 11637 // For consistent timing, use native custom elements only when not polyfilling | |
| 11638 // other key related web components features. | |
| 11639 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill && (!w
indow.HTMLImports || HTMLImports.useNative); | |
| 11640 | |
| 11641 if (useNative) { | |
| 11642 | |
| 11643 // stub | |
| 11644 var nop = function() {}; | |
| 11645 | |
| 11646 // exports | |
| 11647 scope.registry = {}; | |
| 11648 scope.upgradeElement = nop; | |
| 11649 | |
| 11650 scope.watchShadow = nop; | |
| 11651 scope.upgrade = nop; | |
| 11652 scope.upgradeAll = nop; | |
| 11653 scope.upgradeSubtree = nop; | |
| 11654 scope.observeDocument = nop; | |
| 11655 scope.upgradeDocument = nop; | |
| 11656 scope.upgradeDocumentTree = nop; | |
| 11657 scope.takeRecords = nop; | |
| 11658 scope.reservedTagList = []; | |
| 11659 | |
| 11660 } else { | |
| 11661 | |
| 11662 /** | |
| 11663 * Registers a custom tag name with the document. | |
| 11664 * | |
| 11665 * When a registered element is created, a `readyCallback` method is called | |
| 11666 * in the scope of the element. The `readyCallback` method can be specified on | |
| 11667 * either `options.prototype` or `options.lifecycle` with the latter taking | |
| 11668 * precedence. | |
| 11669 * | |
| 11670 * @method register | |
| 11671 * @param {String} name The tag name to register. Must include a dash ('-'), | |
| 11672 * for example 'x-component'. | |
| 11673 * @param {Object} options | |
| 11674 * @param {String} [options.extends] | |
| 11675 * (_off spec_) Tag name of an element to extend (or blank for a new | |
| 11676 * element). This parameter is not part of the specification, but instead | |
| 11677 * is a hint for the polyfill because the extendee is difficult to infer. | |
| 11678 * Remember that the input prototype must chain to the extended element's | |
| 11679 * prototype (or HTMLElement.prototype) regardless of the value of | |
| 11680 * `extends`. | |
| 11681 * @param {Object} options.prototype The prototype to use for the new | |
| 11682 * element. The prototype must inherit from HTMLElement. | |
| 11683 * @param {Object} [options.lifecycle] | |
| 11684 * Callbacks that fire at important phases in the life of the custom | |
| 11685 * element. | |
| 11686 * | |
| 11687 * @example | |
| 11688 * FancyButton = document.registerElement("fancy-button", { | |
| 11689 * extends: 'button', | |
| 11690 * prototype: Object.create(HTMLButtonElement.prototype, { | |
| 11691 * readyCallback: { | |
| 11692 * value: function() { | |
| 11693 * console.log("a fancy-button was created", | |
| 11694 * } | |
| 11695 * } | |
| 11696 * }) | |
| 11697 * }); | |
| 11698 * @return {Function} Constructor for the newly registered type. | |
| 11699 */ | |
| 11700 function register(name, options) { | |
| 11701 //console.warn('document.registerElement("' + name + '", ', options, ')'); | |
| 11702 // construct a defintion out of options | |
| 11703 // TODO(sjmiles): probably should clone options instead of mutating it | |
| 11704 var definition = options || {}; | |
| 11705 if (!name) { | |
| 11706 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
| 11707 // offer guidance) | |
| 11708 throw new Error('document.registerElement: first argument `name` must not
be empty'); | |
| 11709 } | |
| 11710 if (name.indexOf('-') < 0) { | |
| 11711 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
| 11712 // offer guidance) | |
| 11713 throw new Error('document.registerElement: first argument (\'name\') must
contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); | |
| 11714 } | |
| 11715 // prevent registering reserved names | |
| 11716 if (isReservedTag(name)) { | |
| 11717 throw new Error('Failed to execute \'registerElement\' on \'Document\': Re
gistration failed for type \'' + String(name) + '\'. The type name is invalid.')
; | |
| 11718 } | |
| 11719 // elements may only be registered once | |
| 11720 if (getRegisteredDefinition(name)) { | |
| 11721 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n
ame) + '\' is already registered'); | |
| 11722 } | |
| 11723 // must have a prototype, default to an extension of HTMLElement | |
| 11724 // TODO(sjmiles): probably should throw if no prototype, check spec | |
| 11725 if (!definition.prototype) { | |
| 11726 // TODO(sjmiles): replace with more appropriate error (EricB can probably | |
| 11727 // offer guidance) | |
| 11728 throw new Error('Options missing required prototype property'); | |
| 11729 } | |
| 11730 // record name | |
| 11731 definition.__name = name.toLowerCase(); | |
| 11732 // ensure a lifecycle object so we don't have to null test it | |
| 11733 definition.lifecycle = definition.lifecycle || {}; | |
| 11734 // build a list of ancestral custom elements (for native base detection) | |
| 11735 // TODO(sjmiles): we used to need to store this, but current code only | |
| 11736 // uses it in 'resolveTagName': it should probably be inlined | |
| 11737 definition.ancestry = ancestry(definition.extends); | |
| 11738 // extensions of native specializations of HTMLElement require localName | |
| 11739 // to remain native, and use secondary 'is' specifier for extension type | |
| 11740 resolveTagName(definition); | |
| 11741 // some platforms require modifications to the user-supplied prototype | |
| 11742 // chain | |
| 11743 resolvePrototypeChain(definition); | |
| 11744 // overrides to implement attributeChanged callback | |
| 11745 overrideAttributeApi(definition.prototype); | |
| 11746 // 7.1.5: Register the DEFINITION with DOCUMENT | |
| 11747 registerDefinition(definition.__name, definition); | |
| 11748 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE | |
| 11749 // 7.1.8. Return the output of the previous step. | |
| 11750 definition.ctor = generateConstructor(definition); | |
| 11751 definition.ctor.prototype = definition.prototype; | |
| 11752 // force our .constructor to be our actual constructor | |
| 11753 definition.prototype.constructor = definition.ctor; | |
| 11754 // if initial parsing is complete | |
| 11755 if (scope.ready) { | |
| 11756 // upgrade any pre-existing nodes of this type | |
| 11757 scope.upgradeDocumentTree(document); | |
| 11758 } | |
| 11759 return definition.ctor; | |
| 11760 } | |
| 11761 | |
| 11762 function isReservedTag(name) { | |
| 11763 for (var i = 0; i < reservedTagList.length; i++) { | |
| 11764 if (name === reservedTagList[i]) { | |
| 11765 return true; | |
| 11766 } | |
| 11767 } | |
| 11768 } | |
| 11769 | |
| 11770 var reservedTagList = [ | |
| 11771 'annotation-xml', 'color-profile', 'font-face', 'font-face-src', | |
| 11772 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph' | |
| 11773 ]; | |
| 11774 | |
| 11775 function ancestry(extnds) { | |
| 11776 var extendee = getRegisteredDefinition(extnds); | |
| 11777 if (extendee) { | |
| 11778 return ancestry(extendee.extends).concat([extendee]); | |
| 11779 } | |
| 11780 return []; | |
| 11781 } | |
| 11782 | |
| 11783 function resolveTagName(definition) { | |
| 11784 // if we are explicitly extending something, that thing is our | |
| 11785 // baseTag, unless it represents a custom component | |
| 11786 var baseTag = definition.extends; | |
| 11787 // if our ancestry includes custom components, we only have a | |
| 11788 // baseTag if one of them does | |
| 11789 for (var i=0, a; (a=definition.ancestry[i]); i++) { | |
| 11790 baseTag = a.is && a.tag; | |
| 11791 } | |
| 11792 // our tag is our baseTag, if it exists, and otherwise just our name | |
| 11793 definition.tag = baseTag || definition.__name; | |
| 11794 if (baseTag) { | |
| 11795 // if there is a base tag, use secondary 'is' specifier | |
| 11796 definition.is = definition.__name; | |
| 11797 } | |
| 11798 } | |
| 11799 | |
| 11800 function resolvePrototypeChain(definition) { | |
| 11801 // if we don't support __proto__ we need to locate the native level | |
| 11802 // prototype for precise mixing in | |
| 11803 if (!Object.__proto__) { | |
| 11804 // default prototype | |
| 11805 var nativePrototype = HTMLElement.prototype; | |
| 11806 // work out prototype when using type-extension | |
| 11807 if (definition.is) { | |
| 11808 var inst = document.createElement(definition.tag); | |
| 11809 var expectedPrototype = Object.getPrototypeOf(inst); | |
| 11810 // only set nativePrototype if it will actually appear in the definition
's chain | |
| 11811 if (expectedPrototype === definition.prototype) { | |
| 11812 nativePrototype = expectedPrototype; | |
| 11813 } | |
| 11814 } | |
| 11815 // ensure __proto__ reference is installed at each point on the prototype | |
| 11816 // chain. | |
| 11817 // NOTE: On platforms without __proto__, a mixin strategy is used instead | |
| 11818 // of prototype swizzling. In this case, this generated __proto__ provides | |
| 11819 // limited support for prototype traversal. | |
| 11820 var proto = definition.prototype, ancestor; | |
| 11821 while (proto && (proto !== nativePrototype)) { | |
| 11822 ancestor = Object.getPrototypeOf(proto); | |
| 11823 proto.__proto__ = ancestor; | |
| 11824 proto = ancestor; | |
| 11825 } | |
| 11826 // cache this in case of mixin | |
| 11827 definition.native = nativePrototype; | |
| 11828 } | |
| 11829 } | |
| 11830 | |
| 11831 // SECTION 4 | |
| 11832 | |
| 11833 function instantiate(definition) { | |
| 11834 // 4.a.1. Create a new object that implements PROTOTYPE | |
| 11835 // 4.a.2. Let ELEMENT by this new object | |
| 11836 // | |
| 11837 // the custom element instantiation algorithm must also ensure that the | |
| 11838 // output is a valid DOM element with the proper wrapper in place. | |
| 11839 // | |
| 11840 return upgrade(domCreateElement(definition.tag), definition); | |
| 11841 } | |
| 11842 | |
| 11843 function upgrade(element, definition) { | |
| 11844 // some definitions specify an 'is' attribute | |
| 11845 if (definition.is) { | |
| 11846 element.setAttribute('is', definition.is); | |
| 11847 } | |
| 11848 // make 'element' implement definition.prototype | |
| 11849 implement(element, definition); | |
| 11850 // flag as upgraded | |
| 11851 element.__upgraded__ = true; | |
| 11852 // lifecycle management | |
| 11853 created(element); | |
| 11854 // attachedCallback fires in tree order, call before recursing | |
| 11855 scope.insertedNode(element); | |
| 11856 // there should never be a shadow root on element at this point | |
| 11857 scope.upgradeSubtree(element); | |
| 11858 // OUTPUT | |
| 11859 return element; | |
| 11860 } | |
| 11861 | |
| 11862 function implement(element, definition) { | |
| 11863 // prototype swizzling is best | |
| 11864 if (Object.__proto__) { | |
| 11865 element.__proto__ = definition.prototype; | |
| 11866 } else { | |
| 11867 // where above we can re-acquire inPrototype via | |
| 11868 // getPrototypeOf(Element), we cannot do so when | |
| 11869 // we use mixin, so we install a magic reference | |
| 11870 customMixin(element, definition.prototype, definition.native); | |
| 11871 element.__proto__ = definition.prototype; | |
| 11872 } | |
| 11873 } | |
| 11874 | |
| 11875 function customMixin(inTarget, inSrc, inNative) { | |
| 11876 // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of | |
| 11877 // any property. This set should be precalculated. We also need to | |
| 11878 // consider this for supporting 'super'. | |
| 11879 var used = {}; | |
| 11880 // start with inSrc | |
| 11881 var p = inSrc; | |
| 11882 // The default is HTMLElement.prototype, so we add a test to avoid mixing in | |
| 11883 // native prototypes | |
| 11884 while (p !== inNative && p !== HTMLElement.prototype) { | |
| 11885 var keys = Object.getOwnPropertyNames(p); | |
| 11886 for (var i=0, k; k=keys[i]; i++) { | |
| 11887 if (!used[k]) { | |
| 11888 Object.defineProperty(inTarget, k, | |
| 11889 Object.getOwnPropertyDescriptor(p, k)); | |
| 11890 used[k] = 1; | |
| 11891 } | |
| 11892 } | |
| 11893 p = Object.getPrototypeOf(p); | |
| 11894 } | |
| 11895 } | |
| 11896 | |
| 11897 function created(element) { | |
| 11898 // invoke createdCallback | |
| 11899 if (element.createdCallback) { | |
| 11900 element.createdCallback(); | |
| 11901 } | |
| 11902 } | |
| 11903 | |
| 11904 // attribute watching | |
| 11905 | |
| 11906 function overrideAttributeApi(prototype) { | |
| 11907 // overrides to implement callbacks | |
| 11908 // TODO(sjmiles): should support access via .attributes NamedNodeMap | |
| 11909 // TODO(sjmiles): preserves user defined overrides, if any | |
| 11910 if (prototype.setAttribute._polyfilled) { | |
| 11911 return; | |
| 11912 } | |
| 11913 var setAttribute = prototype.setAttribute; | |
| 11914 prototype.setAttribute = function(name, value) { | |
| 11915 changeAttribute.call(this, name, value, setAttribute); | |
| 11916 } | |
| 11917 var removeAttribute = prototype.removeAttribute; | |
| 11918 prototype.removeAttribute = function(name) { | |
| 11919 changeAttribute.call(this, name, null, removeAttribute); | |
| 11920 } | |
| 11921 prototype.setAttribute._polyfilled = true; | |
| 11922 } | |
| 11923 | |
| 11924 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ | |
| 11925 // index.html#dfn-attribute-changed-callback | |
| 11926 function changeAttribute(name, value, operation) { | |
| 11927 name = name.toLowerCase(); | |
| 11928 var oldValue = this.getAttribute(name); | |
| 11929 operation.apply(this, arguments); | |
| 11930 var newValue = this.getAttribute(name); | |
| 11931 if (this.attributeChangedCallback | |
| 11932 && (newValue !== oldValue)) { | |
| 11933 this.attributeChangedCallback(name, oldValue, newValue); | |
| 11934 } | |
| 11935 } | |
| 11936 | |
| 11937 // element registry (maps tag names to definitions) | |
| 11938 | |
| 11939 var registry = {}; | |
| 11940 | |
| 11941 function getRegisteredDefinition(name) { | |
| 11942 if (name) { | |
| 11943 return registry[name.toLowerCase()]; | |
| 11944 } | |
| 11945 } | |
| 11946 | |
| 11947 function registerDefinition(name, definition) { | |
| 11948 registry[name] = definition; | |
| 11949 } | |
| 11950 | |
| 11951 function generateConstructor(definition) { | |
| 11952 return function() { | |
| 11953 return instantiate(definition); | |
| 11954 }; | |
| 11955 } | |
| 11956 | |
| 11957 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; | |
| 11958 function createElementNS(namespace, tag, typeExtension) { | |
| 11959 // NOTE: we do not support non-HTML elements, | |
| 11960 // just call createElementNS for non HTML Elements | |
| 11961 if (namespace === HTML_NAMESPACE) { | |
| 11962 return createElement(tag, typeExtension); | |
| 11963 } else { | |
| 11964 return domCreateElementNS(namespace, tag); | |
| 11965 } | |
| 11966 } | |
| 11967 | |
| 11968 function createElement(tag, typeExtension) { | |
| 11969 // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could | |
| 11970 // error check it, or perhaps there should only ever be one argument | |
| 11971 var definition = getRegisteredDefinition(typeExtension || tag); | |
| 11972 if (definition) { | |
| 11973 if (tag == definition.tag && typeExtension == definition.is) { | |
| 11974 return new definition.ctor(); | |
| 11975 } | |
| 11976 // Handle empty string for type extension. | |
| 11977 if (!typeExtension && !definition.is) { | |
| 11978 return new definition.ctor(); | |
| 11979 } | |
| 11980 } | |
| 11981 | |
| 11982 if (typeExtension) { | |
| 11983 var element = createElement(tag); | |
| 11984 element.setAttribute('is', typeExtension); | |
| 11985 return element; | |
| 11986 } | |
| 11987 var element = domCreateElement(tag); | |
| 11988 // Custom tags should be HTMLElements even if not upgraded. | |
| 11989 if (tag.indexOf('-') >= 0) { | |
| 11990 implement(element, HTMLElement); | |
| 11991 } | |
| 11992 return element; | |
| 11993 } | |
| 11994 | |
| 11995 function upgradeElement(element) { | |
| 11996 if (!element.__upgraded__ && (element.nodeType === Node.ELEMENT_NODE)) { | |
| 11997 var is = element.getAttribute('is'); | |
| 11998 var definition = getRegisteredDefinition(is || element.localName); | |
| 11999 if (definition) { | |
| 12000 if (is && definition.tag == element.localName) { | |
| 12001 return upgrade(element, definition); | |
| 12002 } else if (!is && !definition.extends) { | |
| 12003 return upgrade(element, definition); | |
| 12004 } | |
| 12005 } | |
| 12006 } | |
| 12007 } | |
| 12008 | |
| 12009 function cloneNode(deep) { | |
| 12010 // call original clone | |
| 12011 var n = domCloneNode.call(this, deep); | |
| 12012 // upgrade the element and subtree | |
| 12013 scope.upgradeAll(n); | |
| 12014 // return the clone | |
| 12015 return n; | |
| 12016 } | |
| 12017 // capture native createElement before we override it | |
| 12018 | |
| 12019 var domCreateElement = document.createElement.bind(document); | |
| 12020 var domCreateElementNS = document.createElementNS.bind(document); | |
| 12021 | |
| 12022 // capture native cloneNode before we override it | |
| 12023 | |
| 12024 var domCloneNode = Node.prototype.cloneNode; | |
| 12025 | |
| 12026 // exports | |
| 12027 | |
| 12028 document.registerElement = register; | |
| 12029 document.createElement = createElement; // override | |
| 12030 document.createElementNS = createElementNS; // override | |
| 12031 Node.prototype.cloneNode = cloneNode; // override | |
| 12032 | |
| 12033 scope.registry = registry; | |
| 12034 | |
| 12035 /** | |
| 12036 * Upgrade an element to a custom element. Upgrading an element | |
| 12037 * causes the custom prototype to be applied, an `is` attribute | |
| 12038 * to be attached (as needed), and invocation of the `readyCallback`. | |
| 12039 * `upgrade` does nothing if the element is already upgraded, or | |
| 12040 * if it matches no registered custom tag name. | |
| 12041 * | |
| 12042 * @method ugprade | |
| 12043 * @param {Element} element The element to upgrade. | |
| 12044 * @return {Element} The upgraded element. | |
| 12045 */ | |
| 12046 scope.upgrade = upgradeElement; | |
| 12047 } | |
| 12048 | |
| 12049 // Create a custom 'instanceof'. This is necessary when CustomElements | |
| 12050 // are implemented via a mixin strategy, as for example on IE10. | |
| 12051 var isInstance; | |
| 12052 if (!Object.__proto__ && !useNative) { | |
| 12053 isInstance = function(obj, ctor) { | |
| 12054 var p = obj; | |
| 12055 while (p) { | |
| 12056 // NOTE: this is not technically correct since we're not checking if | |
| 12057 // an object is an instance of a constructor; however, this should | |
| 12058 // be good enough for the mixin strategy. | |
| 12059 if (p === ctor.prototype) { | |
| 12060 return true; | |
| 12061 } | |
| 12062 p = p.__proto__; | |
| 12063 } | |
| 12064 return false; | |
| 12065 } | |
| 12066 } else { | |
| 12067 isInstance = function(obj, base) { | |
| 12068 return obj instanceof base; | |
| 12069 } | |
| 12070 } | |
| 12071 | |
| 12072 // exports | |
| 12073 scope.instanceof = isInstance; | |
| 12074 scope.reservedTagList = reservedTagList; | |
| 12075 | |
| 12076 // bc | |
| 12077 document.register = document.registerElement; | |
| 12078 | |
| 12079 scope.hasNative = hasNative; | |
| 12080 scope.useNative = useNative; | |
| 12081 | |
| 12082 })(window.CustomElements); | |
| 12083 | |
| 12084 /* | |
| 12085 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12086 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12087 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12088 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12089 * Code distributed by Google as part of the polymer project is also | |
| 12090 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12091 */ | |
| 12092 | |
| 12093 (function(scope) { | |
| 12094 | |
| 12095 // import | |
| 12096 | |
| 12097 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; | |
| 12098 | |
| 12099 // highlander object for parsing a document tree | |
| 12100 | |
| 12101 var parser = { | |
| 12102 selectors: [ | |
| 12103 'link[rel=' + IMPORT_LINK_TYPE + ']' | |
| 12104 ], | |
| 12105 map: { | |
| 12106 link: 'parseLink' | |
| 12107 }, | |
| 12108 parse: function(inDocument) { | |
| 12109 if (!inDocument.__parsed) { | |
| 12110 // only parse once | |
| 12111 inDocument.__parsed = true; | |
| 12112 // all parsable elements in inDocument (depth-first pre-order traversal) | |
| 12113 var elts = inDocument.querySelectorAll(parser.selectors); | |
| 12114 // for each parsable node type, call the mapped parsing method | |
| 12115 forEach(elts, function(e) { | |
| 12116 parser[parser.map[e.localName]](e); | |
| 12117 }); | |
| 12118 // upgrade all upgradeable static elements, anything dynamically | |
| 12119 // created should be caught by observer | |
| 12120 CustomElements.upgradeDocument(inDocument); | |
| 12121 // observe document for dom changes | |
| 12122 CustomElements.observeDocument(inDocument); | |
| 12123 } | |
| 12124 }, | |
| 12125 parseLink: function(linkElt) { | |
| 12126 // imports | |
| 12127 if (isDocumentLink(linkElt)) { | |
| 12128 this.parseImport(linkElt); | |
| 12129 } | |
| 12130 }, | |
| 12131 parseImport: function(linkElt) { | |
| 12132 if (linkElt.import) { | |
| 12133 parser.parse(linkElt.import); | |
| 12134 } | |
| 12135 } | |
| 12136 }; | |
| 12137 | |
| 12138 function isDocumentLink(inElt) { | |
| 12139 return (inElt.localName === 'link' | |
| 12140 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); | |
| 12141 } | |
| 12142 | |
| 12143 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | |
| 12144 | |
| 12145 // exports | |
| 12146 | |
| 12147 scope.parser = parser; | |
| 12148 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
| 12149 | |
| 12150 })(window.CustomElements); | |
| 12151 /* | |
| 12152 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12153 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12154 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12155 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12156 * Code distributed by Google as part of the polymer project is also | |
| 12157 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12158 */ | |
| 12159 (function(scope){ | |
| 12160 | |
| 12161 // bootstrap parsing | |
| 12162 function bootstrap() { | |
| 12163 // parse document | |
| 12164 CustomElements.parser.parse(document); | |
| 12165 // one more pass before register is 'live' | |
| 12166 CustomElements.upgradeDocument(document); | |
| 12167 // install upgrade hook if HTMLImports are available | |
| 12168 if (window.HTMLImports) { | |
| 12169 HTMLImports.__importsParsingHook = function(elt) { | |
| 12170 CustomElements.parser.parse(elt.import); | |
| 12171 } | |
| 12172 } | |
| 12173 // set internal 'ready' flag, now document.registerElement will trigger | |
| 12174 // synchronous upgrades | |
| 12175 CustomElements.ready = true; | |
| 12176 // async to ensure *native* custom elements upgrade prior to this | |
| 12177 // DOMContentLoaded can fire before elements upgrade (e.g. when there's | |
| 12178 // an external script) | |
| 12179 setTimeout(function() { | |
| 12180 // capture blunt profiling data | |
| 12181 CustomElements.readyTime = Date.now(); | |
| 12182 if (window.HTMLImports) { | |
| 12183 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; | |
| 12184 } | |
| 12185 // notify the system that we are bootstrapped | |
| 12186 document.dispatchEvent( | |
| 12187 new CustomEvent('WebComponentsReady', {bubbles: true}) | |
| 12188 ); | |
| 12189 }); | |
| 12190 } | |
| 12191 | |
| 12192 // CustomEvent shim for IE | |
| 12193 if (typeof window.CustomEvent !== 'function') { | |
| 12194 window.CustomEvent = function(inType, params) { | |
| 12195 params = params || {}; | |
| 12196 var e = document.createEvent('CustomEvent'); | |
| 12197 e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable
), params.detail); | |
| 12198 return e; | |
| 12199 }; | |
| 12200 window.CustomEvent.prototype = window.Event.prototype; | |
| 12201 } | |
| 12202 | |
| 12203 // When loading at readyState complete time (or via flag), boot custom elements | |
| 12204 // immediately. | |
| 12205 // If relevant, HTMLImports must already be loaded. | |
| 12206 if (document.readyState === 'complete' || scope.flags.eager) { | |
| 12207 bootstrap(); | |
| 12208 // When loading at readyState interactive time, bootstrap only if HTMLImports | |
| 12209 // are not pending. Also avoid IE as the semantics of this state are unreliable. | |
| 12210 } else if (document.readyState === 'interactive' && !window.attachEvent && | |
| 12211 (!window.HTMLImports || window.HTMLImports.ready)) { | |
| 12212 bootstrap(); | |
| 12213 // When loading at other readyStates, wait for the appropriate DOM event to | |
| 12214 // bootstrap. | |
| 12215 } else { | |
| 12216 var loadEvent = window.HTMLImports && !HTMLImports.ready ? | |
| 12217 'HTMLImportsLoaded' : 'DOMContentLoaded'; | |
| 12218 window.addEventListener(loadEvent, bootstrap); | |
| 12219 } | |
| 12220 | |
| 12221 })(window.CustomElements); | |
| 12222 | |
| 12223 /* | |
| 12224 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12225 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12226 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12227 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12228 * Code distributed by Google as part of the polymer project is also | |
| 12229 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12230 */ | |
| 12231 | |
| 12232 (function() { | |
| 12233 | |
| 12234 if (window.ShadowDOMPolyfill) { | |
| 12235 | |
| 12236 // ensure wrapped inputs for these functions | |
| 12237 var fns = ['upgradeAll', 'upgradeSubtree', 'observeDocument', | |
| 12238 'upgradeDocument']; | |
| 12239 | |
| 12240 // cache originals | |
| 12241 var original = {}; | |
| 12242 fns.forEach(function(fn) { | |
| 12243 original[fn] = CustomElements[fn]; | |
| 12244 }); | |
| 12245 | |
| 12246 // override | |
| 12247 fns.forEach(function(fn) { | |
| 12248 CustomElements[fn] = function(inNode) { | |
| 12249 return original[fn](wrap(inNode)); | |
| 12250 }; | |
| 12251 }); | |
| 12252 | |
| 12253 } | |
| 12254 | |
| 12255 })(); | |
| 12256 | |
| 12257 /* | |
| 12258 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12259 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12260 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12261 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12262 * Code distributed by Google as part of the polymer project is also | |
| 12263 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12264 */ | |
| 12265 | |
| 12266 (function(scope) { | |
| 12267 | |
| 12268 'use strict'; | |
| 12269 | |
| 12270 // polyfill performance.now | |
| 12271 | |
| 12272 if (!window.performance) { | |
| 12273 var start = Date.now(); | |
| 12274 // only at millisecond precision | |
| 12275 window.performance = {now: function(){ return Date.now() - start }}; | |
| 12276 } | |
| 12277 | |
| 12278 // polyfill for requestAnimationFrame | |
| 12279 | |
| 12280 if (!window.requestAnimationFrame) { | |
| 12281 window.requestAnimationFrame = (function() { | |
| 12282 var nativeRaf = window.webkitRequestAnimationFrame || | |
| 12283 window.mozRequestAnimationFrame; | |
| 12284 | |
| 12285 return nativeRaf ? | |
| 12286 function(callback) { | |
| 12287 return nativeRaf(function() { | |
| 12288 callback(performance.now()); | |
| 12289 }); | |
| 12290 } : | |
| 12291 function( callback ){ | |
| 12292 return window.setTimeout(callback, 1000 / 60); | |
| 12293 }; | |
| 12294 })(); | |
| 12295 } | |
| 12296 | |
| 12297 if (!window.cancelAnimationFrame) { | |
| 12298 window.cancelAnimationFrame = (function() { | |
| 12299 return window.webkitCancelAnimationFrame || | |
| 12300 window.mozCancelAnimationFrame || | |
| 12301 function(id) { | |
| 12302 clearTimeout(id); | |
| 12303 }; | |
| 12304 })(); | |
| 12305 } | |
| 12306 | |
| 12307 // Make a stub for Polymer() for polyfill purposes; under the HTMLImports | |
| 12308 // polyfill, scripts in the main document run before imports. That means | |
| 12309 // if (1) polymer is imported and (2) Polymer() is called in the main document | |
| 12310 // in a script after the import, 2 occurs before 1. We correct this here | |
| 12311 // by specfiically patching Polymer(); this is not necessary under native | |
| 12312 // HTMLImports. | |
| 12313 var elementDeclarations = []; | |
| 12314 | |
| 12315 var polymerStub = function(name, dictionary) { | |
| 12316 if ((typeof name !== 'string') && (arguments.length === 1)) { | |
| 12317 Array.prototype.push.call(arguments, document._currentScript); | |
| 12318 } | |
| 12319 elementDeclarations.push(arguments); | |
| 12320 }; | |
| 12321 window.Polymer = polymerStub; | |
| 12322 | |
| 12323 // deliver queued delcarations | |
| 12324 scope.consumeDeclarations = function(callback) { | |
| 12325 scope.consumeDeclarations = function() { | |
| 12326 throw 'Possible attempt to load Polymer twice'; | |
| 12327 }; | |
| 12328 if (callback) { | |
| 12329 callback(elementDeclarations); | |
| 12330 } | |
| 12331 elementDeclarations = null; | |
| 12332 }; | |
| 12333 | |
| 12334 function installPolymerWarning() { | |
| 12335 if (window.Polymer === polymerStub) { | |
| 12336 window.Polymer = function() { | |
| 12337 throw new Error('You tried to use polymer without loading it first. To '
+ | |
| 12338 'load polymer, <link rel="import" href="' + | |
| 12339 'components/polymer/polymer.html">'); | |
| 12340 }; | |
| 12341 } | |
| 12342 } | |
| 12343 | |
| 12344 // Once DOMContent has loaded, any main document scripts that depend on | |
| 12345 // Polymer() should have run. Calling Polymer() now is an error until | |
| 12346 // polymer is imported. | |
| 12347 if (HTMLImports.useNative) { | |
| 12348 installPolymerWarning(); | |
| 12349 } else { | |
| 12350 addEventListener('DOMContentLoaded', installPolymerWarning); | |
| 12351 } | |
| 12352 | |
| 12353 })(window.Platform); | |
| 12354 | |
| 12355 /* | |
| 12356 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12357 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12358 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12359 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12360 * Code distributed by Google as part of the polymer project is also | |
| 12361 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12362 */ | |
| 12363 | |
| 12364 (function(scope) { | |
| 12365 | |
| 12366 // It's desireable to provide a default stylesheet | |
| 12367 // that's convenient for styling unresolved elements, but | |
| 12368 // it's cumbersome to have to include this manually in every page. | |
| 12369 // It would make sense to put inside some HTMLImport but | |
| 12370 // the HTMLImports polyfill does not allow loading of stylesheets | |
| 12371 // that block rendering. Therefore this injection is tolerated here. | |
| 12372 // | |
| 12373 // NOTE: position: relative fixes IE's failure to inherit opacity | |
| 12374 // when a child is not statically positioned. | |
| 12375 var style = document.createElement('style'); | |
| 12376 style.textContent = '' | |
| 12377 + 'body {' | |
| 12378 + 'transition: opacity ease-in 0.2s;' | |
| 12379 + ' } \n' | |
| 12380 + 'body[unresolved] {' | |
| 12381 + 'opacity: 0; display: block; overflow: hidden; position: relative;' | |
| 12382 + ' } \n' | |
| 12383 ; | |
| 12384 var head = document.querySelector('head'); | |
| 12385 head.insertBefore(style, head.firstChild); | |
| 12386 | |
| 12387 })(Platform); | |
| 12388 | |
| 12389 /* | |
| 12390 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
| 12391 * This code may only be used under the BSD style license found at http://polyme
r.github.io/LICENSE.txt | |
| 12392 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.
txt | |
| 12393 * The complete set of contributors may be found at http://polymer.github.io/CON
TRIBUTORS.txt | |
| 12394 * Code distributed by Google as part of the polymer project is also | |
| 12395 * subject to an additional IP rights grant found at http://polymer.github.io/PA
TENTS.txt | |
| 12396 */ | |
| 12397 | |
| 12398 (function(scope) { | |
| 12399 | |
| 12400 function withDependencies(task, depends) { | |
| 12401 depends = depends || []; | |
| 12402 if (!depends.map) { | |
| 12403 depends = [depends]; | |
| 12404 } | |
| 12405 return task.apply(this, depends.map(marshal)); | |
| 12406 } | |
| 12407 | |
| 12408 function module(name, dependsOrFactory, moduleFactory) { | |
| 12409 var module; | |
| 12410 switch (arguments.length) { | |
| 12411 case 0: | |
| 12412 return; | |
| 12413 case 1: | |
| 12414 module = null; | |
| 12415 break; | |
| 12416 case 2: | |
| 12417 // dependsOrFactory is `factory` in this case | |
| 12418 module = dependsOrFactory.apply(this); | |
| 12419 break; | |
| 12420 default: | |
| 12421 // dependsOrFactory is `depends` in this case | |
| 12422 module = withDependencies(moduleFactory, dependsOrFactory); | |
| 12423 break; | |
| 12424 } | |
| 12425 modules[name] = module; | |
| 12426 }; | |
| 12427 | |
| 12428 function marshal(name) { | |
| 12429 return modules[name]; | |
| 12430 } | |
| 12431 | |
| 12432 var modules = {}; | |
| 12433 | |
| 12434 function using(depends, task) { | |
| 12435 HTMLImports.whenImportsReady(function() { | |
| 12436 withDependencies(task, depends); | |
| 12437 }); | |
| 12438 }; | |
| 12439 | |
| 12440 // exports | |
| 12441 | |
| 12442 scope.marshal = marshal; | |
| 12443 // `module` confuses commonjs detectors | |
| 12444 scope.modularize = module; | |
| 12445 scope.using = using; | |
| 12446 | |
| 12447 })(window); | |
| 12448 | |
| 12449 //# sourceMappingURL=platform.concat.js.map | |
| OLD | NEW |