Index: pkg/web_components/lib/platform.concat.js |
diff --git a/pkg/web_components/lib/platform.concat.js b/pkg/web_components/lib/platform.concat.js |
index 449be75909a04336153634aebcadeda141e6e05c..f6ebf1541bbbcf21c42d0e8aba7b5f867c0060e9 100644 |
--- a/pkg/web_components/lib/platform.concat.js |
+++ b/pkg/web_components/lib/platform.concat.js |
@@ -46,7 +46,7 @@ window.logFlags = window.logFlags || {}; |
} |
if (flags.shadow && document.querySelectorAll('script').length > 1) { |
- console.warn('platform.js is not the first script on the page. ' + |
+ console.log('Warning: platform.js is not the first script on the page. ' + |
'See http://www.polymer-project.org/docs/start/platform.html#setup ' + |
'for details.'); |
} |
@@ -88,6 +88,7 @@ if (typeof WeakMap === 'undefined') { |
entry[1] = value; |
else |
defineProperty(key, this.name, {value: [key, value], writable: true}); |
+ return this; |
}, |
get: function(key) { |
var entry; |
@@ -4509,6 +4510,7 @@ window.ShadowDOMPolyfill = {}; |
return s; |
}, |
set textContent(textContent) { |
+ if (textContent == null) textContent = ''; |
var removedNodes = snapshotNodeList(this.childNodes); |
if (this.invalidateShadowRenderer()) { |
@@ -9513,6 +9515,30 @@ scope.ShadowCSS = ShadowCSS; |
if ('#' == hash[0]) |
hash = hash.slice(1); |
parse.call(this, hash, 'fragment'); |
+ }, |
+ |
+ get origin() { |
+ var host; |
+ if (this._isInvalid || !this._scheme) { |
+ return ''; |
+ } |
+ // javascript: Gecko returns String(""), WebKit/Blink String("null") |
+ // Gecko throws error for "data://" |
+ // data: Gecko returns "", Blink returns "data://", WebKit returns "null" |
+ // Gecko returns String("") for file: mailto: |
+ // WebKit/Blink returns String("SCHEME://") for file: mailto: |
+ switch (this._scheme) { |
+ case 'data': |
+ case 'file': |
+ case 'javascript': |
+ case 'mailto': |
+ return 'null'; |
+ } |
+ host = this.host; |
+ if (!host) { |
+ return ''; |
+ } |
+ return this._scheme + '://' + host; |
} |
}; |
@@ -10127,17 +10153,18 @@ window.HTMLImports = window.HTMLImports || {flags:{}}; |
(function(scope) { |
-var hasNative = ('import' in document.createElement('link')); |
+var IMPORT_LINK_TYPE = 'import'; |
+var hasNative = (IMPORT_LINK_TYPE in document.createElement('link')); |
var useNative = hasNative; |
- |
-isIE = /Trident/.test(navigator.userAgent); |
+var isIE = /Trident/.test(navigator.userAgent); |
// TODO(sorvell): SD polyfill intrusion |
var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill); |
var wrap = function(node) { |
return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node; |
}; |
-var mainDoc = wrap(document); |
+ |
+var rootDocument = wrap(document); |
// NOTE: We cannot polyfill document.currentScript because it's not possible |
// both to override and maintain the ability to capture the native value; |
@@ -10157,14 +10184,14 @@ var currentScriptDescriptor = { |
}; |
Object.defineProperty(document, '_currentScript', currentScriptDescriptor); |
-Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor); |
+Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor); |
// call a callback when all HTMLImports in the document at call (or at least |
// document ready) time have loaded. |
// 1. ensure the document is in a ready state (has dom), then |
// 2. watch for loading of imports and call callback when done |
-function whenImportsReady(callback, doc) { |
- doc = doc || mainDoc; |
+function whenReady(callback, doc) { |
+ doc = doc || rootDocument; |
// if document is loading, wait and try again |
whenDocumentReady(function() { |
watchImportsLoad(callback, doc); |
@@ -10204,8 +10231,8 @@ function watchImportsLoad(callback, doc) { |
var imports = doc.querySelectorAll('link[rel=import]'); |
var loaded = 0, l = imports.length; |
function checkDone(d) { |
- if (loaded == l) { |
- callback && callback(); |
+ if ((loaded == l) && callback) { |
+ callback(); |
} |
} |
function loadedImport(e) { |
@@ -10298,10 +10325,10 @@ if (useNative) { |
// have loaded. This event is required to simulate the script blocking |
// behavior of native imports. A main document script that needs to be sure |
// imports have loaded should wait for this event. |
-whenImportsReady(function() { |
+whenReady(function() { |
HTMLImports.ready = true; |
HTMLImports.readyTime = new Date().getTime(); |
- mainDoc.dispatchEvent( |
+ rootDocument.dispatchEvent( |
new CustomEvent('HTMLImportsLoaded', {bubbles: true}) |
); |
}); |
@@ -10309,12 +10336,11 @@ whenImportsReady(function() { |
// exports |
scope.useNative = useNative; |
scope.isImportLoaded = isImportLoaded; |
-scope.whenReady = whenImportsReady; |
+scope.whenReady = whenReady; |
+scope.rootDocument = rootDocument; |
+scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
scope.isIE = isIE; |
-// deprecated |
-scope.whenImportsReady = whenImportsReady; |
- |
})(window.HTMLImports); |
/* |
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
@@ -10344,6 +10370,7 @@ scope.whenImportsReady = whenImportsReady; |
}; |
Loader.prototype = { |
+ |
addNodes: function(nodes) { |
// number of transactions to complete |
this.inflight += nodes.length; |
@@ -10354,6 +10381,7 @@ scope.whenImportsReady = whenImportsReady; |
// anything to do? |
this.checkDone(); |
}, |
+ |
addNode: function(node) { |
// number of transactions to complete |
this.inflight++; |
@@ -10362,6 +10390,7 @@ scope.whenImportsReady = whenImportsReady; |
// anything to do? |
this.checkDone(); |
}, |
+ |
require: function(elt) { |
var url = elt.src || elt.href; |
// ensure we have a standard url that can be used |
@@ -10374,6 +10403,7 @@ scope.whenImportsReady = whenImportsReady; |
this.fetch(url, elt); |
} |
}, |
+ |
dedupe: function(url, elt) { |
if (this.pending[url]) { |
// add to list of nodes waiting for inUrl |
@@ -10394,6 +10424,7 @@ scope.whenImportsReady = whenImportsReady; |
// need fetch (not a dupe) |
return false; |
}, |
+ |
fetch: function(url, elt) { |
flags.load && console.log('fetch', url, elt); |
if (url.match(/^data:/)) { |
@@ -10429,6 +10460,7 @@ scope.whenImportsReady = whenImportsReady; |
*/ |
} |
}, |
+ |
receive: function(url, elt, err, resource, redirectedUrl) { |
this.cache[url] = resource; |
var $p = this.pending[url]; |
@@ -10440,24 +10472,29 @@ scope.whenImportsReady = whenImportsReady; |
} |
this.pending[url] = null; |
}, |
+ |
tail: function() { |
--this.inflight; |
this.checkDone(); |
}, |
+ |
checkDone: function() { |
if (!this.inflight) { |
this.oncomplete(); |
} |
} |
+ |
}; |
xhr = xhr || { |
async: true, |
+ |
ok: function(request) { |
return (request.status >= 200 && request.status < 300) |
|| (request.status === 304) |
|| (request.status === 0); |
}, |
+ |
load: function(url, next, nextContext) { |
var request = new XMLHttpRequest(); |
if (scope.flags.debug || scope.flags.bust) { |
@@ -10482,9 +10519,11 @@ scope.whenImportsReady = whenImportsReady; |
request.send(); |
return request; |
}, |
+ |
loadDocument: function(url, next, nextContext) { |
this.load(url, next, nextContext).responseType = 'document'; |
} |
+ |
}; |
// exports |
@@ -10503,12 +10542,11 @@ scope.whenImportsReady = whenImportsReady; |
*/ |
(function(scope) { |
-var IMPORT_LINK_TYPE = 'import'; |
+// imports |
+var rootDocument = scope.rootDocument; |
var flags = scope.flags; |
var isIE = scope.isIE; |
-// TODO(sorvell): SD polyfill intrusion |
-var mainDoc = window.ShadowDOMPolyfill ? |
- window.ShadowDOMPolyfill.wrapIfNeeded(document) : document; |
+var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
// importParser |
// highlander object to manage parsing of imports |
@@ -10519,8 +10557,10 @@ var mainDoc = window.ShadowDOMPolyfill ? |
// highlander object for parsing a document tree |
var importParser = { |
+ |
// parse selectors for main document elements |
documentSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', |
+ |
// parse selectors for import document elements |
importsSelectors: [ |
'link[rel=' + IMPORT_LINK_TYPE + ']', |
@@ -10529,11 +10569,15 @@ var importParser = { |
'script:not([type])', |
'script[type="text/javascript"]' |
].join(','), |
+ |
map: { |
link: 'parseLink', |
script: 'parseScript', |
style: 'parseStyle' |
}, |
+ |
+ dynamicElements: [], |
+ |
// try to parse the next import in the tree |
parseNext: function() { |
var next = this.nextToParse(); |
@@ -10541,6 +10585,7 @@ var importParser = { |
this.parse(next); |
} |
}, |
+ |
parse: function(elt) { |
if (this.isParsed(elt)) { |
flags.parse && console.log('[%s] is already parsed', elt.localName); |
@@ -10552,6 +10597,14 @@ var importParser = { |
fn.call(this, elt); |
} |
}, |
+ |
+ parseDynamic: function(elt, quiet) { |
+ this.dynamicElements.push(elt); |
+ if (!quiet) { |
+ this.parseNext(); |
+ } |
+ }, |
+ |
// only 1 element may be parsed at a time; parsing is async so each |
// parsing implementation must inform the system that parsing is complete |
// via markParsingComplete. |
@@ -10564,29 +10617,25 @@ var importParser = { |
flags.parse && console.log('parsing', elt); |
this.parsingElement = elt; |
}, |
+ |
markParsingComplete: function(elt) { |
elt.__importParsed = true; |
+ this.markDynamicParsingComplete(elt); |
if (elt.__importElement) { |
elt.__importElement.__importParsed = true; |
+ this.markDynamicParsingComplete(elt.__importElement); |
} |
this.parsingElement = null; |
flags.parse && console.log('completed', elt); |
}, |
- invalidateParse: function(doc) { |
- if (doc && doc.__importLink) { |
- doc.__importParsed = doc.__importLink.__importParsed = false; |
- this.parseSoon(); |
- } |
- }, |
- parseSoon: function() { |
- if (this._parseSoon) { |
- cancelAnimationFrame(this._parseDelay); |
+ |
+ markDynamicParsingComplete: function(elt) { |
+ var i = this.dynamicElements.indexOf(elt); |
+ if (i >= 0) { |
+ this.dynamicElements.splice(i, 1); |
} |
- var parser = this; |
- this._parseSoon = requestAnimationFrame(function() { |
- parser.parseNext(); |
- }); |
}, |
+ |
parseImport: function(elt) { |
// TODO(sorvell): consider if there's a better way to do this; |
// expose an imports parsing hook; this is needed, for example, by the |
@@ -10617,6 +10666,7 @@ var importParser = { |
} |
this.parseNext(); |
}, |
+ |
parseLink: function(linkElt) { |
if (nodeIsImport(linkElt)) { |
this.parseImport(linkElt); |
@@ -10626,6 +10676,7 @@ var importParser = { |
this.parseGeneric(linkElt); |
} |
}, |
+ |
parseStyle: function(elt) { |
// TODO(sorvell): style element load event can just not fire so clone styles |
var src = elt; |
@@ -10633,10 +10684,12 @@ var importParser = { |
elt.__importElement = src; |
this.parseGeneric(elt); |
}, |
+ |
parseGeneric: function(elt) { |
this.trackElement(elt); |
this.addElementToDocument(elt); |
}, |
+ |
rootImportForElement: function(elt) { |
var n = elt; |
while (n.ownerDocument.__importLink) { |
@@ -10644,6 +10697,7 @@ var importParser = { |
} |
return n; |
}, |
+ |
addElementToDocument: function(elt) { |
var port = this.rootImportForElement(elt.__importElement || elt); |
var l = port.__insertedElements = port.__insertedElements || 0; |
@@ -10653,6 +10707,7 @@ var importParser = { |
} |
port.parentNode.insertBefore(elt, refNode); |
}, |
+ |
// tracks when a loadable element has loaded |
trackElement: function(elt, callback) { |
var self = this; |
@@ -10692,6 +10747,7 @@ var importParser = { |
} |
} |
}, |
+ |
// NOTE: execute scripts by injecting them and watching for the load/error |
// event. Inline scripts are handled via dataURL's because browsers tend to |
// provide correct parsing errors in this case. If this has any compatibility |
@@ -10710,11 +10766,14 @@ var importParser = { |
}); |
this.addElementToDocument(script); |
}, |
+ |
// determine the next element in the tree which should be parsed |
nextToParse: function() { |
this._mayParse = []; |
- return !this.parsingElement && this.nextToParseInDoc(mainDoc); |
+ return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || |
+ this.nextToParseDynamic()); |
}, |
+ |
nextToParseInDoc: function(doc, link) { |
// use `marParse` list to avoid looping into the same document again |
// since it could cause an iloop. |
@@ -10734,20 +10793,33 @@ var importParser = { |
// all nodes have been parsed, ready to parse import, if any |
return link; |
}, |
+ |
+ nextToParseDynamic: function() { |
+ return this.dynamicElements[0]; |
+ }, |
+ |
// return the set of parse selectors relevant for this node. |
parseSelectorsForNode: function(node) { |
var doc = node.ownerDocument || node; |
- return doc === mainDoc ? this.documentSelectors : this.importsSelectors; |
+ return doc === rootDocument ? this.documentSelectors : |
+ this.importsSelectors; |
}, |
+ |
isParsed: function(node) { |
return node.__importParsed; |
}, |
+ |
+ needsDynamicParsing: function(elt) { |
+ return (this.dynamicElements.indexOf(elt) >= 0); |
+ }, |
+ |
hasResource: function(node) { |
if (nodeIsImport(node) && (node.import === undefined)) { |
return false; |
} |
return true; |
} |
+ |
}; |
function nodeIsImport(elt) { |
@@ -10797,17 +10869,20 @@ var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; |
var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; |
var path = { |
+ |
resolveUrlsInStyle: function(style) { |
var doc = style.ownerDocument; |
var resolver = doc.createElement('a'); |
style.textContent = this.resolveUrlsInCssText(style.textContent, resolver); |
return style; |
}, |
+ |
resolveUrlsInCssText: function(cssText, urlObj) { |
var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP); |
r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP); |
return r; |
}, |
+ |
replaceUrls: function(text, urlObj, regexp) { |
return text.replace(regexp, function(m, pre, url, post) { |
var urlPath = url.replace(/["']/g, ''); |
@@ -10816,7 +10891,8 @@ var path = { |
return pre + '\'' + urlPath + '\'' + post; |
}); |
} |
-} |
+ |
+}; |
// exports |
scope.parser = importParser; |
@@ -10834,17 +10910,15 @@ scope.path = path; |
*/ |
(function(scope) { |
+// imports |
var useNative = scope.useNative; |
var flags = scope.flags; |
-var IMPORT_LINK_TYPE = 'import'; |
- |
-// TODO(sorvell): SD polyfill intrusion |
-var mainDoc = window.ShadowDOMPolyfill ? |
- ShadowDOMPolyfill.wrapIfNeeded(document) : document; |
+var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
if (!useNative) { |
// imports |
+ var rootDocument = scope.rootDocument; |
var xhr = scope.xhr; |
var Loader = scope.Loader; |
var parser = scope.parser; |
@@ -10856,32 +10930,40 @@ if (!useNative) { |
// - loads any linked import documents (with deduping) |
var importer = { |
+ |
documents: {}, |
+ |
// nodes to load in the mian document |
documentPreloadSelectors: 'link[rel=' + IMPORT_LINK_TYPE + ']', |
+ |
// nodes to load in imports |
importsPreloadSelectors: [ |
'link[rel=' + IMPORT_LINK_TYPE + ']' |
].join(','), |
+ |
loadNode: function(node) { |
importLoader.addNode(node); |
}, |
+ |
// load all loadable elements within the parent element |
loadSubtree: function(parent) { |
var nodes = this.marshalNodes(parent); |
// add these nodes to loader's queue |
importLoader.addNodes(nodes); |
}, |
+ |
marshalNodes: function(parent) { |
// all preloadable nodes in inDocument |
return parent.querySelectorAll(this.loadSelectorsForNode(parent)); |
}, |
+ |
// find the proper set of load selectors for a given node |
loadSelectorsForNode: function(node) { |
var doc = node.ownerDocument || node; |
- return doc === mainDoc ? this.documentPreloadSelectors : |
+ return doc === rootDocument ? this.documentPreloadSelectors : |
this.importsPreloadSelectors; |
}, |
+ |
loaded: function(url, elt, resource, err, redirectedUrl) { |
flags.load && console.log('loaded', url, elt); |
// store generic resource |
@@ -10910,14 +10992,17 @@ if (!useNative) { |
} |
parser.parseNext(); |
}, |
+ |
bootDocument: function(doc) { |
this.loadSubtree(doc); |
this.observe(doc); |
parser.parseNext(); |
}, |
+ |
loadedAll: function() { |
parser.parseNext(); |
} |
+ |
}; |
// loader singleton |
@@ -10985,7 +11070,7 @@ if (!useNative) { |
}; |
Object.defineProperty(document, 'baseURI', baseURIDescriptor); |
- Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor); |
+ Object.defineProperty(rootDocument, 'baseURI', baseURIDescriptor); |
} |
// IE shim for CustomEvent |
@@ -11010,7 +11095,6 @@ scope.importer = importer; |
scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; |
scope.importLoader = importLoader; |
- |
})(window.HTMLImports); |
/* |
@@ -11023,11 +11107,14 @@ scope.importLoader = importLoader; |
*/ |
(function(scope){ |
+// imports |
var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
-var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; |
var importer = scope.importer; |
var parser = scope.parser; |
+var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; |
+ |
+ |
// we track mutations for addedNodes, looking for imports |
function handler(mutations) { |
for (var i=0, l=mutations.length, m; (i<l) && (m=mutations[i]); i++) { |
@@ -11038,25 +11125,28 @@ function handler(mutations) { |
} |
// find loadable elements and add them to the importer |
+// IFF the owning document has already parsed, then parsable elements |
+// need to be marked for dynamic parsing. |
function addedNodes(nodes) { |
- var owner; |
- for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { |
- owner = owner || n.ownerDocument; |
- if (shouldLoadNode(n)) { |
+ var owner, parsed; |
+ for (var i=0, l=nodes.length, n, loading; (i<l) && (n=nodes[i]); i++) { |
+ if (!owner) { |
+ owner = n.ownerDocument; |
+ parsed = parser.isParsed(owner); |
+ } |
+ // note: the act of loading kicks the parser, so we use parseDynamic's |
+ // 2nd argument to control if this added node needs to kick the parser. |
+ loading = shouldLoadNode(n); |
+ if (loading) { |
importer.loadNode(n); |
} |
+ if (shouldParseNode(n) && parsed) { |
+ parser.parseDynamic(n, loading); |
+ } |
if (n.children && n.children.length) { |
addedNodes(n.children); |
} |
} |
- // TODO(sorvell): This is not the right approach here. We shouldn't need to |
- // invalidate parsing when an element is added. Disabling this code |
- // until a better approach is found. |
- /* |
- if (owner) { |
- parser.invalidateParse(owner); |
- } |
- */ |
} |
function shouldLoadNode(node) { |
@@ -11064,6 +11154,11 @@ function shouldLoadNode(node) { |
importer.loadSelectorsForNode(node)); |
} |
+function shouldParseNode(node) { |
+ return (node.nodeType === 1) && matches.call(node, |
+ parser.parseSelectorsForNode(node)); |
+} |
+ |
// x-plat matches |
var matches = HTMLElement.prototype.matches || |
HTMLElement.prototype.matchesSelector || |