addStyles.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var stylesInDom = {};
  6. var memoize = function (fn) {
  7. var memo;
  8. return function () {
  9. if (typeof memo === "undefined") memo = fn.apply(this, arguments);
  10. return memo;
  11. };
  12. };
  13. var isOldIE = memoize(function () {
  14. // Test for IE <= 9 as proposed by Browserhacks
  15. // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
  16. // Tests for existence of standard globals is to allow style-loader
  17. // to operate correctly into non-standard environments
  18. // @see https://github.com/webpack-contrib/style-loader/issues/177
  19. return window && document && document.all && !window.atob;
  20. });
  21. var getElement = (function (fn) {
  22. var memo = {};
  23. return function(selector) {
  24. if (typeof memo[selector] === "undefined") {
  25. memo[selector] = fn.call(this, selector);
  26. }
  27. return memo[selector]
  28. };
  29. })(function (target) {
  30. return document.querySelector(target)
  31. });
  32. var singleton = null;
  33. var singletonCounter = 0;
  34. var stylesInsertedAtTop = [];
  35. var fixUrls = require("./urls");
  36. module.exports = function(list, options) {
  37. if (typeof DEBUG !== "undefined" && DEBUG) {
  38. if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
  39. }
  40. options = options || {};
  41. options.attrs = typeof options.attrs === "object" ? options.attrs : {};
  42. // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  43. // tags it will allow on a page
  44. if (!options.singleton) options.singleton = isOldIE();
  45. // By default, add <style> tags to the <head> element
  46. if (!options.insertInto) options.insertInto = "head";
  47. // By default, add <style> tags to the bottom of the target
  48. if (!options.insertAt) options.insertAt = "bottom";
  49. var styles = listToStyles(list, options);
  50. addStylesToDom(styles, options);
  51. return function update (newList) {
  52. var mayRemove = [];
  53. for (var i = 0; i < styles.length; i++) {
  54. var item = styles[i];
  55. var domStyle = stylesInDom[item.id];
  56. domStyle.refs--;
  57. mayRemove.push(domStyle);
  58. }
  59. if(newList) {
  60. var newStyles = listToStyles(newList, options);
  61. addStylesToDom(newStyles, options);
  62. }
  63. for (var i = 0; i < mayRemove.length; i++) {
  64. var domStyle = mayRemove[i];
  65. if(domStyle.refs === 0) {
  66. for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();
  67. delete stylesInDom[domStyle.id];
  68. }
  69. }
  70. };
  71. };
  72. function addStylesToDom (styles, options) {
  73. for (var i = 0; i < styles.length; i++) {
  74. var item = styles[i];
  75. var domStyle = stylesInDom[item.id];
  76. if(domStyle) {
  77. domStyle.refs++;
  78. for(var j = 0; j < domStyle.parts.length; j++) {
  79. domStyle.parts[j](item.parts[j]);
  80. }
  81. for(; j < item.parts.length; j++) {
  82. domStyle.parts.push(addStyle(item.parts[j], options));
  83. }
  84. } else {
  85. var parts = [];
  86. for(var j = 0; j < item.parts.length; j++) {
  87. parts.push(addStyle(item.parts[j], options));
  88. }
  89. stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
  90. }
  91. }
  92. }
  93. function listToStyles (list, options) {
  94. var styles = [];
  95. var newStyles = {};
  96. for (var i = 0; i < list.length; i++) {
  97. var item = list[i];
  98. var id = options.base ? item[0] + options.base : item[0];
  99. var css = item[1];
  100. var media = item[2];
  101. var sourceMap = item[3];
  102. var part = {css: css, media: media, sourceMap: sourceMap};
  103. if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
  104. else newStyles[id].parts.push(part);
  105. }
  106. return styles;
  107. }
  108. function insertStyleElement (options, style) {
  109. var target = getElement(options.insertInto)
  110. if (!target) {
  111. throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
  112. }
  113. var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];
  114. if (options.insertAt === "top") {
  115. if (!lastStyleElementInsertedAtTop) {
  116. target.insertBefore(style, target.firstChild);
  117. } else if (lastStyleElementInsertedAtTop.nextSibling) {
  118. target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
  119. } else {
  120. target.appendChild(style);
  121. }
  122. stylesInsertedAtTop.push(style);
  123. } else if (options.insertAt === "bottom") {
  124. target.appendChild(style);
  125. } else {
  126. throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
  127. }
  128. }
  129. function removeStyleElement (style) {
  130. if (style.parentNode === null) return false;
  131. style.parentNode.removeChild(style);
  132. var idx = stylesInsertedAtTop.indexOf(style);
  133. if(idx >= 0) {
  134. stylesInsertedAtTop.splice(idx, 1);
  135. }
  136. }
  137. function createStyleElement (options) {
  138. var style = document.createElement("style");
  139. options.attrs.type = "text/css";
  140. addAttrs(style, options.attrs);
  141. insertStyleElement(options, style);
  142. return style;
  143. }
  144. function createLinkElement (options) {
  145. var link = document.createElement("link");
  146. options.attrs.type = "text/css";
  147. options.attrs.rel = "stylesheet";
  148. addAttrs(link, options.attrs);
  149. insertStyleElement(options, link);
  150. return link;
  151. }
  152. function addAttrs (el, attrs) {
  153. Object.keys(attrs).forEach(function (key) {
  154. el.setAttribute(key, attrs[key]);
  155. });
  156. }
  157. function addStyle (obj, options) {
  158. var style, update, remove, result;
  159. // If a transform function was defined, run it on the css
  160. if (options.transform && obj.css) {
  161. result = options.transform(obj.css);
  162. if (result) {
  163. // If transform returns a value, use that instead of the original css.
  164. // This allows running runtime transformations on the css.
  165. obj.css = result;
  166. } else {
  167. // If the transform function returns a falsy value, don't add this css.
  168. // This allows conditional loading of css
  169. return function() {
  170. // noop
  171. };
  172. }
  173. }
  174. if (options.singleton) {
  175. var styleIndex = singletonCounter++;
  176. style = singleton || (singleton = createStyleElement(options));
  177. update = applyToSingletonTag.bind(null, style, styleIndex, false);
  178. remove = applyToSingletonTag.bind(null, style, styleIndex, true);
  179. } else if (
  180. obj.sourceMap &&
  181. typeof URL === "function" &&
  182. typeof URL.createObjectURL === "function" &&
  183. typeof URL.revokeObjectURL === "function" &&
  184. typeof Blob === "function" &&
  185. typeof btoa === "function"
  186. ) {
  187. style = createLinkElement(options);
  188. update = updateLink.bind(null, style, options);
  189. remove = function () {
  190. removeStyleElement(style);
  191. if(style.href) URL.revokeObjectURL(style.href);
  192. };
  193. } else {
  194. style = createStyleElement(options);
  195. update = applyToTag.bind(null, style);
  196. remove = function () {
  197. removeStyleElement(style);
  198. };
  199. }
  200. update(obj);
  201. return function updateStyle (newObj) {
  202. if (newObj) {
  203. if (
  204. newObj.css === obj.css &&
  205. newObj.media === obj.media &&
  206. newObj.sourceMap === obj.sourceMap
  207. ) {
  208. return;
  209. }
  210. update(obj = newObj);
  211. } else {
  212. remove();
  213. }
  214. };
  215. }
  216. var replaceText = (function () {
  217. var textStore = [];
  218. return function (index, replacement) {
  219. textStore[index] = replacement;
  220. return textStore.filter(Boolean).join('\n');
  221. };
  222. })();
  223. function applyToSingletonTag (style, index, remove, obj) {
  224. var css = remove ? "" : obj.css;
  225. if (style.styleSheet) {
  226. style.styleSheet.cssText = replaceText(index, css);
  227. } else {
  228. var cssNode = document.createTextNode(css);
  229. var childNodes = style.childNodes;
  230. if (childNodes[index]) style.removeChild(childNodes[index]);
  231. if (childNodes.length) {
  232. style.insertBefore(cssNode, childNodes[index]);
  233. } else {
  234. style.appendChild(cssNode);
  235. }
  236. }
  237. }
  238. function applyToTag (style, obj) {
  239. var css = obj.css;
  240. var media = obj.media;
  241. if(media) {
  242. style.setAttribute("media", media)
  243. }
  244. if(style.styleSheet) {
  245. style.styleSheet.cssText = css;
  246. } else {
  247. while(style.firstChild) {
  248. style.removeChild(style.firstChild);
  249. }
  250. style.appendChild(document.createTextNode(css));
  251. }
  252. }
  253. function updateLink (link, options, obj) {
  254. var css = obj.css;
  255. var sourceMap = obj.sourceMap;
  256. /*
  257. If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
  258. and there is no publicPath defined then lets turn convertToAbsoluteUrls
  259. on by default. Otherwise default to the convertToAbsoluteUrls option
  260. directly
  261. */
  262. var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;
  263. if (options.convertToAbsoluteUrls || autoFixUrls) {
  264. css = fixUrls(css);
  265. }
  266. if (sourceMap) {
  267. // http://stackoverflow.com/a/26603875
  268. css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
  269. }
  270. var blob = new Blob([css], { type: "text/css" });
  271. var oldSrc = link.href;
  272. link.href = URL.createObjectURL(blob);
  273. if(oldSrc) URL.revokeObjectURL(oldSrc);
  274. }