299 lines
8.6 KiB
JavaScript
299 lines
8.6 KiB
JavaScript
/**
|
|
* MIT License http://www.opensource.org/licenses/mit-license.php
|
|
* Tobias Koppers @sokra
|
|
* Vitaliy Filippov vitalif@yourcmc.ru
|
|
*/
|
|
var stylesInDom = {},
|
|
memoize = function(fn) {
|
|
var memo;
|
|
return function () {
|
|
if (typeof memo === "undefined") memo = fn.apply(this, arguments);
|
|
return memo;
|
|
};
|
|
},
|
|
isOldIE = memoize(function() {
|
|
// Test for IE <= 9 as proposed by Browserhacks
|
|
// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
|
|
// Tests for existence of standard globals is to allow style-loader
|
|
// to operate correctly into non-standard environments
|
|
// @see https://github.com/webpack-contrib/style-loader/issues/177
|
|
return window && document && document.all && !window.atob;
|
|
}),
|
|
getElement = (function(fn) {
|
|
var memo = {};
|
|
return function(selector) {
|
|
if (typeof memo[selector] === "undefined") {
|
|
memo[selector] = fn.call(this, selector);
|
|
}
|
|
return memo[selector]
|
|
};
|
|
})(function (styleTarget) {
|
|
return document.querySelector(styleTarget)
|
|
}),
|
|
singletonElement = null,
|
|
singletonCounter = 0,
|
|
styleElementsInsertedAtTop = [],
|
|
fixUrls = require("./fixUrls");
|
|
|
|
module.exports = function(list, options) {
|
|
if(typeof DEBUG !== "undefined" && DEBUG) {
|
|
if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
|
|
}
|
|
|
|
options = options || {};
|
|
options.attrs = typeof options.attrs === "object" ? options.attrs : {};
|
|
|
|
// By default, use "__universalLoaderStyles" as the ID of style element
|
|
if (!options.elementId) options.elementId = "__universalLoaderStyles";
|
|
|
|
// Use single-tag solution by default
|
|
if (options.singleton == undefined) options.singleton = true;
|
|
|
|
// By default, add <style> tags to the <head> element
|
|
if (options.insertInto == undefined) options.insertInto = "head";
|
|
|
|
// By default, add <style> tags to the bottom of the target
|
|
if (options.insertAt == undefined) options.insertAt = "bottom";
|
|
|
|
var styles = listToStyles(list);
|
|
addStylesToDom(styles, options);
|
|
|
|
return function update(newList) {
|
|
var mayRemove = [];
|
|
for(var i = 0; i < styles.length; i++) {
|
|
var item = styles[i];
|
|
var domStyle = stylesInDom[item.id];
|
|
domStyle.refs--;
|
|
mayRemove.push(domStyle);
|
|
}
|
|
if(newList) {
|
|
var newStyles = listToStyles(newList);
|
|
addStylesToDom(newStyles, options);
|
|
}
|
|
for(var i = 0; i < mayRemove.length; i++) {
|
|
var domStyle = mayRemove[i];
|
|
if(domStyle.refs === 0) {
|
|
for(var j = 0; j < domStyle.parts.length; j++)
|
|
domStyle.parts[j]();
|
|
delete stylesInDom[domStyle.id];
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
function addStylesToDom(styles, options) {
|
|
for(var i = 0; i < styles.length; i++) {
|
|
var item = styles[i];
|
|
var domStyle = stylesInDom[item.id];
|
|
if(domStyle) {
|
|
domStyle.refs++;
|
|
for(var j = 0; j < domStyle.parts.length; j++) {
|
|
domStyle.parts[j](item.parts[j]);
|
|
}
|
|
for(; j < item.parts.length; j++) {
|
|
domStyle.parts.push(addStyle(item.parts[j], options));
|
|
}
|
|
} else {
|
|
var parts = [];
|
|
for(var j = 0; j < item.parts.length; j++) {
|
|
parts.push(addStyle(item.parts[j], options));
|
|
}
|
|
stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
|
|
}
|
|
}
|
|
}
|
|
|
|
function listToStyles(list) {
|
|
var styles = [];
|
|
var newStyles = {};
|
|
list = list.default || list;
|
|
for(var i = 0; i < list.length; i++) {
|
|
var item = list[i];
|
|
var id = item[0];
|
|
var css = item[1];
|
|
var media = item[2];
|
|
var sourceMap = item[3];
|
|
var part = {css: css, media: media, sourceMap: sourceMap};
|
|
if(!newStyles[id])
|
|
styles.push(newStyles[id] = {id: id, parts: [part]});
|
|
else
|
|
newStyles[id].parts.push(part);
|
|
}
|
|
return styles;
|
|
}
|
|
|
|
function insertStyleElement(options, styleElement) {
|
|
var styleTarget = getElement(options.insertInto)
|
|
if (!styleTarget) {
|
|
throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
|
|
}
|
|
var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
|
|
if (options.insertAt === "top") {
|
|
if(!lastStyleElementInsertedAtTop) {
|
|
styleTarget.insertBefore(styleElement, styleTarget.firstChild);
|
|
} else if(lastStyleElementInsertedAtTop.nextSibling) {
|
|
styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
|
|
} else {
|
|
styleTarget.appendChild(styleElement);
|
|
}
|
|
styleElementsInsertedAtTop.push(styleElement);
|
|
} else if (options.insertAt === "bottom") {
|
|
styleTarget.appendChild(styleElement);
|
|
} else {
|
|
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
|
|
}
|
|
}
|
|
|
|
function removeStyleElement(styleElement) {
|
|
styleElement.parentNode.removeChild(styleElement);
|
|
var idx = styleElementsInsertedAtTop.indexOf(styleElement);
|
|
if(idx >= 0) {
|
|
styleElementsInsertedAtTop.splice(idx, 1);
|
|
}
|
|
}
|
|
|
|
function createStyleElement(options, id) {
|
|
var styleElement = document.getElementById(id);
|
|
options.attrs.type = "text/css";
|
|
if (!styleElement) {
|
|
styleElement = document.createElement("style");
|
|
attachTagAttrs(styleElement, options.attrs);
|
|
styleElement.setAttribute('id', id);
|
|
insertStyleElement(options, styleElement);
|
|
}
|
|
return styleElement;
|
|
}
|
|
|
|
function createLinkElement(options) {
|
|
var linkElement = document.createElement("link");
|
|
options.attrs.type = "text/css";
|
|
options.attrs.rel = "stylesheet";
|
|
|
|
attachTagAttrs(linkElement, options.attrs);
|
|
insertStyleElement(options, linkElement);
|
|
return linkElement;
|
|
}
|
|
|
|
function attachTagAttrs(element, attrs) {
|
|
Object.keys(attrs).forEach(function (key) {
|
|
element.setAttribute(key, attrs[key]);
|
|
});
|
|
}
|
|
|
|
function addStyle(obj, options) {
|
|
var styleElement, update, remove;
|
|
|
|
var styleIndex = singletonCounter++;
|
|
if (options.singleton) {
|
|
styleElement = singletonElement || (singletonElement = createStyleElement(options, options.elementId));
|
|
update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
|
|
remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
|
|
} else if(obj.sourceMap &&
|
|
typeof URL === "function" &&
|
|
typeof URL.createObjectURL === "function" &&
|
|
typeof URL.revokeObjectURL === "function" &&
|
|
typeof Blob === "function" &&
|
|
typeof btoa === "function") {
|
|
styleElement = createLinkElement(options);
|
|
update = updateLink.bind(null, styleElement, options);
|
|
remove = function() {
|
|
removeStyleElement(styleElement);
|
|
if(styleElement.href)
|
|
URL.revokeObjectURL(styleElement.href);
|
|
};
|
|
} else {
|
|
styleElement = createStyleElement(options, options.elementId+styleIndex);
|
|
update = applyToTag.bind(null, styleElement);
|
|
remove = function() {
|
|
removeStyleElement(styleElement);
|
|
};
|
|
}
|
|
|
|
update(obj);
|
|
|
|
return function updateStyle(newObj) {
|
|
if(newObj) {
|
|
if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
|
|
return;
|
|
update(obj = newObj);
|
|
} else {
|
|
remove();
|
|
}
|
|
};
|
|
}
|
|
|
|
var replaceText = (function () {
|
|
var textStore = [];
|
|
|
|
return function (index, replacement) {
|
|
textStore[index] = replacement;
|
|
return textStore.filter(Boolean).join('\n');
|
|
};
|
|
})();
|
|
|
|
function applyToSingletonTag(styleElement, index, remove, obj) {
|
|
var css = remove ? "" : obj.css;
|
|
|
|
if (styleElement.styleSheet) {
|
|
styleElement.styleSheet.cssText = replaceText(index, css);
|
|
} else {
|
|
var cssNode = document.createTextNode(css);
|
|
var childNodes = styleElement.childNodes;
|
|
if (childNodes[index])
|
|
styleElement.removeChild(childNodes[index]);
|
|
if (childNodes.length) {
|
|
styleElement.insertBefore(cssNode, childNodes[index]);
|
|
} else {
|
|
styleElement.appendChild(cssNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
function applyToTag(styleElement, obj) {
|
|
var css = obj.css;
|
|
var media = obj.media;
|
|
|
|
if(media) {
|
|
styleElement.setAttribute("media", media)
|
|
}
|
|
|
|
if(styleElement.styleSheet) {
|
|
styleElement.styleSheet.cssText = css;
|
|
} else {
|
|
while(styleElement.firstChild) {
|
|
styleElement.removeChild(styleElement.firstChild);
|
|
}
|
|
styleElement.appendChild(document.createTextNode(css));
|
|
}
|
|
}
|
|
|
|
function updateLink(linkElement, options, obj) {
|
|
var css = obj.css;
|
|
var sourceMap = obj.sourceMap;
|
|
|
|
/* If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
|
|
and there is no publicPath defined then lets turn convertToAbsoluteUrls
|
|
on by default. Otherwise default to the convertToAbsoluteUrls option
|
|
directly
|
|
*/
|
|
var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;
|
|
|
|
if (options.convertToAbsoluteUrls || autoFixUrls){
|
|
css = fixUrls(css);
|
|
}
|
|
|
|
if(sourceMap) {
|
|
// http://stackoverflow.com/a/26603875
|
|
css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
|
|
}
|
|
|
|
var blob = new Blob([css], { type: "text/css" });
|
|
|
|
var oldSrc = linkElement.href;
|
|
|
|
linkElement.href = URL.createObjectURL(blob);
|
|
|
|
if(oldSrc)
|
|
URL.revokeObjectURL(oldSrc);
|
|
}
|