process related docs added
function _onNodesAdded(nodes) {
var i;
for (i = 0; i < nodes.length; i++) {
//check for Javascript files
if (Utils.isExternalScript(nodes[i])) {
_transport.send(JSON.stringify({
method: 'ScriptAdded',
src: nodes[i].src
}));
}
//check for stylesheets
if (Utils.isExternalStylesheet(nodes[i])) {
CSS.checkForStylesheetLoaded(nodes[i].href);
}
}
}
process related docs removed
function _onNodesRemoved(nodes) {
var i;
//iterate on removed nodes
for (i = 0; i < nodes.length; i++) {
// check for external JS files
if (Utils.isExternalScript(nodes[i])) {
_transport.send(JSON.stringify({
method: 'ScriptRemoved',
src: nodes[i].src
}));
}
//check for external StyleSheets
if (Utils.isExternalStylesheet(nodes[i])) {
CSS.notifyStylesheetRemoved(nodes[i].href);
}
}
}
function _enableListeners() {
// enable MutationOberver if it's supported
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
if (MutationObserver) {
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length > 0) {
_onNodesAdded(mutation.addedNodes);
}
if (mutation.removedNodes.length > 0) {
_onNodesRemoved(mutation.removedNodes);
}
});
});
observer.observe(_document, {
childList: true,
subtree: true
});
} else {
// use MutationEvents as fallback
window.document.addEventListener('DOMNodeInserted', function niLstnr(e) {
_onNodesAdded([e.target]);
});
window.document.addEventListener('DOMNodeRemoved', function nrLstnr(e) {
_onNodesRemoved([e.target]);
});
}
}
Retrieves related documents (external CSS and JS files)
function related() {
var rel = {
scripts: {},
stylesheets: {}
};
var i;
// iterate on document scripts (HTMLCollection doesn't provide forEach iterator).
for (i = 0; i < _document.scripts.length; i++) {
// add only external scripts
if (_document.scripts[i].src) {
rel.scripts[_document.scripts[i].src] = true;
}
}
var s, j;
//traverse @import rules
var traverseRules = function _traverseRules(sheet, base) {
var i,
href = sheet.href,
cssRules;
// Deal with Firefox's SecurityError when accessing sheets
// from other domains. Chrome will safely return `undefined`.
try {
cssRules = sheet.cssRules;
} catch (e) {
if (e.name !== "SecurityError") {
throw e;
}
}
if (href && cssRules) {
if (rel.stylesheets[href] === undefined) {
rel.stylesheets[href] = [];
}
rel.stylesheets[href].push(base);
for (i = 0; i < cssRules.length; i++) {
if (cssRules[i].href) {
traverseRules(cssRules[i].styleSheet, base);
}
}
}
};
//iterate on document.stylesheets (StyleSheetList doesn't provide forEach iterator).
for (j = 0; j < window.document.styleSheets.length; j++) {
s = window.document.styleSheets[j];
traverseRules(s, s.href);
}
return rel;
}
Start listening for events and send initial related documents message.
function start(document, transport) {
_transport = transport;
_document = document;
// start listening to node changes
_enableListeners();
var rel = related();
// send the current status of related docs.
_transport.send(JSON.stringify({
method: "DocumentRelated",
related: rel
}));
// initialize stylesheets with current status for further notifications.
CSS.stylesheets = rel.stylesheets;
}