Lightweight CSS Framework for Building Apps and Websites https://crispy-css.com
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.

230 lines
9.9 KiB

5 years ago
  1. /*!
  2. * @copyright Copyright (c) 2017 IcoMoon.io
  3. * @license Licensed under MIT license
  4. * See https://github.com/Keyamoon/svgxuse
  5. * @version 1.2.6
  6. */
  7. /*jslint browser: true */
  8. /*global XDomainRequest, MutationObserver, window */
  9. (function () {
  10. "use strict";
  11. if (typeof window !== "undefined" && window.addEventListener) {
  12. var cache = Object.create(null); // holds xhr objects to prevent multiple requests
  13. var checkUseElems;
  14. var tid; // timeout id
  15. var debouncedCheck = function () {
  16. clearTimeout(tid);
  17. tid = setTimeout(checkUseElems, 100);
  18. };
  19. var unobserveChanges = function () {
  20. return;
  21. };
  22. var observeChanges = function () {
  23. var observer;
  24. window.addEventListener("resize", debouncedCheck, false);
  25. window.addEventListener("orientationchange", debouncedCheck, false);
  26. if (window.MutationObserver) {
  27. observer = new MutationObserver(debouncedCheck);
  28. observer.observe(document.documentElement, {
  29. childList: true,
  30. subtree: true,
  31. attributes: true
  32. });
  33. unobserveChanges = function () {
  34. try {
  35. observer.disconnect();
  36. window.removeEventListener("resize", debouncedCheck, false);
  37. window.removeEventListener("orientationchange", debouncedCheck, false);
  38. } catch (ignore) {}
  39. };
  40. } else {
  41. document.documentElement.addEventListener("DOMSubtreeModified", debouncedCheck, false);
  42. unobserveChanges = function () {
  43. document.documentElement.removeEventListener("DOMSubtreeModified", debouncedCheck, false);
  44. window.removeEventListener("resize", debouncedCheck, false);
  45. window.removeEventListener("orientationchange", debouncedCheck, false);
  46. };
  47. }
  48. };
  49. var createRequest = function (url) {
  50. // In IE 9, cross origin requests can only be sent using XDomainRequest.
  51. // XDomainRequest would fail if CORS headers are not set.
  52. // Therefore, XDomainRequest should only be used with cross origin requests.
  53. function getOrigin(loc) {
  54. var a;
  55. if (loc.protocol !== undefined) {
  56. a = loc;
  57. } else {
  58. a = document.createElement("a");
  59. a.href = loc;
  60. }
  61. return a.protocol.replace(/:/g, "") + a.host;
  62. }
  63. var Request;
  64. var origin;
  65. var origin2;
  66. if (window.XMLHttpRequest) {
  67. Request = new XMLHttpRequest();
  68. origin = getOrigin(location);
  69. origin2 = getOrigin(url);
  70. if (Request.withCredentials === undefined && origin2 !== "" && origin2 !== origin) {
  71. Request = XDomainRequest || undefined;
  72. } else {
  73. Request = XMLHttpRequest;
  74. }
  75. }
  76. return Request;
  77. };
  78. var xlinkNS = "http://www.w3.org/1999/xlink";
  79. checkUseElems = function () {
  80. var base;
  81. var bcr;
  82. var fallback = ""; // optional fallback URL in case no base path to SVG file was given and no symbol definition was found.
  83. var hash;
  84. var href;
  85. var i;
  86. var inProgressCount = 0;
  87. var isHidden;
  88. var Request;
  89. var url;
  90. var uses;
  91. var xhr;
  92. function observeIfDone() {
  93. // If done with making changes, start watching for chagnes in DOM again
  94. inProgressCount -= 1;
  95. if (inProgressCount === 0) { // if all xhrs were resolved
  96. unobserveChanges(); // make sure to remove old handlers
  97. observeChanges(); // watch for changes to DOM
  98. }
  99. }
  100. function attrUpdateFunc(spec) {
  101. return function () {
  102. if (cache[spec.base] !== true) {
  103. spec.useEl.setAttributeNS(xlinkNS, "xlink:href", "#" + spec.hash);
  104. if (spec.useEl.hasAttribute("href")) {
  105. spec.useEl.setAttribute("href", "#" + spec.hash);
  106. }
  107. }
  108. };
  109. }
  110. function onloadFunc(xhr) {
  111. return function () {
  112. var body = document.body;
  113. var x = document.createElement("x");
  114. var svg;
  115. xhr.onload = null;
  116. x.innerHTML = xhr.responseText;
  117. svg = x.getElementsByTagName("svg")[0];
  118. if (svg) {
  119. svg.setAttribute("aria-hidden", "true");
  120. svg.style.position = "absolute";
  121. svg.style.width = 0;
  122. svg.style.height = 0;
  123. svg.style.overflow = "hidden";
  124. body.insertBefore(svg, body.firstChild);
  125. }
  126. observeIfDone();
  127. };
  128. }
  129. function onErrorTimeout(xhr) {
  130. return function () {
  131. xhr.onerror = null;
  132. xhr.ontimeout = null;
  133. observeIfDone();
  134. };
  135. }
  136. unobserveChanges(); // stop watching for changes to DOM
  137. // find all use elements
  138. uses = document.getElementsByTagName("use");
  139. for (i = 0; i < uses.length; i += 1) {
  140. try {
  141. bcr = uses[i].getBoundingClientRect();
  142. } catch (ignore) {
  143. // failed to get bounding rectangle of the use element
  144. bcr = false;
  145. }
  146. href = uses[i].getAttribute("href")
  147. || uses[i].getAttributeNS(xlinkNS, "href")
  148. || uses[i].getAttribute("xlink:href");
  149. if (href && href.split) {
  150. url = href.split("#");
  151. } else {
  152. url = ["", ""];
  153. }
  154. base = url[0];
  155. hash = url[1];
  156. isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0;
  157. if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) {
  158. // the use element is empty
  159. // if there is a reference to an external SVG, try to fetch it
  160. // use the optional fallback URL if there is no reference to an external SVG
  161. if (fallback && !base.length && hash && !document.getElementById(hash)) {
  162. base = fallback;
  163. }
  164. if (uses[i].hasAttribute("href")) {
  165. uses[i].setAttributeNS(xlinkNS, "xlink:href", href);
  166. }
  167. if (base.length) {
  168. // schedule updating xlink:href
  169. xhr = cache[base];
  170. if (xhr !== true) {
  171. // true signifies that prepending the SVG was not required
  172. setTimeout(attrUpdateFunc({
  173. useEl: uses[i],
  174. base: base,
  175. hash: hash
  176. }), 0);
  177. }
  178. if (xhr === undefined) {
  179. Request = createRequest(base);
  180. if (Request !== undefined) {
  181. xhr = new Request();
  182. cache[base] = xhr;
  183. xhr.onload = onloadFunc(xhr);
  184. xhr.onerror = onErrorTimeout(xhr);
  185. xhr.ontimeout = onErrorTimeout(xhr);
  186. xhr.open("GET", base);
  187. xhr.send();
  188. inProgressCount += 1;
  189. }
  190. }
  191. }
  192. } else {
  193. if (!isHidden) {
  194. if (cache[base] === undefined) {
  195. // remember this URL if the use element was not empty and no request was sent
  196. cache[base] = true;
  197. } else if (cache[base].onload) {
  198. // if it turns out that prepending the SVG is not necessary,
  199. // abort the in-progress xhr.
  200. cache[base].abort();
  201. delete cache[base].onload;
  202. cache[base] = true;
  203. }
  204. } else if (base.length && cache[base]) {
  205. setTimeout(attrUpdateFunc({
  206. useEl: uses[i],
  207. base: base,
  208. hash: hash
  209. }), 0);
  210. }
  211. }
  212. }
  213. uses = "";
  214. inProgressCount += 1;
  215. observeIfDone();
  216. };
  217. var winLoad;
  218. winLoad = function () {
  219. window.removeEventListener("load", winLoad, false); // to prevent memory leaks
  220. tid = setTimeout(checkUseElems, 0);
  221. };
  222. if (document.readyState !== "complete") {
  223. // The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty.
  224. window.addEventListener("load", winLoad, false);
  225. } else {
  226. // No need to add a listener if the document is already loaded, initialize immediately.
  227. winLoad();
  228. }
  229. }
  230. }());