Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
refactor such that only base.js is necessary for native HTMLImports.
Browse files Browse the repository at this point in the history
  • Loading branch information
sorvell committed Aug 1, 2014
1 parent fe9a927 commit 57616cf
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 166 deletions.
1 change: 1 addition & 0 deletions html-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var scopeName = 'HTMLImports';
var modules = [
'../MutationObservers/mutation-observers.js',
'src/scope.js',
'src/base.js',
'src/Loader.js',
'src/Parser.js',
'src/HTMLImports.js',
Expand Down
166 changes: 25 additions & 141 deletions src/HTMLImports.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

(function(scope) {

var hasNative = ('import' in document.createElement('link'));
var useNative = hasNative;
var useNative = scope.useNative;
var flags = scope.flags;
var IMPORT_LINK_TYPE = 'import';

Expand Down Expand Up @@ -143,156 +142,41 @@ if (!useNative) {
}
return doc;
}
} else {
// do nothing if using native imports
var importer = {};
}

// NOTE: We cannot polyfill document.currentScript because it's not possible
// both to override and maintain the ability to capture the native value;
// therefore we choose to expose _currentScript both when native imports
// and the polyfill are in use.
var currentScriptDescriptor = {
get: function() {
return HTMLImports.currentScript || document.currentScript;
},
configurable: true
};

Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor);

// Polyfill document.baseURI for browsers without it.
if (!document.baseURI) {
var baseURIDescriptor = {
get: function() {
return window.location.href;
},
configurable: true
};

Object.defineProperty(document, 'baseURI', baseURIDescriptor);
Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor);
}

// 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;
// if document is loading, wait and try again
whenDocumentReady(function() {
watchImportsLoad(callback, doc);
}, doc);
}

// call the callback when the document is in a ready state (has dom)
var requiredReadyState = HTMLImports.isIE ? 'complete' : 'interactive';
var READY_EVENT = 'readystatechange';
function isDocumentReady(doc) {
return (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState);
}

// call <callback> when we ensure the document is in a ready state
function whenDocumentReady(callback, doc) {
if (!isDocumentReady(doc)) {
var checkReady = function() {
if (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState) {
doc.removeEventListener(READY_EVENT, checkReady);
whenDocumentReady(callback, doc);
}
}
doc.addEventListener(READY_EVENT, checkReady);
} else if (callback) {
callback();
// Polyfill document.baseURI for browsers without it.
if (!document.baseURI) {
var baseURIDescriptor = {
get: function() {
return window.location.href;
},
configurable: true
};

Object.defineProperty(document, 'baseURI', baseURIDescriptor);
Object.defineProperty(mainDoc, 'baseURI', baseURIDescriptor);
}
}

// call <callback> when we ensure all imports have loaded
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();
}
}
function loadedImport(e) {
loaded++;
checkDone();
}
if (l) {
for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
if (isImportLoaded(imp)) {
loadedImport.call(imp);
} else {
imp.addEventListener('load', loadedImport);
imp.addEventListener('error', loadedImport);
}
}
} else {
checkDone();
}
}

function isImportLoaded(link) {
return useNative ? (link.import && (link.import.readyState !== 'loading')) || link.__loaded :
link.__importParsed;
}

// TODO(sorvell): install a mutation observer to see if HTMLImports have loaded
// this is a workaround for https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007
// and should be removed when this bug is addressed.
if (useNative) {
new MutationObserver(function(mxns) {
for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
if (m.addedNodes) {
handleImports(m.addedNodes);
}
}
}).observe(document.head, {childList: true});

function handleImports(nodes) {
for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
if (isImport(n)) {
handleImport(n);
}
}
}

function isImport(element) {
return element.localName === 'link' && element.rel === 'import';
}

function handleImport(element) {
var loaded = element.import;
if (loaded) {
markTargetLoaded({target: element});
} else {
element.addEventListener('load', markTargetLoaded);
element.addEventListener('error', markTargetLoaded);
}
}

function markTargetLoaded(event) {
event.target.__loaded = true;
// IE shim for CustomEvent
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function(inType, dictionary) {
var e = document.createEvent('HTMLEvents');
e.initEvent(inType,
dictionary.bubbles === false ? false : true,
dictionary.cancelable === false ? false : true,
dictionary.detail);
return e;
};
}

} else {
// do nothing if using native imports
var importer = {};
}

// exports
scope.hasNative = hasNative;
scope.useNative = useNative;
scope.importer = importer;
scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
scope.isImportLoaded = isImportLoaded;
scope.importLoader = importLoader;
scope.whenReady = whenImportsReady;

// deprecated
scope.whenImportsReady = whenImportsReady;

})(window.HTMLImports);
158 changes: 158 additions & 0 deletions src/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/

(function(scope) {

var hasNative = ('import' in document.createElement('link'));
var useNative = hasNative;

// TODO(sorvell): SD polyfill intrusion
var mainDoc = window.ShadowDOMPolyfill ?
ShadowDOMPolyfill.wrapIfNeeded(document) : document;

// NOTE: We cannot polyfill document.currentScript because it's not possible
// both to override and maintain the ability to capture the native value;
// therefore we choose to expose _currentScript both when native imports
// and the polyfill are in use.
var currentScriptDescriptor = {
get: function() {
return HTMLImports.currentScript || document.currentScript;
},
configurable: true
};

Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
Object.defineProperty(mainDoc, '_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;
// if document is loading, wait and try again
whenDocumentReady(function() {
watchImportsLoad(callback, doc);
}, doc);
}

// call the callback when the document is in a ready state (has dom)
var requiredReadyState = HTMLImports.isIE ? 'complete' : 'interactive';
var READY_EVENT = 'readystatechange';
function isDocumentReady(doc) {
return (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState);
}

// call <callback> when we ensure the document is in a ready state
function whenDocumentReady(callback, doc) {
if (!isDocumentReady(doc)) {
var checkReady = function() {
if (doc.readyState === 'complete' ||
doc.readyState === requiredReadyState) {
doc.removeEventListener(READY_EVENT, checkReady);
whenDocumentReady(callback, doc);
}
}
doc.addEventListener(READY_EVENT, checkReady);
} else if (callback) {
callback();
}
}

// call <callback> when we ensure all imports have loaded
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();
}
}
function loadedImport(e) {
loaded++;
checkDone();
}
if (l) {
for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
if (isImportLoaded(imp)) {
loadedImport.call(imp);
} else {
imp.addEventListener('load', loadedImport);
imp.addEventListener('error', loadedImport);
}
}
} else {
checkDone();
}
}

function isImportLoaded(link) {
return useNative ? (link.import && (link.import.readyState !== 'loading')) || link.__loaded :
link.__importParsed;
}

// TODO(sorvell): install a mutation observer to see if HTMLImports have loaded
// this is a workaround for https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007
// and should be removed when this bug is addressed.
if (useNative) {
new MutationObserver(function(mxns) {
for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
if (m.addedNodes) {
handleImports(m.addedNodes);
}
}
}).observe(document.head, {childList: true});

function handleImports(nodes) {
for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
if (isImport(n)) {
handleImport(n);
}
}
}

function isImport(element) {
return element.localName === 'link' && element.rel === 'import';
}

function handleImport(element) {
var loaded = element.import;
if (loaded) {
markTargetLoaded({target: element});
} else {
element.addEventListener('load', markTargetLoaded);
element.addEventListener('error', markTargetLoaded);
}
}

function markTargetLoaded(event) {
event.target.__loaded = true;
}

}

// Fire the 'HTMLImportsLoaded' event when imports in document at load time
// 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() {
HTMLImports.ready = true;
HTMLImports.readyTime = new Date().getTime();
mainDoc.dispatchEvent(
new CustomEvent('HTMLImportsLoaded', {bubbles: true})
);
});

// exports
scope.useNative = useNative;
scope.isImportLoaded = isImportLoaded;
scope.whenReady = whenImportsReady;

// deprecated
scope.whenImportsReady = whenImportsReady;

})(window.HTMLImports);
25 changes: 0 additions & 25 deletions src/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,10 @@

// bootstrap

// IE shim for CustomEvent
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function(inType, dictionary) {
var e = document.createEvent('HTMLEvents');
e.initEvent(inType,
dictionary.bubbles === false ? false : true,
dictionary.cancelable === false ? false : true,
dictionary.detail);
return e;
};
}

// TODO(sorvell): SD polyfill intrusion
var doc = window.ShadowDOMPolyfill ?
window.ShadowDOMPolyfill.wrapIfNeeded(document) : document;

// Fire the 'HTMLImportsLoaded' event when imports in document at load time
// 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.
HTMLImports.whenImportsReady(function() {
HTMLImports.ready = true;
HTMLImports.readyTime = new Date().getTime();
doc.dispatchEvent(
new CustomEvent('HTMLImportsLoaded', {bubbles: true})
);
});


// no need to bootstrap the polyfill when native imports is available.
if (!HTMLImports.useNative) {
function bootstrap() {
Expand Down

0 comments on commit 57616cf

Please sign in to comment.