You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
10 KiB

4 years ago
  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 getTarget = function (target, parent) {
  22. if (parent){
  23. return parent.querySelector(target);
  24. }
  25. return document.querySelector(target);
  26. };
  27. var getElement = (function (fn) {
  28. var memo = {};
  29. return function(target, parent) {
  30. // If passing function in options, then use it for resolve "head" element.
  31. // Useful for Shadow Root style i.e
  32. // {
  33. // insertInto: function () { return document.querySelector("#foo").shadowRoot }
  34. // }
  35. if (typeof target === 'function') {
  36. return target();
  37. }
  38. if (typeof memo[target] === "undefined") {
  39. var styleTarget = getTarget.call(this, target, parent);
  40. // Special case to return head of iframe instead of iframe itself
  41. if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
  42. try {
  43. // This will throw an exception if access to iframe is blocked
  44. // due to cross-origin restrictions
  45. styleTarget = styleTarget.contentDocument.head;
  46. } catch(e) {
  47. styleTarget = null;
  48. }
  49. }
  50. memo[target] = styleTarget;
  51. }
  52. return memo[target]
  53. };
  54. })();
  55. var singleton = null;
  56. var singletonCounter = 0;
  57. var stylesInsertedAtTop = [];
  58. var fixUrls = require("./urls");
  59. module.exports = function(list, options) {
  60. if (typeof DEBUG !== "undefined" && DEBUG) {
  61. if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
  62. }
  63. options = options || {};
  64. options.attrs = typeof options.attrs === "object" ? options.attrs : {};
  65. // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  66. // tags it will allow on a page
  67. if (!options.singleton && typeof options.singleton !== "boolean") options.singleton = isOldIE();
  68. // By default, add <style> tags to the <head> element
  69. if (!options.insertInto) options.insertInto = "head";
  70. // By default, add <style> tags to the bottom of the target
  71. if (!options.insertAt) options.insertAt = "bottom";
  72. var styles = listToStyles(list, options);
  73. addStylesToDom(styles, options);
  74. return function update (newList) {
  75. var mayRemove = [];
  76. for (var i = 0; i < styles.length; i++) {
  77. var item = styles[i];
  78. var domStyle = stylesInDom[item.id];
  79. domStyle.refs--;
  80. mayRemove.push(domStyle);
  81. }
  82. if(newList) {
  83. var newStyles = listToStyles(newList, options);
  84. addStylesToDom(newStyles, options);
  85. }
  86. for (var i = 0; i < mayRemove.length; i++) {
  87. var domStyle = mayRemove[i];
  88. if(domStyle.refs === 0) {
  89. for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();
  90. delete stylesInDom[domStyle.id];
  91. }
  92. }
  93. };
  94. };
  95. function addStylesToDom (styles, options) {
  96. for (var i = 0; i < styles.length; i++) {
  97. var item = styles[i];
  98. var domStyle = stylesInDom[item.id];
  99. if(domStyle) {
  100. domStyle.refs++;
  101. for(var j = 0; j < domStyle.parts.length; j++) {
  102. domStyle.parts[j](item.parts[j]);
  103. }
  104. for(; j < item.parts.length; j++) {
  105. domStyle.parts.push(addStyle(item.parts[j], options));
  106. }
  107. } else {
  108. var parts = [];
  109. for(var j = 0; j < item.parts.length; j++) {
  110. parts.push(addStyle(item.parts[j], options));
  111. }
  112. stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
  113. }
  114. }
  115. }
  116. function listToStyles (list, options) {
  117. var styles = [];
  118. var newStyles = {};
  119. for (var i = 0; i < list.length; i++) {
  120. var item = list[i];
  121. var id = options.base ? item[0] + options.base : item[0];
  122. var css = item[1];
  123. var media = item[2];
  124. var sourceMap = item[3];
  125. var part = {css: css, media: media, sourceMap: sourceMap};
  126. if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
  127. else newStyles[id].parts.push(part);
  128. }
  129. return styles;
  130. }
  131. function insertStyleElement (options, style) {
  132. var target = getElement(options.insertInto)
  133. if (!target) {
  134. throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
  135. }
  136. var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];
  137. if (options.insertAt === "top") {
  138. if (!lastStyleElementInsertedAtTop) {
  139. target.insertBefore(style, target.firstChild);
  140. } else if (lastStyleElementInsertedAtTop.nextSibling) {
  141. target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
  142. } else {
  143. target.appendChild(style);
  144. }
  145. stylesInsertedAtTop.push(style);
  146. } else if (options.insertAt === "bottom") {
  147. target.appendChild(style);
  148. } else if (typeof options.insertAt === "object" && options.insertAt.before) {
  149. var nextSibling = getElement(options.insertAt.before, target);
  150. target.insertBefore(style, nextSibling);
  151. } else {
  152. throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
  153. }
  154. }
  155. function removeStyleElement (style) {
  156. if (style.parentNode === null) return false;
  157. style.parentNode.removeChild(style);
  158. var idx = stylesInsertedAtTop.indexOf(style);
  159. if(idx >= 0) {
  160. stylesInsertedAtTop.splice(idx, 1);
  161. }
  162. }
  163. function createStyleElement (options) {
  164. var style = document.createElement("style");
  165. if(options.attrs.type === undefined) {
  166. options.attrs.type = "text/css";
  167. }
  168. if(options.attrs.nonce === undefined) {
  169. var nonce = getNonce();
  170. if (nonce) {
  171. options.attrs.nonce = nonce;
  172. }
  173. }
  174. addAttrs(style, options.attrs);
  175. insertStyleElement(options, style);
  176. return style;
  177. }
  178. function createLinkElement (options) {
  179. var link = document.createElement("link");
  180. if(options.attrs.type === undefined) {
  181. options.attrs.type = "text/css";
  182. }
  183. options.attrs.rel = "stylesheet";
  184. addAttrs(link, options.attrs);
  185. insertStyleElement(options, link);
  186. return link;
  187. }
  188. function addAttrs (el, attrs) {
  189. Object.keys(attrs).forEach(function (key) {
  190. el.setAttribute(key, attrs[key]);
  191. });
  192. }
  193. function getNonce() {
  194. if (typeof __webpack_nonce__ === 'undefined') {
  195. return null;
  196. }
  197. return __webpack_nonce__;
  198. }
  199. function addStyle (obj, options) {
  200. var style, update, remove, result;
  201. // If a transform function was defined, run it on the css
  202. if (options.transform && obj.css) {
  203. result = typeof options.transform === 'function'
  204. ? options.transform(obj.css)
  205. : options.transform.default(obj.css);
  206. if (result) {
  207. // If transform returns a value, use that instead of the original css.
  208. // This allows running runtime transformations on the css.
  209. obj.css = result;
  210. } else {
  211. // If the transform function returns a falsy value, don't add this css.
  212. // This allows conditional loading of css
  213. return function() {
  214. // noop
  215. };
  216. }
  217. }
  218. if (options.singleton) {
  219. var styleIndex = singletonCounter++;
  220. style = singleton || (singleton = createStyleElement(options));
  221. update = applyToSingletonTag.bind(null, style, styleIndex, false);
  222. remove = applyToSingletonTag.bind(null, style, styleIndex, true);
  223. } else if (
  224. obj.sourceMap &&
  225. typeof URL === "function" &&
  226. typeof URL.createObjectURL === "function" &&
  227. typeof URL.revokeObjectURL === "function" &&
  228. typeof Blob === "function" &&
  229. typeof btoa === "function"
  230. ) {
  231. style = createLinkElement(options);
  232. update = updateLink.bind(null, style, options);
  233. remove = function () {
  234. removeStyleElement(style);
  235. if(style.href) URL.revokeObjectURL(style.href);
  236. };
  237. } else {
  238. style = createStyleElement(options);
  239. update = applyToTag.bind(null, style);
  240. remove = function () {
  241. removeStyleElement(style);
  242. };
  243. }
  244. update(obj);
  245. return function updateStyle (newObj) {
  246. if (newObj) {
  247. if (
  248. newObj.css === obj.css &&
  249. newObj.media === obj.media &&
  250. newObj.sourceMap === obj.sourceMap
  251. ) {
  252. return;
  253. }
  254. update(obj = newObj);
  255. } else {
  256. remove();
  257. }
  258. };
  259. }
  260. var replaceText = (function () {
  261. var textStore = [];
  262. return function (index, replacement) {
  263. textStore[index] = replacement;
  264. return textStore.filter(Boolean).join('\n');
  265. };
  266. })();
  267. function applyToSingletonTag (style, index, remove, obj) {
  268. var css = remove ? "" : obj.css;
  269. if (style.styleSheet) {
  270. style.styleSheet.cssText = replaceText(index, css);
  271. } else {
  272. var cssNode = document.createTextNode(css);
  273. var childNodes = style.childNodes;
  274. if (childNodes[index]) style.removeChild(childNodes[index]);
  275. if (childNodes.length) {
  276. style.insertBefore(cssNode, childNodes[index]);
  277. } else {
  278. style.appendChild(cssNode);
  279. }
  280. }
  281. }
  282. function applyToTag (style, obj) {
  283. var css = obj.css;
  284. var media = obj.media;
  285. if(media) {
  286. style.setAttribute("media", media)
  287. }
  288. if(style.styleSheet) {
  289. style.styleSheet.cssText = css;
  290. } else {
  291. while(style.firstChild) {
  292. style.removeChild(style.firstChild);
  293. }
  294. style.appendChild(document.createTextNode(css));
  295. }
  296. }
  297. function updateLink (link, options, obj) {
  298. var css = obj.css;
  299. var sourceMap = obj.sourceMap;
  300. /*
  301. If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
  302. and there is no publicPath defined then lets turn convertToAbsoluteUrls
  303. on by default. Otherwise default to the convertToAbsoluteUrls option
  304. directly
  305. */
  306. var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;
  307. if (options.convertToAbsoluteUrls || autoFixUrls) {
  308. css = fixUrls(css);
  309. }
  310. if (sourceMap) {
  311. // http://stackoverflow.com/a/26603875
  312. css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
  313. }
  314. var blob = new Blob([css], { type: "text/css" });
  315. var oldSrc = link.href;
  316. link.href = URL.createObjectURL(blob);
  317. if(oldSrc) URL.revokeObjectURL(oldSrc);
  318. }