inlineresources
Version:
Inlines style sheets, images, fonts and scripts in HTML documents. Works in the browser.
1,304 lines (1,105 loc) • 40.2 kB
JavaScript
/*! inlineresources - v1.0.1 - 2025-11-06
* http://www.github.com/cburgmer/inlineresources
* Copyright (c) 2025 Christoph Burgmer; Licensed MIT */
// UMD header
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['url', 'css-font-face-src'], function (a0,b1) {
return (root['inlineresources'] = factory(a0,b1));
});
} else if (typeof exports === 'object') { // browserify context
var f = factory(require('url'), require('css-font-face-src'));
for(var prop in f) exports[prop] = f[prop];
} else {
root['inlineresources'] = factory(url,cssFontFaceSrc);
}
}(this, function (url, cssFontFaceSrc) {
var modules = {url: url, 'css-font-face-src': cssFontFaceSrc};
var require = function (name) { if (modules[name]) { return modules[name]; } else { throw new Error('Module not found: ' + name); }; };
// cheat browserify module to leave the function reference for us
var module = {}, exports={};
// from here on it's browserify all the way
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.inlineresources = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
// Simple, stupid "background"/"background-image" value parser that just aims at exposing the image URLs
;
var cssSupport = _dereq_("./cssSupport");
var trimCSSWhitespace = function (url) {
var whitespaceRegex = /^[\t\r\f\n ]*(.+?)[\t\r\f\n ]*$/;
return url.replace(whitespaceRegex, "$1");
};
// TODO exporting this for the sake of unit testing. Should rather test the background value parser explicitly.
exports.extractCssUrl = function (cssUrl) {
var urlRegex = /^url\(("[^"]+"|'[^']+'|[^\)]+)\)/,
quotedUrl;
if (!urlRegex.test(cssUrl)) {
throw new Error("Invalid url");
}
quotedUrl = urlRegex.exec(cssUrl)[1];
return cssSupport.unquoteString(trimCSSWhitespace(quotedUrl));
};
var sliceBackgroundDeclaration = function (backgroundDeclarationText) {
var functionParamRegexS = "\\s*(?:\"[^\"]*\"|'[^']*'|[^\\(]+)\\s*",
valueRegexS =
"(" +
"url\\(" +
functionParamRegexS +
"\\)" +
"|" +
"[^,\\s]+" +
")",
simpleSingularBackgroundRegexS = "(?:\\s*" + valueRegexS + ")+",
simpleBackgroundRegexS =
"^\\s*(" +
simpleSingularBackgroundRegexS +
")" +
"(?:\\s*,\\s*(" +
simpleSingularBackgroundRegexS +
"))*" +
"\\s*$",
simpleSingularBackgroundRegex = new RegExp(
simpleSingularBackgroundRegexS,
"g"
),
outerRepeatedMatch,
backgroundLayers = [],
getValues = function (singularBackgroundDeclaration) {
var valueRegex = new RegExp(valueRegexS, "g"),
backgroundValues = [],
repeatedMatch;
repeatedMatch = valueRegex.exec(singularBackgroundDeclaration);
while (repeatedMatch) {
backgroundValues.push(repeatedMatch[1]);
repeatedMatch = valueRegex.exec(singularBackgroundDeclaration);
}
return backgroundValues;
};
if (backgroundDeclarationText.match(new RegExp(simpleBackgroundRegexS))) {
outerRepeatedMatch = simpleSingularBackgroundRegex.exec(
backgroundDeclarationText
);
while (outerRepeatedMatch) {
backgroundLayers.push(getValues(outerRepeatedMatch[0]));
outerRepeatedMatch = simpleSingularBackgroundRegex.exec(
backgroundDeclarationText
);
}
return backgroundLayers;
}
return [];
};
var findBackgroundImageUrlInValues = function (values) {
var i, url;
for (i = 0; i < values.length; i++) {
try {
url = exports.extractCssUrl(values[i]);
return {
url: url,
idx: i,
};
} catch (e) {}
}
};
exports.parse = function (backgroundValue) {
var backgroundLayers = sliceBackgroundDeclaration(backgroundValue);
return backgroundLayers.map(function (backgroundLayerValues) {
var urlMatch = findBackgroundImageUrlInValues(backgroundLayerValues);
if (urlMatch) {
return {
preUrl: backgroundLayerValues.slice(0, urlMatch.idx),
url: urlMatch.url,
postUrl: backgroundLayerValues.slice(urlMatch.idx + 1),
};
} else {
return {
preUrl: backgroundLayerValues,
};
}
});
};
exports.serialize = function (parsedBackground) {
var backgroundLayers = parsedBackground.map(function (backgroundLayer) {
var values = [].concat(backgroundLayer.preUrl);
if (backgroundLayer.url) {
values.push('url("' + backgroundLayer.url + '")');
}
if (backgroundLayer.postUrl) {
values = values.concat(backgroundLayer.postUrl);
}
return values.join(" ");
});
return backgroundLayers.join(", ");
};
},{"./cssSupport":2}],2:[function(_dereq_,module,exports){
;
exports.unquoteString = function (quotedUrl) {
var doubleQuoteRegex = /^"(.*)"$/,
singleQuoteRegex = /^'(.*)'$/;
if (doubleQuoteRegex.test(quotedUrl)) {
return quotedUrl.replace(doubleQuoteRegex, "$1");
} else {
if (singleQuoteRegex.test(quotedUrl)) {
return quotedUrl.replace(singleQuoteRegex, "$1");
} else {
return quotedUrl;
}
}
};
exports.rulesForCssText = function (styleContent, options) {
var doc = document.implementation.createHTMLDocument(""),
styleElement = document.createElement("style"),
rules;
styleElement.textContent = styleContent;
if (options && options.nonce) {
styleElement.nonce = options.nonce;
}
// the style will only be parsed once it is added to a document
doc.body.appendChild(styleElement);
rules = styleElement.sheet.cssRules;
return Array.prototype.slice.call(rules);
};
exports.cssRulesToText = function (cssRules) {
return cssRules.reduce(function (cssText, rule) {
return cssText + rule.cssText;
}, "");
};
exports.exchangeRule = function (cssRules, rule, newRuleText, options) {
var ruleIdx = cssRules.indexOf(rule);
// We create a new document and stylesheet to parse the rule,
// instead of relying on rule.parentStyleSheet, because
// rule.parentStyleSheet may be null
// (https://github.com/cburgmer/inlineresources/issues/3)
cssRules[ruleIdx] = exports.rulesForCssText(newRuleText, options)[0];
};
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=443978
exports.changeFontFaceRuleSrc = function (cssRules, rule, newSrc, options) {
var newRuleText =
"@font-face { font-family: " +
rule.style.getPropertyValue("font-family") +
"; ";
if (rule.style.getPropertyValue("font-style")) {
newRuleText +=
"font-style: " + rule.style.getPropertyValue("font-style") + "; ";
}
if (rule.style.getPropertyValue("font-weight")) {
newRuleText +=
"font-weight: " + rule.style.getPropertyValue("font-weight") + "; ";
}
if (rule.style.getPropertyValue("unicode-range")) {
newRuleText +=
"unicode-range: " +
rule.style.getPropertyValue("unicode-range") +
"; ";
}
newRuleText += "src: " + newSrc + "}";
exports.exchangeRule(cssRules, rule, newRuleText, options);
};
},{}],3:[function(_dereq_,module,exports){
;
var util = _dereq_("./util"),
inlineImage = _dereq_("./inlineImage"),
inlineScript = _dereq_("./inlineScript"),
inlineCss = _dereq_("./inlineCss"),
cssSupport = _dereq_("./cssSupport");
var getUrlBasePath = function (url) {
return util.joinUrl(url, ".");
};
var parameterHashFunction = function (params) {
// HACK JSON.stringify is poor man's hashing;
// same objects might not receive same result as key order is not guaranteed
var a = params.map(function (param, idx) {
// Only include options relevant for method
if (idx === params.length - 1) {
param = {
// Two different HTML pages on the same path level have the same base path, but a different URL
baseUrl: getUrlBasePath(param.baseUrl),
};
}
return JSON.stringify(param);
});
return a;
};
var memoizeFunctionOnCaching = function (func, options) {
if (
options.cache !== false &&
options.cache !== "none" &&
options.cacheBucket
) {
return util.memoize(func, parameterHashFunction, options.cacheBucket);
} else {
return func;
}
};
/* Style inlining */
var requestExternalsForStylesheet = function (
styleContent,
alreadyLoadedCssUrls,
options
) {
var cssRules = cssSupport.rulesForCssText(styleContent, options);
return inlineCss
.loadCSSImportsForRules(cssRules, alreadyLoadedCssUrls, options)
.then(function (cssImportResult) {
return inlineCss
.loadAndInlineCSSResourcesForRules(cssRules, options)
.then(function (cssResourcesResult) {
var errors = cssImportResult.errors.concat(
cssResourcesResult.errors
),
hasChanges =
cssImportResult.hasChanges ||
cssResourcesResult.hasChanges;
if (hasChanges) {
styleContent = cssSupport.cssRulesToText(cssRules);
}
return {
hasChanges: hasChanges,
content: styleContent,
errors: errors,
};
});
});
};
var loadAndInlineCssForStyle = function (style, options, alreadyLoadedCssUrls) {
var styleContent = style.textContent,
processExternals = memoizeFunctionOnCaching(
requestExternalsForStylesheet,
options
);
return processExternals(styleContent, alreadyLoadedCssUrls, options).then(
function (result) {
if (result.hasChanges) {
style.childNodes[0].nodeValue = result.content;
}
return util.cloneArray(result.errors);
}
);
};
var getCssStyleElements = function (doc) {
var styles = doc.getElementsByTagName("style");
return Array.prototype.filter.call(styles, function (style) {
return (
!style.attributes.type || style.attributes.type.value === "text/css"
);
});
};
exports.loadAndInlineStyles = function (doc, options) {
var styles = getCssStyleElements(doc),
allErrors = [],
alreadyLoadedCssUrls = [],
inlineOptions;
inlineOptions = util.clone(options);
inlineOptions.baseUrl =
inlineOptions.baseUrl || util.getDocumentBaseUrl(doc);
return Promise.all(
styles.map(function (style) {
return loadAndInlineCssForStyle(
style,
inlineOptions,
alreadyLoadedCssUrls
).then(function (errors) {
allErrors = allErrors.concat(errors);
});
})
).then(function () {
return allErrors;
});
};
/* CSS link inlining */
var substituteLinkWithInlineStyle = function (oldLinkNode, styleContent) {
var parent = oldLinkNode.parentNode,
styleNode;
styleContent = styleContent.trim();
if (styleContent) {
styleNode = oldLinkNode.ownerDocument.createElement("style");
styleNode.type = "text/css";
styleNode.appendChild(
oldLinkNode.ownerDocument.createTextNode(styleContent)
);
parent.insertBefore(styleNode, oldLinkNode);
}
parent.removeChild(oldLinkNode);
};
var requestStylesheetAndInlineResources = function (url, options) {
return util
.ajax(url, options)
.then(function (content) {
var cssRules = cssSupport.rulesForCssText(content, options);
return {
content: content,
cssRules: cssRules,
};
})
.then(function (result) {
var hasChangesFromPathAdjustment =
inlineCss.adjustPathsOfCssResources(
url,
result.cssRules,
options
);
return {
content: result.content,
cssRules: result.cssRules,
hasChanges: hasChangesFromPathAdjustment,
};
})
.then(function (result) {
return inlineCss
.loadCSSImportsForRules(result.cssRules, [], options)
.then(function (cssImportResult) {
return {
content: result.content,
cssRules: result.cssRules,
hasChanges:
result.hasChanges || cssImportResult.hasChanges,
errors: cssImportResult.errors,
};
});
})
.then(function (result) {
return inlineCss
.loadAndInlineCSSResourcesForRules(result.cssRules, options)
.then(function (cssResourcesResult) {
return {
content: result.content,
cssRules: result.cssRules,
hasChanges:
result.hasChanges || cssResourcesResult.hasChanges,
errors: result.errors.concat(cssResourcesResult.errors),
};
});
})
.then(function (result) {
var content = result.content;
if (result.hasChanges) {
content = cssSupport.cssRulesToText(result.cssRules);
}
return {
content: content,
errors: result.errors,
};
});
};
var loadLinkedCSS = function (link, options) {
var cssHref = link.attributes.href.value,
documentBaseUrl = util.getDocumentBaseUrl(link.ownerDocument),
ajaxOptions = util.clone(options);
if (!ajaxOptions.baseUrl && documentBaseUrl) {
ajaxOptions.baseUrl = documentBaseUrl;
}
var processStylesheet = memoizeFunctionOnCaching(
requestStylesheetAndInlineResources,
options
);
return processStylesheet(cssHref, ajaxOptions).then(function (result) {
return {
content: result.content,
errors: util.cloneArray(result.errors),
};
});
};
var getCssStylesheetLinks = function (doc) {
var links = doc.getElementsByTagName("link");
return Array.prototype.filter.call(links, function (link) {
return (
link.attributes.rel &&
link.attributes.rel.value === "stylesheet" &&
(!link.attributes.type || link.attributes.type.value === "text/css")
);
});
};
exports.loadAndInlineCssLinks = function (doc, options) {
var links = getCssStylesheetLinks(doc),
errors = [];
return Promise.all(
links.map(function (link) {
return loadLinkedCSS(link, options).then(
function (result) {
substituteLinkWithInlineStyle(link, result.content + "\n");
errors = errors.concat(result.errors);
},
function (e) {
errors.push({
resourceType: "stylesheet",
url: e.url,
msg: "Unable to load stylesheet " + e.url,
});
}
);
})
).then(function () {
return errors;
});
};
/* Main */
exports.loadAndInlineImages = inlineImage.inline;
exports.loadAndInlineScript = inlineScript.inline;
exports.inlineReferences = function (doc, options) {
var allErrors = [],
inlineFuncs = [
exports.loadAndInlineImages,
exports.loadAndInlineStyles,
exports.loadAndInlineCssLinks,
];
if (options.inlineScripts !== false) {
inlineFuncs.push(exports.loadAndInlineScript);
}
return Promise.all(
inlineFuncs.map(function (func) {
return func(doc, options).then(function (errors) {
allErrors = allErrors.concat(errors);
});
})
).then(function () {
return allErrors;
});
};
},{"./cssSupport":2,"./inlineCss":4,"./inlineImage":5,"./inlineScript":6,"./util":7}],4:[function(_dereq_,module,exports){
;
var util = _dereq_("./util"),
cssSupport = _dereq_("./cssSupport"),
backgroundValueParser = _dereq_("./backgroundValueParser"),
fontFaceSrcValueParser = _dereq_("css-font-face-src");
var updateCssPropertyValue = function (rule, property, value) {
rule.style.setProperty(
property,
value,
rule.style.getPropertyPriority(property)
);
};
var findBackgroundImageRules = function (cssRules) {
return cssRules.filter(function (rule) {
return (
rule.type === window.CSSRule.STYLE_RULE &&
(rule.style.getPropertyValue("background-image") ||
rule.style.getPropertyValue("background"))
);
});
};
var findBackgroundDeclarations = function (rules) {
var backgroundDeclarations = [];
rules.forEach(function (rule) {
if (rule.style.getPropertyValue("background-image")) {
backgroundDeclarations.push({
property: "background-image",
value: rule.style.getPropertyValue("background-image"),
rule: rule,
});
} else if (rule.style.getPropertyValue("background")) {
backgroundDeclarations.push({
property: "background",
value: rule.style.getPropertyValue("background"),
rule: rule,
});
}
});
return backgroundDeclarations;
};
var findFontFaceRules = function (cssRules) {
return cssRules.filter(function (rule) {
return (
rule.type === window.CSSRule.FONT_FACE_RULE &&
rule.style.getPropertyValue("src")
);
});
};
var findCSSImportRules = function (cssRules) {
return cssRules.filter(function (rule) {
return rule.type === window.CSSRule.IMPORT_RULE && rule.href;
});
};
var findExternalBackgroundUrls = function (parsedBackground) {
var matchIndices = [];
parsedBackground.forEach(function (backgroundLayer, i) {
if (backgroundLayer.url && !util.isDataUri(backgroundLayer.url)) {
matchIndices.push(i);
}
});
return matchIndices;
};
var findExternalFontFaceUrls = function (parsedFontFaceSources) {
var sourceIndices = [];
parsedFontFaceSources.forEach(function (sourceItem, i) {
if (sourceItem.url && !util.isDataUri(sourceItem.url)) {
sourceIndices.push(i);
}
});
return sourceIndices;
};
exports.adjustPathsOfCssResources = function (baseUrl, cssRules, options) {
var backgroundRules = findBackgroundImageRules(cssRules),
backgroundDeclarations = findBackgroundDeclarations(backgroundRules),
change = false;
backgroundDeclarations.forEach(function (declaration) {
var parsedBackground = backgroundValueParser.parse(declaration.value),
externalBackgroundIndices =
findExternalBackgroundUrls(parsedBackground),
backgroundValue;
if (externalBackgroundIndices.length > 0) {
externalBackgroundIndices.forEach(function (backgroundLayerIndex) {
var relativeUrl = parsedBackground[backgroundLayerIndex].url,
url = util.joinUrl(baseUrl, relativeUrl);
parsedBackground[backgroundLayerIndex].url = url;
});
backgroundValue = backgroundValueParser.serialize(parsedBackground);
updateCssPropertyValue(
declaration.rule,
declaration.property,
backgroundValue
);
change = true;
}
});
findFontFaceRules(cssRules).forEach(function (rule) {
var fontFaceSrcDeclaration = rule.style.getPropertyValue("src"),
parsedFontFaceSources,
externalFontFaceUrlIndices;
try {
parsedFontFaceSources = fontFaceSrcValueParser.parse(
fontFaceSrcDeclaration
);
} catch (e) {
return;
}
externalFontFaceUrlIndices = findExternalFontFaceUrls(
parsedFontFaceSources
);
if (externalFontFaceUrlIndices.length > 0) {
externalFontFaceUrlIndices.forEach(function (fontFaceUrlIndex) {
var relativeUrl = parsedFontFaceSources[fontFaceUrlIndex].url,
url = util.joinUrl(baseUrl, relativeUrl);
parsedFontFaceSources[fontFaceUrlIndex].url = url;
});
cssSupport.changeFontFaceRuleSrc(
cssRules,
rule,
fontFaceSrcValueParser.serialize(
parsedFontFaceSources,
options
),
options
);
change = true;
}
});
findCSSImportRules(cssRules).forEach(function (rule) {
var cssUrl = rule.href,
url = util.joinUrl(baseUrl, cssUrl);
cssSupport.exchangeRule(
cssRules,
rule,
"@import url(" + url + ");",
options
);
change = true;
});
return change;
};
/* CSS import inlining */
var substituteRule = function (cssRules, rule, newCssRules) {
var position = cssRules.indexOf(rule);
cssRules.splice(position, 1);
newCssRules.forEach(function (newRule, i) {
cssRules.splice(position + i, 0, newRule);
});
};
var loadAndInlineCSSImport = function (
cssRules,
rule,
alreadyLoadedCssUrls,
options
) {
var url = rule.href,
cssHrefRelativeToDoc;
url = cssSupport.unquoteString(url);
cssHrefRelativeToDoc = util.joinUrl(options.baseUrl, url);
if (alreadyLoadedCssUrls.indexOf(cssHrefRelativeToDoc) >= 0) {
// Remove URL by adding empty string
substituteRule(cssRules, rule, []);
return Promise.resolve([]);
} else {
alreadyLoadedCssUrls.push(cssHrefRelativeToDoc);
}
return util.ajax(url, options).then(
function (cssText) {
var externalCssRules = cssSupport.rulesForCssText(cssText, options);
// Recursively follow @import statements
return exports
.loadCSSImportsForRules(
externalCssRules,
alreadyLoadedCssUrls,
options
)
.then(function (result) {
exports.adjustPathsOfCssResources(
url,
externalCssRules,
options
);
substituteRule(cssRules, rule, externalCssRules);
return result.errors;
});
},
function (e) {
throw {
resourceType: "stylesheet",
url: e.url,
msg: "Unable to load stylesheet " + e.url,
};
}
);
};
exports.loadCSSImportsForRules = function (
cssRules,
alreadyLoadedCssUrls,
options
) {
var rulesToInline = findCSSImportRules(cssRules),
errors = [],
hasChanges = false;
return Promise.all(
rulesToInline.map(function (rule) {
return loadAndInlineCSSImport(
cssRules,
rule,
alreadyLoadedCssUrls,
options
).then(
function (moreErrors) {
errors = errors.concat(moreErrors);
hasChanges = true;
},
function (e) {
errors.push(e);
}
);
})
).then(function () {
return {
hasChanges: hasChanges,
errors: errors,
};
});
};
/* CSS linked resource inlining */
var loadAndInlineBackgroundImages = function (backgroundValue, options) {
var parsedBackground = backgroundValueParser.parse(backgroundValue),
externalBackgroundLayerIndices =
findExternalBackgroundUrls(parsedBackground),
hasChanges = false;
return util
.collectAndReportErrors(
externalBackgroundLayerIndices.map(function (backgroundLayerIndex) {
var url = parsedBackground[backgroundLayerIndex].url;
return util.getDataURIForImageURL(url, options).then(
function (dataURI) {
parsedBackground[backgroundLayerIndex].url = dataURI;
hasChanges = true;
},
function (e) {
throw {
resourceType: "backgroundImage",
url: e.url,
msg: "Unable to load background-image " + e.url,
};
}
);
})
)
.then(function (errors) {
return {
backgroundValue:
backgroundValueParser.serialize(parsedBackground),
hasChanges: hasChanges,
errors: errors,
};
});
};
var iterateOverRulesAndInlineBackgroundImages = function (cssRules, options) {
var rulesToInline = findBackgroundImageRules(cssRules),
backgroundDeclarations = findBackgroundDeclarations(rulesToInline),
errors = [],
cssHasChanges = false;
return Promise.all(
backgroundDeclarations.map(function (declaration) {
return loadAndInlineBackgroundImages(
declaration.value,
options
).then(function (result) {
if (result.hasChanges) {
updateCssPropertyValue(
declaration.rule,
declaration.property,
result.backgroundValue
);
cssHasChanges = true;
}
errors = errors.concat(result.errors);
});
})
).then(function () {
return {
hasChanges: cssHasChanges,
errors: errors,
};
});
};
var loadAndInlineFontFace = function (srcDeclarationValue, options) {
var hasChanges = false,
parsedFontFaceSources,
externalFontFaceUrlIndices;
try {
parsedFontFaceSources =
fontFaceSrcValueParser.parse(srcDeclarationValue);
} catch (e) {
parsedFontFaceSources = [];
}
externalFontFaceUrlIndices = findExternalFontFaceUrls(
parsedFontFaceSources
);
return util
.collectAndReportErrors(
externalFontFaceUrlIndices.map(function (urlIndex) {
var fontSrc = parsedFontFaceSources[urlIndex],
format = fontSrc.format || "woff";
return util.binaryAjax(fontSrc.url, options).then(
function (content) {
var base64Content = btoa(content);
fontSrc.url =
"data:font/" + format + ";base64," + base64Content;
hasChanges = true;
},
function (e) {
throw {
resourceType: "fontFace",
url: e.url,
msg: "Unable to load font-face " + e.url,
};
}
);
})
)
.then(function (errors) {
return {
srcDeclarationValue: fontFaceSrcValueParser.serialize(
parsedFontFaceSources
),
hasChanges: hasChanges,
errors: errors,
};
});
};
var iterateOverRulesAndInlineFontFace = function (cssRules, options) {
var rulesToInline = findFontFaceRules(cssRules),
errors = [],
hasChanges = false;
return Promise.all(
rulesToInline.map(function (rule) {
var srcDeclarationValue = rule.style.getPropertyValue("src");
return loadAndInlineFontFace(srcDeclarationValue, options).then(
function (result) {
if (result.hasChanges) {
cssSupport.changeFontFaceRuleSrc(
cssRules,
rule,
result.srcDeclarationValue,
options
);
hasChanges = true;
}
errors = errors.concat(result.errors);
}
);
})
).then(function () {
return {
hasChanges: hasChanges,
errors: errors,
};
});
};
exports.loadAndInlineCSSResourcesForRules = function (cssRules, options) {
var hasChanges = false,
errors = [];
return Promise.all(
[
iterateOverRulesAndInlineBackgroundImages,
iterateOverRulesAndInlineFontFace,
].map(function (func) {
return func(cssRules, options).then(function (result) {
hasChanges = hasChanges || result.hasChanges;
errors = errors.concat(result.errors);
});
})
).then(function () {
return {
hasChanges: hasChanges,
errors: errors,
};
});
};
},{"./backgroundValueParser":1,"./cssSupport":2,"./util":7,"css-font-face-src":"css-font-face-src"}],5:[function(_dereq_,module,exports){
;
var util = _dereq_("./util");
var encodeImageAsDataURI = function (image, options) {
var url = null;
if (image.hasAttribute("src")) {
url = image.getAttribute("src");
} else if (image.hasAttributeNS("http://www.w3.org/1999/xlink", "href")) {
url = image.getAttributeNS("http://www.w3.org/1999/xlink", "href");
} else if (image.hasAttribute("href")) {
url = image.getAttribute("href");
}
var documentBase = util.getDocumentBaseUrl(image.ownerDocument),
ajaxOptions = util.clone(options);
if (!ajaxOptions.baseUrl && documentBase) {
ajaxOptions.baseUrl = documentBase;
}
return util.getDataURIForImageURL(url, ajaxOptions).then(
function (dataURI) {
return dataURI;
},
function (e) {
throw {
resourceType: "image",
url: e.url,
msg: "Unable to load image " + e.url,
};
}
);
};
var filterExternalImages = function (images) {
return images.filter(function (image) {
var url = null;
if (image.hasAttribute("src")) {
url = image.getAttribute("src");
} else if (
image.hasAttributeNS("http://www.w3.org/1999/xlink", "href")
) {
url = image.getAttributeNS("http://www.w3.org/1999/xlink", "href");
} else if (image.hasAttribute("href")) {
url = image.getAttribute("href");
}
return url !== null && !util.isDataUri(url);
});
};
var filterInputsForImageType = function (inputs) {
return Array.prototype.filter.call(inputs, function (input) {
return input.type === "image";
});
};
var toArray = function (arrayLike) {
return Array.prototype.slice.call(arrayLike);
};
exports.inline = function (doc, options) {
var images = toArray(doc.getElementsByTagName("img")),
svgImages = toArray(doc.getElementsByTagName("image")),
imageInputs = filterInputsForImageType(
doc.getElementsByTagName("input")
);
images = images.concat(svgImages);
images = images.concat(imageInputs);
var externalImages = filterExternalImages(images);
return util.collectAndReportErrors(
externalImages.map(function (image) {
return encodeImageAsDataURI(image, options).then(function (
dataURI
) {
if (!!image.attributes.src) {
image.attributes.src.value = dataURI;
} else if (!!image.attributes["xlink:href"]) {
image.attributes["xlink:href"].value = dataURI;
} else if (!!image.attributes.href) {
image.attributes.href.value = dataURI;
}
});
})
);
};
},{"./util":7}],6:[function(_dereq_,module,exports){
;
var util = _dereq_("./util");
var loadLinkedScript = function (script, options) {
var src = script.attributes.src.value,
documentBase = util.getDocumentBaseUrl(script.ownerDocument),
ajaxOptions = util.clone(options);
if (!ajaxOptions.baseUrl && documentBase) {
ajaxOptions.baseUrl = documentBase;
}
return util.ajax(src, ajaxOptions).catch(function (e) {
throw {
resourceType: "script",
url: e.url,
msg: "Unable to load script " + e.url,
};
});
};
var escapeClosingTags = function (text) {
// http://stackoverflow.com/questions/9246382/escaping-script-tag-inside-javascript
return text.replace(/<\//g, "<\\/");
};
var substituteExternalScriptWithInline = function (scriptNode, jsCode) {
scriptNode.attributes.removeNamedItem("src");
scriptNode.textContent = escapeClosingTags(jsCode);
};
var getScripts = function (doc) {
var scripts = doc.getElementsByTagName("script");
return Array.prototype.filter.call(scripts, function (script) {
return !!script.attributes.src;
});
};
exports.inline = function (doc, options) {
var scripts = getScripts(doc);
return util.collectAndReportErrors(
scripts.map(function (script) {
return loadLinkedScript(script, options).then(function (jsCode) {
substituteExternalScriptWithInline(script, jsCode);
});
})
);
};
},{"./util":7}],7:[function(_dereq_,module,exports){
;
var url = _dereq_("url");
exports.getDocumentBaseUrl = function (doc) {
if (doc.baseURI !== "about:blank") {
return doc.baseURI;
}
return null;
};
exports.clone = function (object) {
var theClone = {},
i;
for (i in object) {
if (object.hasOwnProperty(i)) {
theClone[i] = object[i];
}
}
return theClone;
};
exports.cloneArray = function (nodeList) {
return Array.prototype.slice.apply(nodeList, [0]);
};
exports.joinUrl = function (baseUrl, relUrl) {
if (!baseUrl) {
return relUrl;
}
return url.resolve(baseUrl, relUrl);
};
exports.isDataUri = function (url) {
return /^data:/.test(url);
};
exports.collectAndReportErrors = function (promises) {
var errors = [];
return Promise.all(
promises.map(function (promise) {
return promise.catch(function (e) {
errors.push(e);
});
})
).then(function () {
return errors;
});
};
var lastCacheDate = null;
var getUncachableURL = function (url, cache) {
if (cache === false || cache === "none" || cache === "repeated") {
if (lastCacheDate === null || cache !== "repeated") {
lastCacheDate = Date.now();
}
return url + "?_=" + lastCacheDate;
} else {
return url;
}
};
exports.ajax = function (url, options) {
return new Promise(function (resolve, reject) {
var ajaxRequest = new window.XMLHttpRequest(),
joinedUrl = exports.joinUrl(options.baseUrl, url),
augmentedUrl;
var doReject = function () {
reject({
msg: "Unable to load url",
url: joinedUrl,
});
};
augmentedUrl = getUncachableURL(joinedUrl, options.cache);
ajaxRequest.addEventListener(
"load",
function () {
if (ajaxRequest.status === 200 || ajaxRequest.status === 0) {
resolve(ajaxRequest.response);
} else {
doReject();
}
},
false
);
ajaxRequest.addEventListener("error", doReject, false);
try {
ajaxRequest.open("GET", augmentedUrl, true);
ajaxRequest.overrideMimeType(options.mimeType);
ajaxRequest.send(null);
} catch (e) {
doReject();
}
});
};
exports.binaryAjax = function (url, options) {
var ajaxOptions = exports.clone(options);
ajaxOptions.mimeType = "text/plain; charset=x-user-defined";
return exports.ajax(url, ajaxOptions).then(function (content) {
var binaryContent = "";
for (var i = 0; i < content.length; i++) {
binaryContent += String.fromCharCode(content.charCodeAt(i) & 0xff);
}
return binaryContent;
});
};
var detectMimeType = function (content) {
var startsWith = function (string, substring) {
return string.substring(0, substring.length) === substring;
};
if (startsWith(content, "<?xml") || startsWith(content, "<svg")) {
return "image/svg+xml";
}
return "image/png";
};
exports.getDataURIForImageURL = function (url, options) {
return exports.binaryAjax(url, options).then(function (content) {
var base64Content = btoa(content),
mimeType = detectMimeType(content);
return "data:" + mimeType + ";base64," + base64Content;
});
};
var uniqueIdList = [];
var constantUniqueIdFor = function (element) {
// HACK, using a list results in O(n), but how do we hash a function?
if (uniqueIdList.indexOf(element) < 0) {
uniqueIdList.push(element);
}
return uniqueIdList.indexOf(element);
};
exports.memoize = function (func, hasher, memo) {
if (typeof memo !== "object") {
throw new Error("cacheBucket is not an object");
}
return function () {
var args = Array.prototype.slice.call(arguments);
var argumentHash = hasher(args),
funcHash = constantUniqueIdFor(func),
retValue;
if (memo[funcHash] && memo[funcHash][argumentHash]) {
return memo[funcHash][argumentHash];
} else {
retValue = func.apply(null, args);
memo[funcHash] = memo[funcHash] || {};
memo[funcHash][argumentHash] = retValue;
return retValue;
}
};
};
},{"url":"url"}]},{},[3])(3)
});
// back to us
return module.exports;
}));