Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Polymer Authors. All rights reserved. | 1 // Copyright (c) 2012 The Polymer Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // Redistribution and use in source and binary forms, with or without |
| 4 // modification, are permitted provided that the following conditions are | 4 // modification, are permitted provided that the following conditions are |
| 5 // met: | 5 // met: |
| 6 // | 6 // |
| 7 // * Redistributions of source code must retain the above copyright | 7 // * Redistributions of source code must retain the above copyright |
| 8 // notice, this list of conditions and the following disclaimer. | 8 // notice, this list of conditions and the following disclaimer. |
| 9 // * Redistributions in binary form must reproduce the above | 9 // * Redistributions in binary form must reproduce the above |
| 10 // copyright notice, this list of conditions and the following disclaimer | 10 // copyright notice, this list of conditions and the following disclaimer |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 49 }, | 49 }, |
| 50 delete: function(key) { | 50 delete: function(key) { |
| 51 this.set(key, undefined); | 51 this.set(key, undefined); |
| 52 } | 52 } |
| 53 }; | 53 }; |
| 54 | 54 |
| 55 window.WeakMap = WeakMap; | 55 window.WeakMap = WeakMap; |
| 56 })(); | 56 })(); |
| 57 } | 57 } |
| 58 | 58 |
| 59 (function(global) { | |
|
blois
2014/01/27 20:20:26
Looks like the MutationObserver polyfill is extern
Jennifer Messerly
2014/01/29 20:52:37
good catch! will fix that in the tests
| |
| 60 | |
| 61 var registrationsTable = new WeakMap(); | |
| 62 | |
| 63 // We use setImmediate or postMessage for our future callback. | |
| 64 var setImmediate = window.msSetImmediate; | |
| 65 | |
| 66 // Use post message to emulate setImmediate. | |
| 67 if (!setImmediate) { | |
| 68 var setImmediateQueue = []; | |
| 69 var sentinel = String(Math.random()); | |
| 70 window.addEventListener('message', function(e) { | |
| 71 if (e.data === sentinel) { | |
| 72 var queue = setImmediateQueue; | |
| 73 setImmediateQueue = []; | |
| 74 queue.forEach(function(func) { | |
| 75 func(); | |
| 76 }); | |
| 77 } | |
| 78 }); | |
| 79 setImmediate = function(func) { | |
| 80 setImmediateQueue.push(func); | |
| 81 window.postMessage(sentinel, '*'); | |
| 82 }; | |
| 83 } | |
| 84 | |
| 85 // This is used to ensure that we never schedule 2 callas to setImmediate | |
| 86 var isScheduled = false; | |
| 87 | |
| 88 // Keep track of observers that needs to be notified next time. | |
| 89 var scheduledObservers = []; | |
| 90 | |
| 91 /** | |
| 92 * Schedules |dispatchCallback| to be called in the future. | |
| 93 * @param {MutationObserver} observer | |
| 94 */ | |
| 95 function scheduleCallback(observer) { | |
| 96 scheduledObservers.push(observer); | |
| 97 if (!isScheduled) { | |
| 98 isScheduled = true; | |
| 99 setImmediate(dispatchCallbacks); | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 function wrapIfNeeded(node) { | |
| 104 return window.ShadowDOMPolyfill && | |
| 105 window.ShadowDOMPolyfill.wrapIfNeeded(node) || | |
| 106 node; | |
| 107 } | |
| 108 | |
| 109 function dispatchCallbacks() { | |
| 110 // http://dom.spec.whatwg.org/#mutation-observers | |
| 111 | |
| 112 isScheduled = false; // Used to allow a new setImmediate call above. | |
| 113 | |
| 114 var observers = scheduledObservers; | |
| 115 scheduledObservers = []; | |
| 116 // Sort observers based on their creation UID (incremental). | |
| 117 observers.sort(function(o1, o2) { | |
| 118 return o1.uid_ - o2.uid_; | |
| 119 }); | |
| 120 | |
| 121 var anyNonEmpty = false; | |
| 122 observers.forEach(function(observer) { | |
| 123 | |
| 124 // 2.1, 2.2 | |
| 125 var queue = observer.takeRecords(); | |
| 126 // 2.3. Remove all transient registered observers whose observer is mo. | |
| 127 removeTransientObserversFor(observer); | |
| 128 | |
| 129 // 2.4 | |
| 130 if (queue.length) { | |
| 131 observer.callback_(queue, observer); | |
| 132 anyNonEmpty = true; | |
| 133 } | |
| 134 }); | |
| 135 | |
| 136 // 3. | |
| 137 if (anyNonEmpty) | |
| 138 dispatchCallbacks(); | |
| 139 } | |
| 140 | |
| 141 function removeTransientObserversFor(observer) { | |
| 142 observer.nodes_.forEach(function(node) { | |
| 143 var registrations = registrationsTable.get(node); | |
| 144 if (!registrations) | |
| 145 return; | |
| 146 registrations.forEach(function(registration) { | |
| 147 if (registration.observer === observer) | |
| 148 registration.removeTransientObservers(); | |
| 149 }); | |
| 150 }); | |
| 151 } | |
| 152 | |
| 153 /** | |
| 154 * This function is used for the "For each registered observer observer (with | |
| 155 * observer's options as options) in target's list of registered observers, | |
| 156 * run these substeps:" and the "For each ancestor ancestor of target, and for | |
| 157 * each registered observer observer (with options options) in ancestor's list | |
| 158 * of registered observers, run these substeps:" part of the algorithms. The | |
| 159 * |options.subtree| is checked to ensure that the callback is called | |
| 160 * correctly. | |
| 161 * | |
| 162 * @param {Node} target | |
| 163 * @param {function(MutationObserverInit):MutationRecord} callback | |
| 164 */ | |
| 165 function forEachAncestorAndObserverEnqueueRecord(target, callback) { | |
| 166 for (var node = target; node; node = node.parentNode) { | |
| 167 var registrations = registrationsTable.get(node); | |
| 168 | |
| 169 if (registrations) { | |
| 170 for (var j = 0; j < registrations.length; j++) { | |
| 171 var registration = registrations[j]; | |
| 172 var options = registration.options; | |
| 173 | |
| 174 // Only target ignores subtree. | |
| 175 if (node !== target && !options.subtree) | |
| 176 continue; | |
| 177 | |
| 178 var record = callback(options); | |
| 179 if (record) | |
| 180 registration.enqueue(record); | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 var uidCounter = 0; | |
| 187 | |
| 188 /** | |
| 189 * The class that maps to the DOM MutationObserver interface. | |
| 190 * @param {Function} callback. | |
| 191 * @constructor | |
| 192 */ | |
| 193 function JsMutationObserver(callback) { | |
| 194 this.callback_ = callback; | |
| 195 this.nodes_ = []; | |
| 196 this.records_ = []; | |
| 197 this.uid_ = ++uidCounter; | |
| 198 } | |
| 199 | |
| 200 JsMutationObserver.prototype = { | |
| 201 observe: function(target, options) { | |
| 202 target = wrapIfNeeded(target); | |
| 203 | |
| 204 // 1.1 | |
| 205 if (!options.childList && !options.attributes && !options.characterData || | |
| 206 | |
| 207 // 1.2 | |
| 208 options.attributeOldValue && !options.attributes || | |
| 209 | |
| 210 // 1.3 | |
| 211 options.attributeFilter && options.attributeFilter.length && | |
| 212 !options.attributes || | |
| 213 | |
| 214 // 1.4 | |
| 215 options.characterDataOldValue && !options.characterData) { | |
| 216 | |
| 217 throw new SyntaxError(); | |
| 218 } | |
| 219 | |
| 220 var registrations = registrationsTable.get(target); | |
| 221 if (!registrations) | |
| 222 registrationsTable.set(target, registrations = []); | |
| 223 | |
| 224 // 2 | |
| 225 // If target's list of registered observers already includes a registered | |
| 226 // observer associated with the context object, replace that registered | |
| 227 // observer's options with options. | |
| 228 var registration; | |
| 229 for (var i = 0; i < registrations.length; i++) { | |
| 230 if (registrations[i].observer === this) { | |
| 231 registration = registrations[i]; | |
| 232 registration.removeListeners(); | |
| 233 registration.options = options; | |
| 234 break; | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 // 3. | |
| 239 // Otherwise, add a new registered observer to target's list of registered | |
| 240 // observers with the context object as the observer and options as the | |
| 241 // options, and add target to context object's list of nodes on which it | |
| 242 // is registered. | |
| 243 if (!registration) { | |
| 244 registration = new Registration(this, target, options); | |
| 245 registrations.push(registration); | |
| 246 this.nodes_.push(target); | |
| 247 } | |
| 248 | |
| 249 registration.addListeners(); | |
| 250 }, | |
| 251 | |
| 252 disconnect: function() { | |
| 253 this.nodes_.forEach(function(node) { | |
| 254 var registrations = registrationsTable.get(node); | |
| 255 for (var i = 0; i < registrations.length; i++) { | |
| 256 var registration = registrations[i]; | |
| 257 if (registration.observer === this) { | |
| 258 registration.removeListeners(); | |
| 259 registrations.splice(i, 1); | |
| 260 // Each node can only have one registered observer associated with | |
| 261 // this observer. | |
| 262 break; | |
| 263 } | |
| 264 } | |
| 265 }, this); | |
| 266 this.records_ = []; | |
| 267 }, | |
| 268 | |
| 269 takeRecords: function() { | |
| 270 var copyOfRecords = this.records_; | |
| 271 this.records_ = []; | |
| 272 return copyOfRecords; | |
| 273 } | |
| 274 }; | |
| 275 | |
| 276 /** | |
| 277 * @param {string} type | |
| 278 * @param {Node} target | |
| 279 * @constructor | |
| 280 */ | |
| 281 function MutationRecord(type, target) { | |
| 282 this.type = type; | |
| 283 this.target = target; | |
| 284 this.addedNodes = []; | |
| 285 this.removedNodes = []; | |
| 286 this.previousSibling = null; | |
| 287 this.nextSibling = null; | |
| 288 this.attributeName = null; | |
| 289 this.attributeNamespace = null; | |
| 290 this.oldValue = null; | |
| 291 } | |
| 292 | |
| 293 function copyMutationRecord(original) { | |
| 294 var record = new MutationRecord(original.type, original.target); | |
| 295 record.addedNodes = original.addedNodes.slice(); | |
| 296 record.removedNodes = original.removedNodes.slice(); | |
| 297 record.previousSibling = original.previousSibling; | |
| 298 record.nextSibling = original.nextSibling; | |
| 299 record.attributeName = original.attributeName; | |
| 300 record.attributeNamespace = original.attributeNamespace; | |
| 301 record.oldValue = original.oldValue; | |
| 302 return record; | |
| 303 }; | |
| 304 | |
| 305 // We keep track of the two (possibly one) records used in a single mutation. | |
| 306 var currentRecord, recordWithOldValue; | |
| 307 | |
| 308 /** | |
| 309 * Creates a record without |oldValue| and caches it as |currentRecord| for | |
| 310 * later use. | |
| 311 * @param {string} oldValue | |
| 312 * @return {MutationRecord} | |
| 313 */ | |
| 314 function getRecord(type, target) { | |
| 315 return currentRecord = new MutationRecord(type, target); | |
| 316 } | |
| 317 | |
| 318 /** | |
| 319 * Gets or creates a record with |oldValue| based in the |currentRecord| | |
| 320 * @param {string} oldValue | |
| 321 * @return {MutationRecord} | |
| 322 */ | |
| 323 function getRecordWithOldValue(oldValue) { | |
| 324 if (recordWithOldValue) | |
| 325 return recordWithOldValue; | |
| 326 recordWithOldValue = copyMutationRecord(currentRecord); | |
| 327 recordWithOldValue.oldValue = oldValue; | |
| 328 return recordWithOldValue; | |
| 329 } | |
| 330 | |
| 331 function clearRecords() { | |
| 332 currentRecord = recordWithOldValue = undefined; | |
| 333 } | |
| 334 | |
| 335 /** | |
| 336 * @param {MutationRecord} record | |
| 337 * @return {boolean} Whether the record represents a record from the current | |
| 338 * mutation event. | |
| 339 */ | |
| 340 function recordRepresentsCurrentMutation(record) { | |
| 341 return record === recordWithOldValue || record === currentRecord; | |
| 342 } | |
| 343 | |
| 344 /** | |
| 345 * Selects which record, if any, to replace the last record in the queue. | |
| 346 * This returns |null| if no record should be replaced. | |
| 347 * | |
| 348 * @param {MutationRecord} lastRecord | |
| 349 * @param {MutationRecord} newRecord | |
| 350 * @param {MutationRecord} | |
| 351 */ | |
| 352 function selectRecord(lastRecord, newRecord) { | |
| 353 if (lastRecord === newRecord) | |
| 354 return lastRecord; | |
| 355 | |
| 356 // Check if the the record we are adding represents the same record. If | |
| 357 // so, we keep the one with the oldValue in it. | |
| 358 if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) | |
| 359 return recordWithOldValue; | |
| 360 | |
| 361 return null; | |
| 362 } | |
| 363 | |
| 364 /** | |
| 365 * Class used to represent a registered observer. | |
| 366 * @param {MutationObserver} observer | |
| 367 * @param {Node} target | |
| 368 * @param {MutationObserverInit} options | |
| 369 * @constructor | |
| 370 */ | |
| 371 function Registration(observer, target, options) { | |
| 372 this.observer = observer; | |
| 373 this.target = target; | |
| 374 this.options = options; | |
| 375 this.transientObservedNodes = []; | |
| 376 } | |
| 377 | |
| 378 Registration.prototype = { | |
| 379 enqueue: function(record) { | |
| 380 var records = this.observer.records_; | |
| 381 var length = records.length; | |
| 382 | |
| 383 // There are cases where we replace the last record with the new record. | |
| 384 // For example if the record represents the same mutation we need to use | |
| 385 // the one with the oldValue. If we get same record (this can happen as we | |
| 386 // walk up the tree) we ignore the new record. | |
| 387 if (records.length > 0) { | |
| 388 var lastRecord = records[length - 1]; | |
| 389 var recordToReplaceLast = selectRecord(lastRecord, record); | |
| 390 if (recordToReplaceLast) { | |
| 391 records[length - 1] = recordToReplaceLast; | |
| 392 return; | |
| 393 } | |
| 394 } else { | |
| 395 scheduleCallback(this.observer); | |
| 396 } | |
| 397 | |
| 398 records[length] = record; | |
| 399 }, | |
| 400 | |
| 401 addListeners: function() { | |
| 402 this.addListeners_(this.target); | |
| 403 }, | |
| 404 | |
| 405 addListeners_: function(node) { | |
| 406 var options = this.options; | |
| 407 if (options.attributes) | |
| 408 node.addEventListener('DOMAttrModified', this, true); | |
| 409 | |
| 410 if (options.characterData) | |
| 411 node.addEventListener('DOMCharacterDataModified', this, true); | |
| 412 | |
| 413 if (options.childList) | |
| 414 node.addEventListener('DOMNodeInserted', this, true); | |
| 415 | |
| 416 if (options.childList || options.subtree) | |
| 417 node.addEventListener('DOMNodeRemoved', this, true); | |
| 418 }, | |
| 419 | |
| 420 removeListeners: function() { | |
| 421 this.removeListeners_(this.target); | |
| 422 }, | |
| 423 | |
| 424 removeListeners_: function(node) { | |
| 425 var options = this.options; | |
| 426 if (options.attributes) | |
| 427 node.removeEventListener('DOMAttrModified', this, true); | |
| 428 | |
| 429 if (options.characterData) | |
| 430 node.removeEventListener('DOMCharacterDataModified', this, true); | |
| 431 | |
| 432 if (options.childList) | |
| 433 node.removeEventListener('DOMNodeInserted', this, true); | |
| 434 | |
| 435 if (options.childList || options.subtree) | |
| 436 node.removeEventListener('DOMNodeRemoved', this, true); | |
| 437 }, | |
| 438 | |
| 439 /** | |
| 440 * Adds a transient observer on node. The transient observer gets removed | |
| 441 * next time we deliver the change records. | |
| 442 * @param {Node} node | |
| 443 */ | |
| 444 addTransientObserver: function(node) { | |
| 445 // Don't add transient observers on the target itself. We already have all | |
| 446 // the required listeners set up on the target. | |
| 447 if (node === this.target) | |
| 448 return; | |
| 449 | |
| 450 this.addListeners_(node); | |
| 451 this.transientObservedNodes.push(node); | |
| 452 var registrations = registrationsTable.get(node); | |
| 453 if (!registrations) | |
| 454 registrationsTable.set(node, registrations = []); | |
| 455 | |
| 456 // We know that registrations does not contain this because we already | |
| 457 // checked if node === this.target. | |
| 458 registrations.push(this); | |
| 459 }, | |
| 460 | |
| 461 removeTransientObservers: function() { | |
| 462 var transientObservedNodes = this.transientObservedNodes; | |
| 463 this.transientObservedNodes = []; | |
| 464 | |
| 465 transientObservedNodes.forEach(function(node) { | |
| 466 // Transient observers are never added to the target. | |
| 467 this.removeListeners_(node); | |
| 468 | |
| 469 var registrations = registrationsTable.get(node); | |
| 470 for (var i = 0; i < registrations.length; i++) { | |
| 471 if (registrations[i] === this) { | |
| 472 registrations.splice(i, 1); | |
| 473 // Each node can only have one registered observer associated with | |
| 474 // this observer. | |
| 475 break; | |
| 476 } | |
| 477 } | |
| 478 }, this); | |
| 479 }, | |
| 480 | |
| 481 handleEvent: function(e) { | |
| 482 // Stop propagation since we are managing the propagation manually. | |
| 483 // This means that other mutation events on the page will not work | |
| 484 // correctly but that is by design. | |
| 485 e.stopImmediatePropagation(); | |
| 486 | |
| 487 switch (e.type) { | |
| 488 case 'DOMAttrModified': | |
| 489 // http://dom.spec.whatwg.org/#concept-mo-queue-attributes | |
| 490 | |
| 491 var name = e.attrName; | |
| 492 var namespace = e.relatedNode.namespaceURI; | |
| 493 var target = e.target; | |
| 494 | |
| 495 // 1. | |
| 496 var record = new getRecord('attributes', target); | |
| 497 record.attributeName = name; | |
| 498 record.attributeNamespace = namespace; | |
| 499 | |
| 500 // 2. | |
| 501 var oldValue = | |
| 502 e.attrChange === MutationEvent.ADDITION ? null : e.prevValue; | |
| 503 | |
| 504 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 505 // 3.1, 4.2 | |
| 506 if (!options.attributes) | |
| 507 return; | |
| 508 | |
| 509 // 3.2, 4.3 | |
| 510 if (options.attributeFilter && options.attributeFilter.length && | |
| 511 options.attributeFilter.indexOf(name) === -1 && | |
| 512 options.attributeFilter.indexOf(namespace) === -1) { | |
| 513 return; | |
| 514 } | |
| 515 // 3.3, 4.4 | |
| 516 if (options.attributeOldValue) | |
| 517 return getRecordWithOldValue(oldValue); | |
| 518 | |
| 519 // 3.4, 4.5 | |
| 520 return record; | |
| 521 }); | |
| 522 | |
| 523 break; | |
| 524 | |
| 525 case 'DOMCharacterDataModified': | |
| 526 // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata | |
| 527 var target = e.target; | |
| 528 | |
| 529 // 1. | |
| 530 var record = getRecord('characterData', target); | |
| 531 | |
| 532 // 2. | |
| 533 var oldValue = e.prevValue; | |
| 534 | |
| 535 | |
| 536 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 537 // 3.1, 4.2 | |
| 538 if (!options.characterData) | |
| 539 return; | |
| 540 | |
| 541 // 3.2, 4.3 | |
| 542 if (options.characterDataOldValue) | |
| 543 return getRecordWithOldValue(oldValue); | |
| 544 | |
| 545 // 3.3, 4.4 | |
| 546 return record; | |
| 547 }); | |
| 548 | |
| 549 break; | |
| 550 | |
| 551 case 'DOMNodeRemoved': | |
| 552 this.addTransientObserver(e.target); | |
| 553 // Fall through. | |
| 554 case 'DOMNodeInserted': | |
| 555 // http://dom.spec.whatwg.org/#concept-mo-queue-childlist | |
| 556 var target = e.relatedNode; | |
| 557 var changedNode = e.target; | |
| 558 var addedNodes, removedNodes; | |
| 559 if (e.type === 'DOMNodeInserted') { | |
| 560 addedNodes = [changedNode]; | |
| 561 removedNodes = []; | |
| 562 } else { | |
| 563 | |
| 564 addedNodes = []; | |
| 565 removedNodes = [changedNode]; | |
| 566 } | |
| 567 var previousSibling = changedNode.previousSibling; | |
| 568 var nextSibling = changedNode.nextSibling; | |
| 569 | |
| 570 // 1. | |
| 571 var record = getRecord('childList', target); | |
| 572 record.addedNodes = addedNodes; | |
| 573 record.removedNodes = removedNodes; | |
| 574 record.previousSibling = previousSibling; | |
| 575 record.nextSibling = nextSibling; | |
| 576 | |
| 577 forEachAncestorAndObserverEnqueueRecord(target, function(options) { | |
| 578 // 2.1, 3.2 | |
| 579 if (!options.childList) | |
| 580 return; | |
| 581 | |
| 582 // 2.2, 3.3 | |
| 583 return record; | |
| 584 }); | |
| 585 | |
| 586 } | |
| 587 | |
| 588 clearRecords(); | |
| 589 } | |
| 590 }; | |
| 591 | |
| 592 global.JsMutationObserver = JsMutationObserver; | |
| 593 | |
| 594 // Provide unprefixed MutationObserver with native or JS implementation | |
| 595 if (!global.MutationObserver && global.WebKitMutationObserver) | |
| 596 global.MutationObserver = global.WebKitMutationObserver; | |
| 597 | |
| 598 if (!global.MutationObserver) | |
| 599 global.MutationObserver = JsMutationObserver; | |
| 600 | |
| 601 | |
| 602 })(this); | |
| 603 | |
| 604 window.CustomElements = window.CustomElements || {flags:{}}; | 59 window.CustomElements = window.CustomElements || {flags:{}}; |
| 605 (function(scope){ | 60 (function(scope){ |
| 606 | 61 |
| 607 var logFlags = window.logFlags || {}; | 62 var logFlags = window.logFlags || {}; |
| 63 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none '; | |
| 608 | 64 |
| 609 // walk the subtree rooted at node, applying 'find(element, data)' function | 65 // walk the subtree rooted at node, applying 'find(element, data)' function |
| 610 // to each element | 66 // to each element |
| 611 // if 'find' returns true for 'element', do not search element's subtree | 67 // if 'find' returns true for 'element', do not search element's subtree |
| 612 function findAll(node, find, data) { | 68 function findAll(node, find, data) { |
| 613 var e = node.firstElementChild; | 69 var e = node.firstElementChild; |
| 614 if (!e) { | 70 if (!e) { |
| 615 e = node.firstChild; | 71 e = node.firstChild; |
| 616 while (e && e.nodeType !== Node.ELEMENT_NODE) { | 72 while (e && e.nodeType !== Node.ELEMENT_NODE) { |
| 617 e = e.nextSibling; | 73 e = e.nextSibling; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 689 function insertedNode(node) { | 145 function insertedNode(node) { |
| 690 inserted(node); | 146 inserted(node); |
| 691 if (inDocument(node)) { | 147 if (inDocument(node)) { |
| 692 forSubtree(node, function(e) { | 148 forSubtree(node, function(e) { |
| 693 inserted(e); | 149 inserted(e); |
| 694 }); | 150 }); |
| 695 } | 151 } |
| 696 } | 152 } |
| 697 | 153 |
| 698 | 154 |
| 699 // TODO(sorvell): on platforms without MutationObserver, mutations may not be | 155 // TODO(sorvell): on platforms without MutationObserver, mutations may not be |
| 700 // reliable and therefore entered/leftView are not reliable. | 156 // reliable and therefore attached/detached are not reliable. |
| 701 // To make these callbacks less likely to fail, we defer all inserts and removes | 157 // To make these callbacks less likely to fail, we defer all inserts and removes |
| 702 // to give a chance for elements to be inserted into dom. | 158 // to give a chance for elements to be inserted into dom. |
| 703 // This ensures enteredViewCallback fires for elements that are created and | 159 // This ensures attachedCallback fires for elements that are created and |
| 704 // immediately added to dom. | 160 // immediately added to dom. |
| 705 var hasPolyfillMutations = (!window.MutationObserver || | 161 var hasPolyfillMutations = (!window.MutationObserver || |
| 706 (window.MutationObserver === window.JsMutationObserver)); | 162 (window.MutationObserver === window.JsMutationObserver)); |
| 707 scope.hasPolyfillMutations = hasPolyfillMutations; | 163 scope.hasPolyfillMutations = hasPolyfillMutations; |
| 708 | 164 |
| 709 var isPendingMutations = false; | 165 var isPendingMutations = false; |
| 710 var pendingMutations = []; | 166 var pendingMutations = []; |
| 711 function deferMutation(fn) { | 167 function deferMutation(fn) { |
| 712 pendingMutations.push(fn); | 168 pendingMutations.push(fn); |
| 713 if (!isPendingMutations) { | 169 if (!isPendingMutations) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 742 // TODO(sjmiles): it's possible we were inserted and removed in the space | 198 // TODO(sjmiles): it's possible we were inserted and removed in the space |
| 743 // of one microtask, in which case we won't be 'inDocument' here | 199 // of one microtask, in which case we won't be 'inDocument' here |
| 744 // But there are other cases where we are testing for inserted without | 200 // But there are other cases where we are testing for inserted without |
| 745 // specific knowledge of mutations, and must test 'inDocument' to determine | 201 // specific knowledge of mutations, and must test 'inDocument' to determine |
| 746 // whether to call inserted | 202 // whether to call inserted |
| 747 // If we can factor these cases into separate code paths we can have | 203 // If we can factor these cases into separate code paths we can have |
| 748 // better diagnostics. | 204 // better diagnostics. |
| 749 // TODO(sjmiles): when logging, do work on all custom elements so we can | 205 // TODO(sjmiles): when logging, do work on all custom elements so we can |
| 750 // track behavior even when callbacks not defined | 206 // track behavior even when callbacks not defined |
| 751 //console.log('inserted: ', element.localName); | 207 //console.log('inserted: ', element.localName); |
| 752 if (element.enteredViewCallback || (element.__upgraded__ && logFlags.dom)) { | 208 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) { |
| 753 logFlags.dom && console.group('inserted:', element.localName); | 209 logFlags.dom && console.group('inserted:', element.localName); |
| 754 if (inDocument(element)) { | 210 if (inDocument(element)) { |
| 755 element.__inserted = (element.__inserted || 0) + 1; | 211 element.__inserted = (element.__inserted || 0) + 1; |
| 756 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state | 212 // if we are in a 'removed' state, bluntly adjust to an 'inserted' state |
| 757 if (element.__inserted < 1) { | 213 if (element.__inserted < 1) { |
| 758 element.__inserted = 1; | 214 element.__inserted = 1; |
| 759 } | 215 } |
| 760 // if we are 'over inserted', squelch the callback | 216 // if we are 'over inserted', squelch the callback |
| 761 if (element.__inserted > 1) { | 217 if (element.__inserted > 1) { |
| 762 logFlags.dom && console.warn('inserted:', element.localName, | 218 logFlags.dom && console.warn('inserted:', element.localName, |
| 763 'insert/remove count:', element.__inserted) | 219 'insert/remove count:', element.__inserted) |
| 764 } else if (element.enteredViewCallback) { | 220 } else if (element.attachedCallback) { |
| 765 logFlags.dom && console.log('inserted:', element.localName); | 221 logFlags.dom && console.log('inserted:', element.localName); |
| 766 element.enteredViewCallback(); | 222 element.attachedCallback(); |
| 767 } | 223 } |
| 768 } | 224 } |
| 769 logFlags.dom && console.groupEnd(); | 225 logFlags.dom && console.groupEnd(); |
| 770 } | 226 } |
| 771 } | 227 } |
| 772 | 228 |
| 773 function removedNode(node) { | 229 function removedNode(node) { |
| 774 removed(node); | 230 removed(node); |
| 775 forSubtree(node, function(e) { | 231 forSubtree(node, function(e) { |
| 776 removed(e); | 232 removed(e); |
| 777 }); | 233 }); |
| 778 } | 234 } |
| 779 | 235 |
| 780 function removed(element) { | 236 function removed(element) { |
| 781 if (hasPolyfillMutations) { | 237 if (hasPolyfillMutations) { |
| 782 deferMutation(function() { | 238 deferMutation(function() { |
| 783 _removed(element); | 239 _removed(element); |
| 784 }); | 240 }); |
| 785 } else { | 241 } else { |
| 786 _removed(element); | 242 _removed(element); |
| 787 } | 243 } |
| 788 } | 244 } |
| 789 | 245 |
| 790 function _removed(element) { | 246 function _removed(element) { |
| 791 // TODO(sjmiles): temporary: do work on all custom elements so we can track | 247 // TODO(sjmiles): temporary: do work on all custom elements so we can track |
| 792 // behavior even when callbacks not defined | 248 // behavior even when callbacks not defined |
| 793 if (element.leftViewCallback || (element.__upgraded__ && logFlags.dom)) { | 249 if (element.attachedCallback || element.detachedCallback || (element.__upgrade d__ && logFlags.dom)) { |
| 794 logFlags.dom && console.log('removed:', element.localName); | 250 logFlags.dom && console.group('removed:', element.localName); |
| 795 if (!inDocument(element)) { | 251 if (!inDocument(element)) { |
| 796 element.__inserted = (element.__inserted || 0) - 1; | 252 element.__inserted = (element.__inserted || 0) - 1; |
| 797 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state | 253 // if we are in a 'inserted' state, bluntly adjust to an 'removed' state |
| 798 if (element.__inserted > 0) { | 254 if (element.__inserted > 0) { |
| 799 element.__inserted = 0; | 255 element.__inserted = 0; |
| 800 } | 256 } |
| 801 // if we are 'over removed', squelch the callback | 257 // if we are 'over removed', squelch the callback |
| 802 if (element.__inserted < 0) { | 258 if (element.__inserted < 0) { |
| 803 logFlags.dom && console.warn('removed:', element.localName, | 259 logFlags.dom && console.warn('removed:', element.localName, |
| 804 'insert/remove count:', element.__inserted) | 260 'insert/remove count:', element.__inserted) |
| 805 } else if (element.leftViewCallback) { | 261 } else if (element.detachedCallback) { |
| 806 element.leftViewCallback(); | 262 element.detachedCallback(); |
| 807 } | 263 } |
| 808 } | 264 } |
| 265 logFlags.dom && console.groupEnd(); | |
| 809 } | 266 } |
| 810 } | 267 } |
| 811 | 268 |
| 269 // SD polyfill intrustion due mainly to the fact that 'document' | |
| 270 // is not entirely wrapped | |
| 271 function wrapIfNeeded(node) { | |
| 272 return window.ShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) | |
| 273 : node; | |
| 274 } | |
| 275 | |
| 812 function inDocument(element) { | 276 function inDocument(element) { |
| 813 var p = element; | 277 var p = element; |
| 814 var doc = window.ShadowDOMPolyfill && | 278 var doc = wrapIfNeeded(document); |
| 815 window.ShadowDOMPolyfill.wrapIfNeeded(document) || document; | |
| 816 while (p) { | 279 while (p) { |
| 817 if (p == doc) { | 280 if (p == doc) { |
| 818 return true; | 281 return true; |
| 819 } | 282 } |
| 820 p = p.parentNode || p.host; | 283 p = p.parentNode || p.host; |
| 821 } | 284 } |
| 822 } | 285 } |
| 823 | 286 |
| 824 function watchShadow(node) { | 287 function watchShadow(node) { |
| 825 if (node.shadowRoot && !node.shadowRoot.__watched) { | 288 if (node.shadowRoot && !node.shadowRoot.__watched) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 889 handler(observer.takeRecords()); | 352 handler(observer.takeRecords()); |
| 890 takeMutations(); | 353 takeMutations(); |
| 891 } | 354 } |
| 892 | 355 |
| 893 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | 356 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
| 894 | 357 |
| 895 function observe(inRoot) { | 358 function observe(inRoot) { |
| 896 observer.observe(inRoot, {childList: true, subtree: true}); | 359 observer.observe(inRoot, {childList: true, subtree: true}); |
| 897 } | 360 } |
| 898 | 361 |
| 899 function observeDocument(document) { | 362 function observeDocument(doc) { |
| 900 observe(document); | 363 observe(doc); |
| 901 } | 364 } |
| 902 | 365 |
| 903 function upgradeDocument(document) { | 366 function upgradeDocument(doc) { |
| 904 logFlags.dom && console.group('upgradeDocument: ', (document.URL || document._ URL || '').split('/').pop()); | 367 logFlags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').po p()); |
| 905 addedNode(document); | 368 addedNode(doc); |
| 906 logFlags.dom && console.groupEnd(); | 369 logFlags.dom && console.groupEnd(); |
| 907 } | 370 } |
| 908 | 371 |
| 372 function upgradeDocumentTree(doc) { | |
| 373 doc = wrapIfNeeded(doc); | |
| 374 upgradeDocument(doc); | |
| 375 //console.log('upgradeDocumentTree: ', (doc.baseURI).split('/').pop()); | |
| 376 // upgrade contained imported documents | |
| 377 var imports = doc.querySelectorAll('link[rel=' + IMPORT_LINK_TYPE + ']'); | |
| 378 for (var i=0, l=imports.length, n; (i<l) && (n=imports[i]); i++) { | |
| 379 if (n.import && n.import.__parsed) { | |
| 380 upgradeDocumentTree(n.import); | |
| 381 } | |
| 382 } | |
| 383 } | |
| 384 | |
| 909 // exports | 385 // exports |
| 910 | 386 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
| 911 scope.watchShadow = watchShadow; | 387 scope.watchShadow = watchShadow; |
| 388 scope.upgradeDocumentTree = upgradeDocumentTree; | |
| 912 scope.upgradeAll = addedNode; | 389 scope.upgradeAll = addedNode; |
| 913 scope.upgradeSubtree = addedSubtree; | 390 scope.upgradeSubtree = addedSubtree; |
| 914 | 391 |
| 915 scope.observeDocument = observeDocument; | 392 scope.observeDocument = observeDocument; |
| 916 scope.upgradeDocument = upgradeDocument; | 393 scope.upgradeDocument = upgradeDocument; |
| 917 | 394 |
| 918 scope.takeRecords = takeRecords; | 395 scope.takeRecords = takeRecords; |
| 919 | 396 |
| 920 })(window.CustomElements); | 397 })(window.CustomElements); |
| 921 | 398 |
| 922 /** | 399 /** |
| 923 * Implements `document.register` | 400 * Implements `document.register` |
| 924 * @module CustomElements | 401 * @module CustomElements |
| 925 */ | 402 */ |
| 926 | 403 |
| 927 /** | 404 /** |
| 928 * Polyfilled extensions to the `document` object. | 405 * Polyfilled extensions to the `document` object. |
| 929 * @class Document | 406 * @class Document |
| 930 */ | 407 */ |
| 931 | 408 |
| 932 (function(scope) { | 409 (function(scope) { |
| 933 | 410 |
| 934 // imports | 411 // imports |
| 935 | 412 |
| 936 if (!scope) { | 413 if (!scope) { |
| 937 scope = window.CustomElements = {flags:{}}; | 414 scope = window.CustomElements = {flags:{}}; |
| 938 } | 415 } |
| 939 var flags = scope.flags; | 416 var flags = scope.flags; |
| 940 | 417 |
| 941 // native document.register? | 418 // native document.registerElement? |
| 942 | 419 |
| 943 var hasNative = Boolean(document.register); | 420 var hasNative = Boolean(document.registerElement); |
| 944 var useNative = !flags.register && hasNative; | 421 // TODO(sorvell): See https://github.com/Polymer/polymer/issues/399 |
| 422 // we'll address this by defaulting to CE polyfill in the presence of the SD | |
| 423 // polyfill. This will avoid spamming excess attached/detached callbacks. | |
| 424 // If there is a compelling need to run CE native with SD polyfill, | |
| 425 // we'll need to fix this issue. | |
| 426 var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill; | |
| 945 | 427 |
| 946 if (useNative) { | 428 if (useNative) { |
| 947 | 429 |
| 948 // stub | 430 // stub |
| 949 var nop = function() {}; | 431 var nop = function() {}; |
| 950 | 432 |
| 951 // exports | 433 // exports |
| 952 scope.registry = {}; | 434 scope.registry = {}; |
| 953 scope.upgradeElement = nop; | 435 scope.upgradeElement = nop; |
| 954 | 436 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 981 * Remember that the input prototype must chain to the extended element's | 463 * Remember that the input prototype must chain to the extended element's |
| 982 * prototype (or HTMLElement.prototype) regardless of the value of | 464 * prototype (or HTMLElement.prototype) regardless of the value of |
| 983 * `extends`. | 465 * `extends`. |
| 984 * @param {Object} options.prototype The prototype to use for the new | 466 * @param {Object} options.prototype The prototype to use for the new |
| 985 * element. The prototype must inherit from HTMLElement. | 467 * element. The prototype must inherit from HTMLElement. |
| 986 * @param {Object} [options.lifecycle] | 468 * @param {Object} [options.lifecycle] |
| 987 * Callbacks that fire at important phases in the life of the custom | 469 * Callbacks that fire at important phases in the life of the custom |
| 988 * element. | 470 * element. |
| 989 * | 471 * |
| 990 * @example | 472 * @example |
| 991 * FancyButton = document.register("fancy-button", { | 473 * FancyButton = document.registerElement("fancy-button", { |
| 992 * extends: 'button', | 474 * extends: 'button', |
| 993 * prototype: Object.create(HTMLButtonElement.prototype, { | 475 * prototype: Object.create(HTMLButtonElement.prototype, { |
| 994 * readyCallback: { | 476 * readyCallback: { |
| 995 * value: function() { | 477 * value: function() { |
| 996 * console.log("a fancy-button was created", | 478 * console.log("a fancy-button was created", |
| 997 * } | 479 * } |
| 998 * } | 480 * } |
| 999 * }) | 481 * }) |
| 1000 * }); | 482 * }); |
| 1001 * @return {Function} Constructor for the newly registered type. | 483 * @return {Function} Constructor for the newly registered type. |
| 1002 */ | 484 */ |
| 1003 function register(name, options) { | 485 function register(name, options) { |
| 1004 //console.warn('document.register("' + name + '", ', options, ')'); | 486 //console.warn('document.registerElement("' + name + '", ', options, ')'); |
| 1005 // construct a defintion out of options | 487 // construct a defintion out of options |
| 1006 // TODO(sjmiles): probably should clone options instead of mutating it | 488 // TODO(sjmiles): probably should clone options instead of mutating it |
| 1007 var definition = options || {}; | 489 var definition = options || {}; |
| 1008 if (!name) { | 490 if (!name) { |
| 1009 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 491 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1010 // offer guidance) | 492 // offer guidance) |
| 1011 throw new Error('document.register: first argument `name` must not be empt y'); | 493 throw new Error('document.registerElement: first argument `name` must not be empty'); |
| 1012 } | 494 } |
| 1013 if (name.indexOf('-') < 0) { | 495 if (name.indexOf('-') < 0) { |
| 1014 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 496 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1015 // offer guidance) | 497 // offer guidance) |
| 1016 throw new Error('document.register: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); | 498 throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); |
| 1017 } | 499 } |
| 1018 // elements may only be registered once | 500 // elements may only be registered once |
| 1019 if (getRegisteredDefinition(name)) { | 501 if (getRegisteredDefinition(name)) { |
| 1020 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n ame) + '\' is already registered'); | 502 throw new Error('DuplicateDefinitionError: a type with name \'' + String(n ame) + '\' is already registered'); |
| 1021 } | 503 } |
| 1022 // must have a prototype, default to an extension of HTMLElement | 504 // must have a prototype, default to an extension of HTMLElement |
| 1023 // TODO(sjmiles): probably should throw if no prototype, check spec | 505 // TODO(sjmiles): probably should throw if no prototype, check spec |
| 1024 if (!definition.prototype) { | 506 if (!definition.prototype) { |
| 1025 // TODO(sjmiles): replace with more appropriate error (EricB can probably | 507 // TODO(sjmiles): replace with more appropriate error (EricB can probably |
| 1026 // offer guidance) | 508 // offer guidance) |
| 1027 throw new Error('Options missing required prototype property'); | 509 throw new Error('Options missing required prototype property'); |
| 1028 } | 510 } |
| 1029 // record name | 511 // record name |
| 1030 definition.name = name.toLowerCase(); | 512 definition.__name = name.toLowerCase(); |
| 1031 // ensure a lifecycle object so we don't have to null test it | 513 // ensure a lifecycle object so we don't have to null test it |
| 1032 definition.lifecycle = definition.lifecycle || {}; | 514 definition.lifecycle = definition.lifecycle || {}; |
| 1033 // build a list of ancestral custom elements (for native base detection) | 515 // build a list of ancestral custom elements (for native base detection) |
| 1034 // TODO(sjmiles): we used to need to store this, but current code only | 516 // TODO(sjmiles): we used to need to store this, but current code only |
| 1035 // uses it in 'resolveTagName': it should probably be inlined | 517 // uses it in 'resolveTagName': it should probably be inlined |
| 1036 definition.ancestry = ancestry(definition.extends); | 518 definition.ancestry = ancestry(definition.extends); |
| 1037 // extensions of native specializations of HTMLElement require localName | 519 // extensions of native specializations of HTMLElement require localName |
| 1038 // to remain native, and use secondary 'is' specifier for extension type | 520 // to remain native, and use secondary 'is' specifier for extension type |
| 1039 resolveTagName(definition); | 521 resolveTagName(definition); |
| 1040 // some platforms require modifications to the user-supplied prototype | 522 // some platforms require modifications to the user-supplied prototype |
| 1041 // chain | 523 // chain |
| 1042 resolvePrototypeChain(definition); | 524 resolvePrototypeChain(definition); |
| 1043 // overrides to implement attributeChanged callback | 525 // overrides to implement attributeChanged callback |
| 1044 overrideAttributeApi(definition.prototype); | 526 overrideAttributeApi(definition.prototype); |
| 1045 // 7.1.5: Register the DEFINITION with DOCUMENT | 527 // 7.1.5: Register the DEFINITION with DOCUMENT |
| 1046 registerDefinition(definition.name, definition); | 528 registerDefinition(definition.__name, definition); |
| 1047 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE | 529 // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE |
| 1048 // 7.1.8. Return the output of the previous step. | 530 // 7.1.8. Return the output of the previous step. |
| 1049 definition.ctor = generateConstructor(definition); | 531 definition.ctor = generateConstructor(definition); |
| 1050 definition.ctor.prototype = definition.prototype; | 532 definition.ctor.prototype = definition.prototype; |
| 1051 // force our .constructor to be our actual constructor | 533 // force our .constructor to be our actual constructor |
| 1052 definition.prototype.constructor = definition.ctor; | 534 definition.prototype.constructor = definition.ctor; |
| 1053 // if initial parsing is complete | 535 // if initial parsing is complete |
| 1054 if (scope.ready || scope.performedInitialDocumentUpgrade) { | 536 if (scope.ready || scope.performedInitialDocumentUpgrade) { |
| 1055 // upgrade any pre-existing nodes of this type | 537 // upgrade any pre-existing nodes of this type |
| 1056 scope.upgradeAll(document); | 538 scope.upgradeDocumentTree(document); |
| 1057 } | 539 } |
| 1058 return definition.ctor; | 540 return definition.ctor; |
| 1059 } | 541 } |
| 1060 | 542 |
| 1061 function ancestry(extnds) { | 543 function ancestry(extnds) { |
| 1062 var extendee = getRegisteredDefinition(extnds); | 544 var extendee = getRegisteredDefinition(extnds); |
| 1063 if (extendee) { | 545 if (extendee) { |
| 1064 return ancestry(extendee.extends).concat([extendee]); | 546 return ancestry(extendee.extends).concat([extendee]); |
| 1065 } | 547 } |
| 1066 return []; | 548 return []; |
| 1067 } | 549 } |
| 1068 | 550 |
| 1069 function resolveTagName(definition) { | 551 function resolveTagName(definition) { |
| 1070 // if we are explicitly extending something, that thing is our | 552 // if we are explicitly extending something, that thing is our |
| 1071 // baseTag, unless it represents a custom component | 553 // baseTag, unless it represents a custom component |
| 1072 var baseTag = definition.extends; | 554 var baseTag = definition.extends; |
| 1073 // if our ancestry includes custom components, we only have a | 555 // if our ancestry includes custom components, we only have a |
| 1074 // baseTag if one of them does | 556 // baseTag if one of them does |
| 1075 for (var i=0, a; (a=definition.ancestry[i]); i++) { | 557 for (var i=0, a; (a=definition.ancestry[i]); i++) { |
| 1076 baseTag = a.is && a.tag; | 558 baseTag = a.is && a.tag; |
| 1077 } | 559 } |
| 1078 // our tag is our baseTag, if it exists, and otherwise just our name | 560 // our tag is our baseTag, if it exists, and otherwise just our name |
| 1079 definition.tag = baseTag || definition.name; | 561 definition.tag = baseTag || definition.__name; |
| 1080 if (baseTag) { | 562 if (baseTag) { |
| 1081 // if there is a base tag, use secondary 'is' specifier | 563 // if there is a base tag, use secondary 'is' specifier |
| 1082 definition.is = definition.name; | 564 definition.is = definition.__name; |
| 1083 } | 565 } |
| 1084 } | 566 } |
| 1085 | 567 |
| 1086 function resolvePrototypeChain(definition) { | 568 function resolvePrototypeChain(definition) { |
| 1087 // if we don't support __proto__ we need to locate the native level | 569 // if we don't support __proto__ we need to locate the native level |
| 1088 // prototype for precise mixing in | 570 // prototype for precise mixing in |
| 1089 if (!Object.__proto__) { | 571 if (!Object.__proto__) { |
| 1090 // default prototype | 572 // default prototype |
| 1091 var nativePrototype = HTMLElement.prototype; | 573 var nativePrototype = HTMLElement.prototype; |
| 1092 // work out prototype when using type-extension | 574 // work out prototype when using type-extension |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1298 // capture native createElement before we override it | 780 // capture native createElement before we override it |
| 1299 | 781 |
| 1300 var domCreateElement = document.createElement.bind(document); | 782 var domCreateElement = document.createElement.bind(document); |
| 1301 | 783 |
| 1302 // capture native cloneNode before we override it | 784 // capture native cloneNode before we override it |
| 1303 | 785 |
| 1304 var domCloneNode = Node.prototype.cloneNode; | 786 var domCloneNode = Node.prototype.cloneNode; |
| 1305 | 787 |
| 1306 // exports | 788 // exports |
| 1307 | 789 |
| 1308 document.register = register; | 790 document.registerElement = register; |
| 1309 document.createElement = createElement; // override | 791 document.createElement = createElement; // override |
| 1310 Node.prototype.cloneNode = cloneNode; // override | 792 Node.prototype.cloneNode = cloneNode; // override |
| 1311 | 793 |
| 1312 scope.registry = registry; | 794 scope.registry = registry; |
| 1313 | 795 |
| 1314 /** | 796 /** |
| 1315 * Upgrade an element to a custom element. Upgrading an element | 797 * Upgrade an element to a custom element. Upgrading an element |
| 1316 * causes the custom prototype to be applied, an `is` attribute | 798 * causes the custom prototype to be applied, an `is` attribute |
| 1317 * to be attached (as needed), and invocation of the `readyCallback`. | 799 * to be attached (as needed), and invocation of the `readyCallback`. |
| 1318 * `upgrade` does nothing if the element is already upgraded, or | 800 * `upgrade` does nothing if the element is already upgraded, or |
| 1319 * if it matches no registered custom tag name. | 801 * if it matches no registered custom tag name. |
| 1320 * | 802 * |
| 1321 * @method ugprade | 803 * @method ugprade |
| 1322 * @param {Element} element The element to upgrade. | 804 * @param {Element} element The element to upgrade. |
| 1323 * @return {Element} The upgraded element. | 805 * @return {Element} The upgraded element. |
| 1324 */ | 806 */ |
| 1325 scope.upgrade = upgradeElement; | 807 scope.upgrade = upgradeElement; |
| 1326 } | 808 } |
| 1327 | 809 |
| 810 // bc | |
| 811 document.register = document.registerElement; | |
| 812 | |
| 1328 scope.hasNative = hasNative; | 813 scope.hasNative = hasNative; |
| 1329 scope.useNative = useNative; | 814 scope.useNative = useNative; |
| 1330 | 815 |
| 1331 })(window.CustomElements); | 816 })(window.CustomElements); |
| 1332 | 817 |
| 1333 (function() { | 818 (function(scope) { |
| 1334 | 819 |
| 1335 // import | 820 // import |
| 1336 | 821 |
| 1337 var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : 'none '; | 822 var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
| 1338 | 823 |
| 1339 // highlander object for parsing a document tree | 824 // highlander object for parsing a document tree |
| 1340 | 825 |
| 1341 var parser = { | 826 var parser = { |
| 1342 selectors: [ | 827 selectors: [ |
| 1343 'link[rel=' + IMPORT_LINK_TYPE + ']' | 828 'link[rel=' + IMPORT_LINK_TYPE + ']' |
| 1344 ], | 829 ], |
| 1345 map: { | 830 map: { |
| 1346 link: 'parseLink' | 831 link: 'parseLink' |
| 1347 }, | 832 }, |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 1362 CustomElements.observeDocument(inDocument); | 847 CustomElements.observeDocument(inDocument); |
| 1363 } | 848 } |
| 1364 }, | 849 }, |
| 1365 parseLink: function(linkElt) { | 850 parseLink: function(linkElt) { |
| 1366 // imports | 851 // imports |
| 1367 if (isDocumentLink(linkElt)) { | 852 if (isDocumentLink(linkElt)) { |
| 1368 this.parseImport(linkElt); | 853 this.parseImport(linkElt); |
| 1369 } | 854 } |
| 1370 }, | 855 }, |
| 1371 parseImport: function(linkElt) { | 856 parseImport: function(linkElt) { |
| 1372 if (linkElt.content) { | 857 if (linkElt.import) { |
| 1373 parser.parse(linkElt.content); | 858 parser.parse(linkElt.import); |
| 1374 } | 859 } |
| 1375 } | 860 } |
| 1376 }; | 861 }; |
| 1377 | 862 |
| 1378 function isDocumentLink(inElt) { | 863 function isDocumentLink(inElt) { |
| 1379 return (inElt.localName === 'link' | 864 return (inElt.localName === 'link' |
| 1380 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); | 865 && inElt.getAttribute('rel') === IMPORT_LINK_TYPE); |
| 1381 } | 866 } |
| 1382 | 867 |
| 1383 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | 868 var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); |
| 1384 | 869 |
| 1385 // exports | 870 // exports |
| 1386 | 871 |
| 1387 CustomElements.parser = parser; | 872 scope.parser = parser; |
| 873 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | |
| 1388 | 874 |
| 1389 })(); | 875 })(window.CustomElements); |
| 1390 (function(scope){ | 876 (function(scope){ |
| 1391 | 877 |
| 1392 // bootstrap parsing | 878 // bootstrap parsing |
| 1393 function bootstrap() { | 879 function bootstrap() { |
| 1394 // parse document | 880 // parse document |
| 1395 CustomElements.parser.parse(document); | 881 CustomElements.parser.parse(document); |
| 1396 // one more pass before register is 'live' | 882 // one more pass before register is 'live' |
| 1397 CustomElements.upgradeDocument(document); | 883 CustomElements.upgradeDocument(document); |
| 1398 CustomElements.performedInitialDocumentUpgrade = true; | 884 CustomElements.performedInitialDocumentUpgrade = true; |
| 1399 // choose async | 885 // choose async |
| 1400 var async = window.Platform && Platform.endOfMicrotask ? | 886 var async = window.Platform && Platform.endOfMicrotask ? |
| 1401 Platform.endOfMicrotask : | 887 Platform.endOfMicrotask : |
| 1402 setTimeout; | 888 setTimeout; |
| 1403 async(function() { | 889 async(function() { |
| 1404 // set internal 'ready' flag, now document.register will trigger | 890 // set internal 'ready' flag, now document.registerElement will trigger |
| 1405 // synchronous upgrades | 891 // synchronous upgrades |
| 1406 CustomElements.ready = true; | 892 CustomElements.ready = true; |
| 1407 // capture blunt profiling data | 893 // capture blunt profiling data |
| 1408 CustomElements.readyTime = Date.now(); | 894 CustomElements.readyTime = Date.now(); |
| 1409 if (window.HTMLImports) { | 895 if (window.HTMLImports) { |
| 1410 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; | 896 CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime; |
| 1411 } | 897 } |
| 1412 // notify the system that we are bootstrapped | 898 // notify the system that we are bootstrapped |
| 1413 document.body.dispatchEvent( | 899 document.dispatchEvent( |
| 1414 new CustomEvent('WebComponentsReady', {bubbles: true}) | 900 new CustomEvent('WebComponentsReady', {bubbles: true}) |
| 1415 ); | 901 ); |
| 1416 }); | 902 }); |
| 1417 } | 903 } |
| 1418 | 904 |
| 1419 // CustomEvent shim for IE | 905 // CustomEvent shim for IE |
| 1420 if (typeof window.CustomEvent !== 'function') { | 906 if (typeof window.CustomEvent !== 'function') { |
| 1421 window.CustomEvent = function(inType) { | 907 window.CustomEvent = function(inType) { |
| 1422 var e = document.createEvent('HTMLEvents'); | 908 var e = document.createEvent('HTMLEvents'); |
| 1423 e.initEvent(inType, true, true); | 909 e.initEvent(inType, true, true); |
| 1424 return e; | 910 return e; |
| 1425 }; | 911 }; |
| 1426 } | 912 } |
| 1427 | 913 |
| 1428 // When loading at readyState complete time (or via flag), boot custom elements | 914 // When loading at readyState complete time (or via flag), boot custom elements |
| 1429 // immediately. | 915 // immediately. |
| 1430 // If relevant, HTMLImports must already be loaded. | 916 // If relevant, HTMLImports must already be loaded. |
| 1431 if (document.readyState === 'complete' || scope.flags.eager) { | 917 if (document.readyState === 'complete' || scope.flags.eager) { |
| 1432 bootstrap(); | 918 bootstrap(); |
| 1433 // When loading at readyState interactive time, bootstrap only if HTMLImports | 919 // When loading at readyState interactive time, bootstrap only if HTMLImports |
| 1434 // are not pending. Also avoid IE as the semantics of this state are unreliable. | 920 // are not pending. Also avoid IE as the semantics of this state are unreliable. |
| 1435 } else if (document.readyState === 'interactive' && !window.attachEvent && | 921 } else if (document.readyState === 'interactive' && !window.attachEvent && |
| 1436 (!window.HTMLImports || window.HTMLImports.ready)) { | 922 (!window.HTMLImports || window.HTMLImports.ready)) { |
| 1437 bootstrap(); | 923 bootstrap(); |
| 1438 // When loading at other readyStates, wait for the appropriate DOM event to | 924 // When loading at other readyStates, wait for the appropriate DOM event to |
| 1439 // bootstrap. | 925 // bootstrap. |
| 1440 } else { | 926 } else { |
| 1441 var loadEvent = window.HTMLImports ? 'HTMLImportsLoaded' : | 927 var loadEvent = window.HTMLImports && !HTMLImports.ready |
| 1442 document.readyState == 'loading' ? 'DOMContentLoaded' : 'load'; | 928 ? 'HTMLImportsLoaded' |
| 929 : document.readyState == 'loading' ? 'DOMContentLoaded' : 'load'; | |
| 1443 window.addEventListener(loadEvent, bootstrap); | 930 window.addEventListener(loadEvent, bootstrap); |
| 1444 } | 931 } |
| 1445 | 932 |
| 1446 })(window.CustomElements); | 933 })(window.CustomElements); |
| 1447 | 934 |
| 1448 (function() { | 935 (function() { |
| 1449 // Patch to allow custom element and shadow dom to work together, from: | 936 // Patch to allow custom element and shadow dom to work together, from: |
| 1450 // https://github.com/Polymer/platform-dev/blob/60ece8c323c5d9325cbfdfd6e8cd180d 4f38a3bc/src/patches-shadowdom-polyfill.js | 937 // https://github.com/Polymer/platform-dev/blob/60ece8c323c5d9325cbfdfd6e8cd180d 4f38a3bc/src/patches-shadowdom-polyfill.js |
| 1451 // include .host reference | 938 // include .host reference |
| 1452 if (HTMLElement.prototype.createShadowRoot) { | 939 if (HTMLElement.prototype.createShadowRoot) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1488 if (window.CustomElements && !CustomElements.useNative) { | 975 if (window.CustomElements && !CustomElements.useNative) { |
| 1489 var originalImportNode = Document.prototype.importNode; | 976 var originalImportNode = Document.prototype.importNode; |
| 1490 Document.prototype.importNode = function(node, deep) { | 977 Document.prototype.importNode = function(node, deep) { |
| 1491 var imported = originalImportNode.call(this, node, deep); | 978 var imported = originalImportNode.call(this, node, deep); |
| 1492 CustomElements.upgradeAll(imported); | 979 CustomElements.upgradeAll(imported); |
| 1493 return imported; | 980 return imported; |
| 1494 } | 981 } |
| 1495 } | 982 } |
| 1496 | 983 |
| 1497 })(); | 984 })(); |
| OLD | NEW |