addStyles.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var stylesInDom = {},
  6. 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. isOldIE = memoize(function() {
  14. return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
  15. }),
  16. getHeadElement = memoize(function () {
  17. return document.head || document.getElementsByTagName("head")[0];
  18. }),
  19. singletonElement = null,
  20. singletonCounter = 0,
  21. styleElementsInsertedAtTop = [];
  22. module.exports = function(list, options) {
  23. if(typeof DEBUG !== "undefined" && DEBUG) {
  24. if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
  25. }
  26. options = options || {};
  27. // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  28. // tags it will allow on a page
  29. if (typeof options.singleton === "undefined") options.singleton = isOldIE();
  30. // By default, add <style> tags to the bottom of <head>.
  31. if (typeof options.insertAt === "undefined") options.insertAt = "bottom";
  32. var styles = listToStyles(list);
  33. addStylesToDom(styles, options);
  34. return function update(newList) {
  35. var mayRemove = [];
  36. for(var i = 0; i < styles.length; i++) {
  37. var item = styles[i];
  38. var domStyle = stylesInDom[item.id];
  39. domStyle.refs--;
  40. mayRemove.push(domStyle);
  41. }
  42. if(newList) {
  43. var newStyles = listToStyles(newList);
  44. addStylesToDom(newStyles, options);
  45. }
  46. for(var i = 0; i < mayRemove.length; i++) {
  47. var domStyle = mayRemove[i];
  48. if(domStyle.refs === 0) {
  49. for(var j = 0; j < domStyle.parts.length; j++)
  50. domStyle.parts[j]();
  51. delete stylesInDom[domStyle.id];
  52. }
  53. }
  54. };
  55. }
  56. function addStylesToDom(styles, options) {
  57. for(var i = 0; i < styles.length; i++) {
  58. var item = styles[i];
  59. var domStyle = stylesInDom[item.id];
  60. if(domStyle) {
  61. domStyle.refs++;
  62. for(var j = 0; j < domStyle.parts.length; j++) {
  63. domStyle.parts[j](item.parts[j]);
  64. }
  65. for(; j < item.parts.length; j++) {
  66. domStyle.parts.push(addStyle(item.parts[j], options));
  67. }
  68. } else {
  69. var parts = [];
  70. for(var j = 0; j < item.parts.length; j++) {
  71. parts.push(addStyle(item.parts[j], options));
  72. }
  73. stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
  74. }
  75. }
  76. }
  77. function listToStyles(list) {
  78. var styles = [];
  79. var newStyles = {};
  80. for(var i = 0; i < list.length; i++) {
  81. var item = list[i];
  82. var id = item[0];
  83. var css = item[1];
  84. var media = item[2];
  85. var sourceMap = item[3];
  86. var part = {css: css, media: media, sourceMap: sourceMap};
  87. if(!newStyles[id])
  88. styles.push(newStyles[id] = {id: id, parts: [part]});
  89. else
  90. newStyles[id].parts.push(part);
  91. }
  92. return styles;
  93. }
  94. function insertStyleElement(options, styleElement) {
  95. var head = getHeadElement();
  96. var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
  97. if (options.insertAt === "top") {
  98. if(!lastStyleElementInsertedAtTop) {
  99. head.insertBefore(styleElement, head.firstChild);
  100. } else if(lastStyleElementInsertedAtTop.nextSibling) {
  101. head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
  102. } else {
  103. head.appendChild(styleElement);
  104. }
  105. styleElementsInsertedAtTop.push(styleElement);
  106. } else if (options.insertAt === "bottom") {
  107. head.appendChild(styleElement);
  108. } else {
  109. throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
  110. }
  111. }
  112. function removeStyleElement(styleElement) {
  113. styleElement.parentNode.removeChild(styleElement);
  114. var idx = styleElementsInsertedAtTop.indexOf(styleElement);
  115. if(idx >= 0) {
  116. styleElementsInsertedAtTop.splice(idx, 1);
  117. }
  118. }
  119. function createStyleElement(options) {
  120. var styleElement = document.createElement("style");
  121. styleElement.type = "text/css";
  122. insertStyleElement(options, styleElement);
  123. return styleElement;
  124. }
  125. function createLinkElement(options) {
  126. var linkElement = document.createElement("link");
  127. linkElement.rel = "stylesheet";
  128. insertStyleElement(options, linkElement);
  129. return linkElement;
  130. }
  131. function addStyle(obj, options) {
  132. var styleElement, update, remove;
  133. if (options.singleton) {
  134. var styleIndex = singletonCounter++;
  135. styleElement = singletonElement || (singletonElement = createStyleElement(options));
  136. update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
  137. remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
  138. } else if(obj.sourceMap &&
  139. typeof URL === "function" &&
  140. typeof URL.createObjectURL === "function" &&
  141. typeof URL.revokeObjectURL === "function" &&
  142. typeof Blob === "function" &&
  143. typeof btoa === "function") {
  144. styleElement = createLinkElement(options);
  145. update = updateLink.bind(null, styleElement);
  146. remove = function() {
  147. removeStyleElement(styleElement);
  148. if(styleElement.href)
  149. URL.revokeObjectURL(styleElement.href);
  150. };
  151. } else {
  152. styleElement = createStyleElement(options);
  153. update = applyToTag.bind(null, styleElement);
  154. remove = function() {
  155. removeStyleElement(styleElement);
  156. };
  157. }
  158. update(obj);
  159. return function updateStyle(newObj) {
  160. if(newObj) {
  161. if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
  162. return;
  163. update(obj = newObj);
  164. } else {
  165. remove();
  166. }
  167. };
  168. }
  169. var replaceText = (function () {
  170. var textStore = [];
  171. return function (index, replacement) {
  172. textStore[index] = replacement;
  173. return textStore.filter(Boolean).join('\n');
  174. };
  175. })();
  176. function applyToSingletonTag(styleElement, index, remove, obj) {
  177. var css = remove ? "" : obj.css;
  178. if (styleElement.styleSheet) {
  179. styleElement.styleSheet.cssText = replaceText(index, css);
  180. } else {
  181. var cssNode = document.createTextNode(css);
  182. var childNodes = styleElement.childNodes;
  183. if (childNodes[index]) styleElement.removeChild(childNodes[index]);
  184. if (childNodes.length) {
  185. styleElement.insertBefore(cssNode, childNodes[index]);
  186. } else {
  187. styleElement.appendChild(cssNode);
  188. }
  189. }
  190. }
  191. function applyToTag(styleElement, obj) {
  192. var css = obj.css;
  193. var media = obj.media;
  194. if(media) {
  195. styleElement.setAttribute("media", media)
  196. }
  197. if(styleElement.styleSheet) {
  198. styleElement.styleSheet.cssText = css;
  199. } else {
  200. while(styleElement.firstChild) {
  201. styleElement.removeChild(styleElement.firstChild);
  202. }
  203. styleElement.appendChild(document.createTextNode(css));
  204. }
  205. }
  206. function updateLink(linkElement, obj) {
  207. var css = obj.css;
  208. var sourceMap = obj.sourceMap;
  209. if(sourceMap) {
  210. // http://stackoverflow.com/a/26603875
  211. css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
  212. }
  213. var blob = new Blob([css], { type: "text/css" });
  214. var oldSrc = linkElement.href;
  215. linkElement.href = URL.createObjectURL(blob);
  216. if(oldSrc)
  217. URL.revokeObjectURL(oldSrc);
  218. }