UNPKG

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
/*! 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 "use strict"; 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){ "use strict"; 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){ "use strict"; 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){ "use strict"; 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){ "use strict"; 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){ "use strict"; 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){ "use strict"; 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; }));