| 1 | | -/** |
| 2 | | - * SVGInject - Version 1.2.3 |
| 3 | | - * A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM. |
| 4 | | - * |
| 5 | | - * https://github.com/iconfu/svg-inject |
| 6 | | - * |
| 7 | | - * Copyright (c) 2018 INCORS, the creators of iconfu.com |
| 8 | | - * @license MIT License - https://github.com/iconfu/svg-inject/blob/master/LICENSE |
| 9 | | - */ |
| 10 | | - |
| 11 | | -(function(window, document) { |
| 12 | | - // constants for better minification |
| 13 | | - var _CREATE_ELEMENT_ = 'createElement'; |
| 14 | | - var _GET_ELEMENTS_BY_TAG_NAME_ = 'getElementsByTagName'; |
| 15 | | - var _LENGTH_ = 'length'; |
| 16 | | - var _STYLE_ = 'style'; |
| 17 | | - var _TITLE_ = 'title'; |
| 18 | | - var _UNDEFINED_ = 'undefined'; |
| 19 | | - var _SET_ATTRIBUTE_ = 'setAttribute'; |
| 20 | | - var _GET_ATTRIBUTE_ = 'getAttribute'; |
| 21 | | - |
| 22 | | - var NULL = null; |
| 23 | | - |
| 24 | | - // constants |
| 25 | | - var __SVGINJECT = '__svgInject'; |
| 26 | | - var ID_SUFFIX = '--inject-'; |
| 27 | | - var ID_SUFFIX_REGEX = new RegExp(ID_SUFFIX + '\\d+', "g"); |
| 28 | | - var LOAD_FAIL = 'LOAD_FAIL'; |
| 29 | | - var SVG_NOT_SUPPORTED = 'SVG_NOT_SUPPORTED'; |
| 30 | | - var SVG_INVALID = 'SVG_INVALID'; |
| 31 | | - var ATTRIBUTE_EXCLUSION_NAMES = ['src', 'alt', 'onload', 'onerror']; |
| 32 | | - var A_ELEMENT = document[_CREATE_ELEMENT_]('a'); |
| 33 | | - var IS_SVG_SUPPORTED = typeof SVGRect != _UNDEFINED_; |
| 34 | | - var DEFAULT_OPTIONS = { |
| 35 | | - useCache: true, |
| 36 | | - copyAttributes: true, |
| 37 | | - makeIdsUnique: true |
| 38 | | - }; |
| 39 | | - // Map of IRI referenceable tag names to properties that can reference them. This is defined in |
| 40 | | - // https://www.w3.org/TR/SVG11/linking.html#processingIRI |
| 41 | | - var IRI_TAG_PROPERTIES_MAP = { |
| 42 | | - clipPath: ['clip-path'], |
| 43 | | - 'color-profile': NULL, |
| 44 | | - cursor: NULL, |
| 45 | | - filter: NULL, |
| 46 | | - linearGradient: ['fill', 'stroke'], |
| 47 | | - marker: ['marker', 'marker-end', 'marker-mid', 'marker-start'], |
| 48 | | - mask: NULL, |
| 49 | | - pattern: ['fill', 'stroke'], |
| 50 | | - radialGradient: ['fill', 'stroke'] |
| 51 | | - }; |
| 52 | | - var INJECTED = 1; |
| 53 | | - var FAIL = 2; |
| 54 | | - |
| 55 | | - var uniqueIdCounter = 1; |
| 56 | | - var xmlSerializer; |
| 57 | | - var domParser; |
| 58 | | - |
| 59 | | - |
| 60 | | - // creates an SVG document from an SVG string |
| 61 | | - function svgStringToSvgDoc(svgStr) { |
| 62 | | - domParser = domParser || new DOMParser(); |
| 63 | | - return domParser.parseFromString(svgStr, 'text/xml'); |
| 64 | | - } |
| 65 | | - |
| 66 | | - |
| 67 | | - // searializes an SVG element to an SVG string |
| 68 | | - function svgElemToSvgString(svgElement) { |
| 69 | | - xmlSerializer = xmlSerializer || new XMLSerializer(); |
| 70 | | - return xmlSerializer.serializeToString(svgElement); |
| 71 | | - } |
| 72 | | - |
| 73 | | - |
| 74 | | - // Returns the absolute url for the specified url |
| 75 | | - function getAbsoluteUrl(url) { |
| 76 | | - A_ELEMENT.href = url; |
| 77 | | - return A_ELEMENT.href; |
| 78 | | - } |
| 79 | | - |
| 80 | | - |
| 81 | | - // Load svg with an XHR request |
| 82 | | - function loadSvg(url, callback, errorCallback) { |
| 83 | | - if (url) { |
| 84 | | - var req = new XMLHttpRequest(); |
| 85 | | - req.onreadystatechange = function() { |
| 86 | | - if (req.readyState == 4) { |
| 87 | | - // readyState is DONE |
| 88 | | - var status = req.status; |
| 89 | | - if (status == 200) { |
| 90 | | - // request status is OK |
| 91 | | - callback(req.responseXML, req.responseText.trim()); |
| 92 | | - } else if (status >= 400) { |
| 93 | | - // request status is error (4xx or 5xx) |
| 94 | | - errorCallback(); |
| 95 | | - } else if (status == 0) { |
| 96 | | - // request status 0 can indicate a failed cross-domain call |
| 97 | | - errorCallback(); |
| 98 | | - } |
| 99 | | - } |
| 100 | | - }; |
| 101 | | - req.open('GET', url, true); |
| 102 | | - req.send(); |
| 103 | | - } |
| 104 | | - } |
| 105 | | - |
| 106 | | - |
| 107 | | - // Copy attributes from img element to svg element |
| 108 | | - function copyAttributes(imgElem, svgElem) { |
| 109 | | - var attribute; |
| 110 | | - var attributeName; |
| 111 | | - var attributeValue; |
| 112 | | - var attributes = imgElem.attributes; |
| 113 | | - for (var i = 0; i < attributes[_LENGTH_]; i++) { |
| 114 | | - attribute = attributes[i]; |
| 115 | | - attributeName = attribute.name; |
| 116 | | - // Only copy attributes not explicitly excluded from copying |
| 117 | | - if (ATTRIBUTE_EXCLUSION_NAMES.indexOf(attributeName) == -1) { |
| 118 | | - attributeValue = attribute.value; |
| 119 | | - // If img attribute is "title", insert a title element into SVG element |
| 120 | | - if (attributeName == _TITLE_) { |
| 121 | | - var titleElem; |
| 122 | | - var firstElementChild = svgElem.firstElementChild; |
| 123 | | - if (firstElementChild && firstElementChild.localName.toLowerCase() == _TITLE_) { |
| 124 | | - // If the SVG element's first child is a title element, keep it as the title element |
| 125 | | - titleElem = firstElementChild; |
| 126 | | - } else { |
| 127 | | - // If the SVG element's first child element is not a title element, create a new title |
| 128 | | - // ele,emt and set it as the first child |
| 129 | | - titleElem = document[_CREATE_ELEMENT_ + 'NS']('http://www.w3.org/2000/svg', _TITLE_); |
| 130 | | - svgElem.insertBefore(titleElem, firstElementChild); |
| 131 | | - } |
| 132 | | - // Set new title content |
| 133 | | - titleElem.textContent = attributeValue; |
| 134 | | - } else { |
| 135 | | - // Set img attribute to svg element |
| 136 | | - svgElem[_SET_ATTRIBUTE_](attributeName, attributeValue); |
| 137 | | - } |
| 138 | | - } |
| 139 | | - } |
| 140 | | - } |
| 141 | | - |
| 142 | | - |
| 143 | | - // This function appends a suffix to IDs of referenced elements in the <defs> in order to to avoid ID collision |
| 144 | | - // between multiple injected SVGs. The suffix has the form "--inject-X", where X is a running number which is |
| 145 | | - // incremented with each injection. References to the IDs are adjusted accordingly. |
| 146 | | - // We assume tha all IDs within the injected SVG are unique, therefore the same suffix can be used for all IDs of one |
| 147 | | - // injected SVG. |
| 148 | | - // If the onlyReferenced argument is set to true, only those IDs will be made unique that are referenced from within the SVG |
| 149 | | - function makeIdsUnique(svgElem, onlyReferenced) { |
| 150 | | - var idSuffix = ID_SUFFIX + uniqueIdCounter++; |
| 151 | | - // Regular expression for functional notations of an IRI references. This will find occurences in the form |
| 152 | | - // url(#anyId) or url("#anyId") (for Internet Explorer) and capture the referenced ID |
| 153 | | - var funcIriRegex = /url\("?#([a-zA-Z][\w:.-]*)"?\)/g; |
| 154 | | - // Get all elements with an ID. The SVG spec recommends to put referenced elements inside <defs> elements, but |
| 155 | | - // this is not a requirement, therefore we have to search for IDs in the whole SVG. |
| 156 | | - var idElements = svgElem.querySelectorAll('[id]'); |
| 157 | | - var idElem; |
| 158 | | - // An object containing referenced IDs as keys is used if only referenced IDs should be uniquified. |
| 159 | | - // If this object does not exist, all IDs will be uniquified. |
| 160 | | - var referencedIds = onlyReferenced ? [] : NULL; |
| 161 | | - var tagName; |
| 162 | | - var iriTagNames = {}; |
| 163 | | - var iriProperties = []; |
| 164 | | - var changed = false; |
| 165 | | - var i, j; |
| 166 | | - |
| 167 | | - if (idElements[_LENGTH_]) { |
| 168 | | - // Make all IDs unique by adding the ID suffix and collect all encountered tag names |
| 169 | | - // that are IRI referenceable from properities. |
| 170 | | - for (i = 0; i < idElements[_LENGTH_]; i++) { |
| 171 | | - tagName = idElements[i].localName; // Use non-namespaced tag name |
| 172 | | - // Make ID unique if tag name is IRI referenceable |
| 173 | | - if (tagName in IRI_TAG_PROPERTIES_MAP) { |
| 174 | | - iriTagNames[tagName] = 1; |
| 175 | | - } |
| 176 | | - } |
| 177 | | - // Get all properties that are mapped to the found IRI referenceable tags |
| 178 | | - for (tagName in iriTagNames) { |
| 179 | | - (IRI_TAG_PROPERTIES_MAP[tagName] || [tagName]).forEach(function (mappedProperty) { |
| 180 | | - // Add mapped properties to array of iri referencing properties. |
| 181 | | - // Use linear search here because the number of possible entries is very small (maximum 11) |
| 182 | | - if (iriProperties.indexOf(mappedProperty) < 0) { |
| 183 | | - iriProperties.push(mappedProperty); |
| 184 | | - } |
| 185 | | - }); |
| 186 | | - } |
| 187 | | - if (iriProperties[_LENGTH_]) { |
| 188 | | - // Add "style" to properties, because it may contain references in the form 'style="fill:url(#myFill)"' |
| 189 | | - iriProperties.push(_STYLE_); |
| 190 | | - } |
| 191 | | - // Run through all elements of the SVG and replace IDs in references. |
| 192 | | - // To get all descending elements, getElementsByTagName('*') seems to perform faster than querySelectorAll('*'). |
| 193 | | - // Since svgElem.getElementsByTagName('*') does not return the svg element itself, we have to handle it separately. |
| 194 | | - var descElements = svgElem[_GET_ELEMENTS_BY_TAG_NAME_]('*'); |
| 195 | | - var element = svgElem; |
| 196 | | - var propertyName; |
| 197 | | - var value; |
| 198 | | - var newValue; |
| 199 | | - for (i = -1; element != NULL;) { |
| 200 | | - if (element.localName == _STYLE_) { |
| 201 | | - // If element is a style element, replace IDs in all occurences of "url(#anyId)" in text content |
| 202 | | - value = element.textContent; |
| 203 | | - newValue = value && value.replace(funcIriRegex, function(match, id) { |
| 204 | | - if (referencedIds) { |
| 205 | | - referencedIds[id] = 1; |
| 206 | | - } |
| 207 | | - return 'url(#' + id + idSuffix + ')'; |
| 208 | | - }); |
| 209 | | - if (newValue !== value) { |
| 210 | | - element.textContent = newValue; |
| 211 | | - } |
| 212 | | - } else if (element.hasAttributes()) { |
| 213 | | - // Run through all property names for which IDs were found |
| 214 | | - for (j = 0; j < iriProperties[_LENGTH_]; j++) { |
| 215 | | - propertyName = iriProperties[j]; |
| 216 | | - value = element[_GET_ATTRIBUTE_](propertyName); |
| 217 | | - newValue = value && value.replace(funcIriRegex, function(match, id) { |
| 218 | | - if (referencedIds) { |
| 219 | | - referencedIds[id] = 1; |
| 220 | | - } |
| 221 | | - return 'url(#' + id + idSuffix + ')'; |
| 222 | | - }); |
| 223 | | - if (newValue !== value) { |
| 224 | | - element[_SET_ATTRIBUTE_](propertyName, newValue); |
| 225 | | - } |
| 226 | | - } |
| 227 | | - // Replace IDs in xlink:ref and href attributes |
| 228 | | - ['xlink:href', 'href'].forEach(function(refAttrName) { |
| 229 | | - var iri = element[_GET_ATTRIBUTE_](refAttrName); |
| 230 | | - if (/^\s*#/.test(iri)) { // Check if iri is non-null and internal reference |
| 231 | | - iri = iri.trim(); |
| 232 | | - element[_SET_ATTRIBUTE_](refAttrName, iri + idSuffix); |
| 233 | | - if (referencedIds) { |
| 234 | | - // Add ID to referenced IDs |
| 235 | | - referencedIds[iri.substring(1)] = 1; |
| 236 | | - } |
| 237 | | - } |
| 238 | | - }); |
| 239 | | - } |
| 240 | | - element = descElements[++i]; |
| 241 | | - } |
| 242 | | - for (i = 0; i < idElements[_LENGTH_]; i++) { |
| 243 | | - idElem = idElements[i]; |
| 244 | | - // If set of referenced IDs exists, make only referenced IDs unique, |
| 245 | | - // otherwise make all IDs unique. |
| 246 | | - if (!referencedIds || referencedIds[idElem.id]) { |
| 247 | | - // Add suffix to element's ID |
| 248 | | - idElem.id += idSuffix; |
| 249 | | - changed = true; |
| 250 | | - } |
| 251 | | - } |
| 252 | | - } |
| 253 | | - // return true if SVG element has changed |
| 254 | | - return changed; |
| 255 | | - } |
| 256 | | - |
| 257 | | - |
| 258 | | - // For cached SVGs the IDs are made unique by simply replacing the already inserted unique IDs with a |
| 259 | | - // higher ID counter. This is much more performant than a call to makeIdsUnique(). |
| 260 | | - function makeIdsUniqueCached(svgString) { |
| 261 | | - return svgString.replace(ID_SUFFIX_REGEX, ID_SUFFIX + uniqueIdCounter++); |
| 262 | | - } |
| 263 | | - |
| 264 | | - |
| 265 | | - // Inject SVG by replacing the img element with the SVG element in the DOM |
| 266 | | - function inject(imgElem, svgElem, absUrl, options) { |
| 267 | | - if (svgElem) { |
| 268 | | - svgElem[_SET_ATTRIBUTE_]('data-inject-url', absUrl); |
| 269 | | - var parentNode = imgElem.parentNode; |
| 270 | | - if (parentNode) { |
| 271 | | - if (options.copyAttributes) { |
| 272 | | - copyAttributes(imgElem, svgElem); |
| 273 | | - } |
| 274 | | - // Invoke beforeInject hook if set |
| 275 | | - var beforeInject = options.beforeInject; |
| 276 | | - var injectElem = (beforeInject && beforeInject(imgElem, svgElem)) || svgElem; |
| 277 | | - // Replace img element with new element. This is the actual injection. |
| 278 | | - parentNode.replaceChild(injectElem, imgElem); |
| 279 | | - // Mark img element as injected |
| 280 | | - imgElem[__SVGINJECT] = INJECTED; |
| 281 | | - removeOnLoadAttribute(imgElem); |
| 282 | | - // Invoke afterInject hook if set |
| 283 | | - var afterInject = options.afterInject; |
| 284 | | - if (afterInject) { |
| 285 | | - afterInject(imgElem, injectElem); |
| 286 | | - } |
| 287 | | - } |
| 288 | | - } else { |
| 289 | | - svgInvalid(imgElem, options); |
| 290 | | - } |
| 291 | | - } |
| 292 | | - |
| 293 | | - |
| 294 | | - // Merges any number of options objects into a new object |
| 295 | | - function mergeOptions() { |
| 296 | | - var mergedOptions = {}; |
| 297 | | - var args = arguments; |
| 298 | | - // Iterate over all specified options objects and add all properties to the new options object |
| 299 | | - for (var i = 0; i < args[_LENGTH_]; i++) { |
| 300 | | - var argument = args[i]; |
| 301 | | - for (var key in argument) { |
| 302 | | - if (argument.hasOwnProperty(key)) { |
| 303 | | - mergedOptions[key] = argument[key]; |
| 304 | | - } |
| 305 | | - } |
| 306 | | - } |
| 307 | | - return mergedOptions; |
| 308 | | - } |
| 309 | | - |
| 310 | | - |
| 311 | | - // Adds the specified CSS to the document's <head> element |
| 312 | | - function addStyleToHead(css) { |
| 313 | | - var head = document[_GET_ELEMENTS_BY_TAG_NAME_]('head')[0]; |
| 314 | | - if (head) { |
| 315 | | - var style = document[_CREATE_ELEMENT_](_STYLE_); |
| 316 | | - style.type = 'text/css'; |
| 317 | | - style.appendChild(document.createTextNode(css)); |
| 318 | | - head.appendChild(style); |
| 319 | | - } |
| 320 | | - } |
| 321 | | - |
| 322 | | - |
| 323 | | - // Builds an SVG element from the specified SVG string |
| 324 | | - function buildSvgElement(svgStr, verify) { |
| 325 | | - if (verify) { |
| 326 | | - var svgDoc; |
| 327 | | - try { |
| 328 | | - // Parse the SVG string with DOMParser |
| 329 | | - svgDoc = svgStringToSvgDoc(svgStr); |
| 330 | | - } catch(e) { |
| 331 | | - return NULL; |
| 332 | | - } |
| 333 | | - if (svgDoc[_GET_ELEMENTS_BY_TAG_NAME_]('parsererror')[_LENGTH_]) { |
| 334 | | - // DOMParser does not throw an exception, but instead puts parsererror tags in the document |
| 335 | | - return NULL; |
| 336 | | - } |
| 337 | | - return svgDoc.documentElement; |
| 338 | | - } else { |
| 339 | | - var div = document.createElement('div'); |
| 340 | | - div.innerHTML = svgStr; |
| 341 | | - return div.firstElementChild; |
| 342 | | - } |
| 343 | | - } |
| 344 | | - |
| 345 | | - |
| 346 | | - function removeOnLoadAttribute(imgElem) { |
| 347 | | - // Remove the onload attribute. Should only be used to remove the unstyled image flash protection and |
| 348 | | - // make the element visible, not for removing the event listener. |
| 349 | | - imgElem.removeAttribute('onload'); |
| 350 | | - } |
| 351 | | - |
| 352 | | - |
| 353 | | - function errorMessage(msg) { |
| 354 | | - console.error('SVGInject: ' + msg); |
| 355 | | - } |
| 356 | | - |
| 357 | | - |
| 358 | | - function fail(imgElem, status, options) { |
| 359 | | - imgElem[__SVGINJECT] = FAIL; |
| 360 | | - if (options.onFail) { |
| 361 | | - options.onFail(imgElem, status); |
| 362 | | - } else { |
| 363 | | - errorMessage(status); |
| 364 | | - } |
| 365 | | - } |
| 366 | | - |
| 367 | | - |
| 368 | | - function svgInvalid(imgElem, options) { |
| 369 | | - removeOnLoadAttribute(imgElem); |
| 370 | | - fail(imgElem, SVG_INVALID, options); |
| 371 | | - } |
| 372 | | - |
| 373 | | - |
| 374 | | - function svgNotSupported(imgElem, options) { |
| 375 | | - removeOnLoadAttribute(imgElem); |
| 376 | | - fail(imgElem, SVG_NOT_SUPPORTED, options); |
| 377 | | - } |
| 378 | | - |
| 379 | | - |
| 380 | | - function loadFail(imgElem, options) { |
| 381 | | - fail(imgElem, LOAD_FAIL, options); |
| 382 | | - } |
| 383 | | - |
| 384 | | - |
| 385 | | - function removeEventListeners(imgElem) { |
| 386 | | - imgElem.onload = NULL; |
| 387 | | - imgElem.onerror = NULL; |
| 388 | | - } |
| 389 | | - |
| 390 | | - |
| 391 | | - function imgNotSet(msg) { |
| 392 | | - errorMessage('no img element'); |
| 393 | | - } |
| 394 | | - |
| 395 | | - |
| 396 | | - function createSVGInject(globalName, options) { |
| 397 | | - var defaultOptions = mergeOptions(DEFAULT_OPTIONS, options); |
| 398 | | - var svgLoadCache = {}; |
| 399 | | - |
| 400 | | - if (IS_SVG_SUPPORTED) { |
| 401 | | - // If the browser supports SVG, add a small stylesheet that hides the <img> elements until |
| 402 | | - // injection is finished. This avoids showing the unstyled SVGs before style is applied. |
| 403 | | - addStyleToHead('img[onload^="' + globalName + '("]{visibility:hidden;}'); |
| 404 | | - } |
| 405 | | - |
| 406 | | - |
| 407 | | - /** |
| 408 | | - * SVGInject |
| 409 | | - * |
| 410 | | - * Injects the SVG specified in the `src` attribute of the specified `img` element or array of `img` |
| 411 | | - * elements. Returns a Promise object which resolves if all passed in `img` elements have either been |
| 412 | | - * injected or failed to inject (Only if a global Promise object is available like in all modern browsers |
| 413 | | - * or through a polyfill). |
| 414 | | - * |
| 415 | | - * Options: |
| 416 | | - * useCache: If set to `true` the SVG will be cached using the absolute URL. Default value is `true`. |
| 417 | | - * copyAttributes: If set to `true` the attributes will be copied from `img` to `svg`. Dfault value |
| 418 | | - * is `true`. |
| 419 | | - * makeIdsUnique: If set to `true` the ID of elements in the `<defs>` element that can be references by |
| 420 | | - * property values (for example 'clipPath') are made unique by appending "--inject-X", where X is a |
| 421 | | - * running number which increases with each injection. This is done to avoid duplicate IDs in the DOM. |
| 422 | | - * beforeLoad: Hook before SVG is loaded. The `img` element is passed as a parameter. If the hook returns |
| 423 | | - * a string it is used as the URL instead of the `img` element's `src` attribute. |
| 424 | | - * afterLoad: Hook after SVG is loaded. The loaded `svg` element and `svg` string are passed as a |
| 425 | | - * parameters. If caching is active this hook will only get called once for injected SVGs with the |
| 426 | | - * same absolute path. Changes to the `svg` element in this hook will be applied to all injected SVGs |
| 427 | | - * with the same absolute path. It's also possible to return an `svg` string or `svg` element which |
| 428 | | - * will then be used for the injection. |
| 429 | | - * beforeInject: Hook before SVG is injected. The `img` and `svg` elements are passed as parameters. If |
| 430 | | - * any html element is returned it gets injected instead of applying the default SVG injection. |
| 431 | | - * afterInject: Hook after SVG is injected. The `img` and `svg` elements are passed as parameters. |
| 432 | | - * onAllFinish: Hook after all `img` elements passed to an SVGInject() call have either been injected or |
| 433 | | - * failed to inject. |
| 434 | | - * onFail: Hook after injection fails. The `img` element and a `status` string are passed as an parameter. |
| 435 | | - * The `status` can be either `'SVG_NOT_SUPPORTED'` (the browser does not support SVG), |
| 436 | | - * `'SVG_INVALID'` (the SVG is not in a valid format) or `'LOAD_FAILED'` (loading of the SVG failed). |
| 437 | | - * |
| 438 | | - * @param {HTMLImageElement} img - an img element or an array of img elements |
| 439 | | - * @param {Object} [options] - optional parameter with [options](#options) for this injection. |
| 440 | | - */ |
| 441 | | - function SVGInject(img, options) { |
| 442 | | - options = mergeOptions(defaultOptions, options); |
| 443 | | - |
| 444 | | - var run = function(resolve) { |
| 445 | | - var allFinish = function() { |
| 446 | | - var onAllFinish = options.onAllFinish; |
| 447 | | - if (onAllFinish) { |
| 448 | | - onAllFinish(); |
| 449 | | - } |
| 450 | | - resolve && resolve(); |
| 451 | | - }; |
| 452 | | - |
| 453 | | - if (img && typeof img[_LENGTH_] != _UNDEFINED_) { |
| 454 | | - // an array like structure of img elements |
| 455 | | - var injectIndex = 0; |
| 456 | | - var injectCount = img[_LENGTH_]; |
| 457 | | - |
| 458 | | - if (injectCount == 0) { |
| 459 | | - allFinish(); |
| 460 | | - } else { |
| 461 | | - var finish = function() { |
| 462 | | - if (++injectIndex == injectCount) { |
| 463 | | - allFinish(); |
| 464 | | - } |
| 465 | | - }; |
| 466 | | - |
| 467 | | - for (var i = 0; i < injectCount; i++) { |
| 468 | | - SVGInjectElement(img[i], options, finish); |
| 469 | | - } |
| 470 | | - } |
| 471 | | - } else { |
| 472 | | - // only one img element |
| 473 | | - SVGInjectElement(img, options, allFinish); |
| 474 | | - } |
| 475 | | - }; |
| 476 | | - |
| 477 | | - // return a Promise object if globally available |
| 478 | | - return typeof Promise == _UNDEFINED_ ? run() : new Promise(run); |
| 479 | | - } |
| 480 | | - |
| 481 | | - |
| 482 | | - // Injects a single svg element. Options must be already merged with the default options. |
| 483 | | - function SVGInjectElement(imgElem, options, callback) { |
| 484 | | - if (imgElem) { |
| 485 | | - var svgInjectAttributeValue = imgElem[__SVGINJECT]; |
| 486 | | - if (!svgInjectAttributeValue) { |
| 487 | | - removeEventListeners(imgElem); |
| 488 | | - |
| 489 | | - if (!IS_SVG_SUPPORTED) { |
| 490 | | - svgNotSupported(imgElem, options); |
| 491 | | - callback(); |
| 492 | | - return; |
| 493 | | - } |
| 494 | | - // Invoke beforeLoad hook if set. If the beforeLoad returns a value use it as the src for the load |
| 495 | | - // URL path. Else use the imgElem's src attribute value. |
| 496 | | - var beforeLoad = options.beforeLoad; |
| 497 | | - var src = (beforeLoad && beforeLoad(imgElem)) || imgElem[_GET_ATTRIBUTE_]('src'); |
| 498 | | - |
| 499 | | - if (!src) { |
| 500 | | - // If no image src attribute is set do no injection. This can only be reached by using javascript |
| 501 | | - // because if no src attribute is set the onload and onerror events do not get called |
| 502 | | - if (src === '') { |
| 503 | | - loadFail(imgElem, options); |
| 504 | | - } |
| 505 | | - callback(); |
| 506 | | - return; |
| 507 | | - } |
| 508 | | - |
| 509 | | - // set array so later calls can register callbacks |
| 510 | | - var onFinishCallbacks = []; |
| 511 | | - imgElem[__SVGINJECT] = onFinishCallbacks; |
| 512 | | - |
| 513 | | - var onFinish = function() { |
| 514 | | - callback(); |
| 515 | | - onFinishCallbacks.forEach(function(onFinishCallback) { |
| 516 | | - onFinishCallback(); |
| 517 | | - }); |
| 518 | | - }; |
| 519 | | - |
| 520 | | - var absUrl = getAbsoluteUrl(src); |
| 521 | | - var useCacheOption = options.useCache; |
| 522 | | - var makeIdsUniqueOption = options.makeIdsUnique; |
| 523 | | - |
| 524 | | - var setSvgLoadCacheValue = function(val) { |
| 525 | | - if (useCacheOption) { |
| 526 | | - svgLoadCache[absUrl].forEach(function(svgLoad) { |
| 527 | | - svgLoad(val); |
| 528 | | - }); |
| 529 | | - svgLoadCache[absUrl] = val; |
| 530 | | - } |
| 531 | | - }; |
| 532 | | - |
| 533 | | - if (useCacheOption) { |
| 534 | | - var svgLoad = svgLoadCache[absUrl]; |
| 535 | | - |
| 536 | | - var handleLoadValue = function(loadValue) { |
| 537 | | - if (loadValue === LOAD_FAIL) { |
| 538 | | - loadFail(imgElem, options); |
| 539 | | - } else if (loadValue === SVG_INVALID) { |
| 540 | | - svgInvalid(imgElem, options); |
| 541 | | - } else { |
| 542 | | - var hasUniqueIds = loadValue[0]; |
| 543 | | - var svgString = loadValue[1]; |
| 544 | | - var uniqueIdsSvgString = loadValue[2]; |
| 545 | | - var svgElem; |
| 546 | | - |
| 547 | | - if (makeIdsUniqueOption) { |
| 548 | | - if (hasUniqueIds === NULL) { |
| 549 | | - // IDs for the SVG string have not been made unique before. This may happen if previous |
| 550 | | - // injection of a cached SVG have been run with the option makedIdsUnique set to false |
| 551 | | - svgElem = buildSvgElement(svgString, false); |
| 552 | | - hasUniqueIds = makeIdsUnique(svgElem, false); |
| 553 | | - |
| 554 | | - loadValue[0] = hasUniqueIds; |
| 555 | | - loadValue[2] = hasUniqueIds && svgElemToSvgString(svgElem); |
| 556 | | - } else if (hasUniqueIds) { |
| 557 | | - // Make IDs unique for already cached SVGs with better performance |
| 558 | | - svgString = makeIdsUniqueCached(uniqueIdsSvgString); |
| 559 | | - } |
| 560 | | - } |
| 561 | | - |
| 562 | | - svgElem = svgElem || buildSvgElement(svgString, false); |
| 563 | | - |
| 564 | | - inject(imgElem, svgElem, absUrl, options); |
| 565 | | - } |
| 566 | | - onFinish(); |
| 567 | | - }; |
| 568 | | - |
| 569 | | - if (typeof svgLoad != _UNDEFINED_) { |
| 570 | | - // Value for url exists in cache |
| 571 | | - if (svgLoad.isCallbackQueue) { |
| 572 | | - // Same url has been cached, but value has not been loaded yet, so add to callbacks |
| 573 | | - svgLoad.push(handleLoadValue); |
| 574 | | - } else { |
| 575 | | - handleLoadValue(svgLoad); |
| 576 | | - } |
| 577 | | - return; |
| 578 | | - } else { |
| 579 | | - var svgLoad = []; |
| 580 | | - // set property isCallbackQueue to Array to differentiate from array with cached loaded values |
| 581 | | - svgLoad.isCallbackQueue = true; |
| 582 | | - svgLoadCache[absUrl] = svgLoad; |
| 583 | | - } |
| 584 | | - } |
| 585 | | - |
| 586 | | - // Load the SVG because it is not cached or caching is disabled |
| 587 | | - loadSvg(absUrl, function(svgXml, svgString) { |
| 588 | | - // Use the XML from the XHR request if it is an instance of Document. Otherwise |
| 589 | | - // (for example of IE9), create the svg document from the svg string. |
| 590 | | - var svgElem = svgXml instanceof Document ? svgXml.documentElement : buildSvgElement(svgString, true); |
| 591 | | - |
| 592 | | - var afterLoad = options.afterLoad; |
| 593 | | - if (afterLoad) { |
| 594 | | - // Invoke afterLoad hook which may modify the SVG element. After load may also return a new |
| 595 | | - // svg element or svg string |
| 596 | | - var svgElemOrSvgString = afterLoad(svgElem, svgString) || svgElem; |
| 597 | | - if (svgElemOrSvgString) { |
| 598 | | - // Update svgElem and svgString because of modifications to the SVG element or SVG string in |
| 599 | | - // the afterLoad hook, so the modified SVG is also used for all later cached injections |
| 600 | | - var isString = typeof svgElemOrSvgString == 'string'; |
| 601 | | - svgString = isString ? svgElemOrSvgString : svgElemToSvgString(svgElem); |
| 602 | | - svgElem = isString ? buildSvgElement(svgElemOrSvgString, true) : svgElemOrSvgString; |
| 603 | | - } |
| 604 | | - } |
| 605 | | - |
| 606 | | - if (svgElem instanceof SVGElement) { |
| 607 | | - var hasUniqueIds = NULL; |
| 608 | | - if (makeIdsUniqueOption) { |
| 609 | | - hasUniqueIds = makeIdsUnique(svgElem, false); |
| 610 | | - } |
| 611 | | - |
| 612 | | - if (useCacheOption) { |
| 613 | | - var uniqueIdsSvgString = hasUniqueIds && svgElemToSvgString(svgElem); |
| 614 | | - // set an array with three entries to the load cache |
| 615 | | - setSvgLoadCacheValue([hasUniqueIds, svgString, uniqueIdsSvgString]); |
| 616 | | - } |
| 617 | | - |
| 618 | | - inject(imgElem, svgElem, absUrl, options); |
| 619 | | - } else { |
| 620 | | - svgInvalid(imgElem, options); |
| 621 | | - setSvgLoadCacheValue(SVG_INVALID); |
| 622 | | - } |
| 623 | | - onFinish(); |
| 624 | | - }, function() { |
| 625 | | - loadFail(imgElem, options); |
| 626 | | - setSvgLoadCacheValue(LOAD_FAIL); |
| 627 | | - onFinish(); |
| 628 | | - }); |
| 629 | | - } else { |
| 630 | | - if (Array.isArray(svgInjectAttributeValue)) { |
| 631 | | - // svgInjectAttributeValue is an array. Injection is not complete so register callback |
| 632 | | - svgInjectAttributeValue.push(callback); |
| 633 | | - } else { |
| 634 | | - callback(); |
| 635 | | - } |
| 636 | | - } |
| 637 | | - } else { |
| 638 | | - imgNotSet(); |
| 639 | | - } |
| 640 | | - } |
| 641 | | - |
| 642 | | - |
| 643 | | - /** |
| 644 | | - * Sets the default [options](#options) for SVGInject. |
| 645 | | - * |
| 646 | | - * @param {Object} [options] - default [options](#options) for an injection. |
| 647 | | - */ |
| 648 | | - SVGInject.setOptions = function(options) { |
| 649 | | - defaultOptions = mergeOptions(defaultOptions, options); |
| 650 | | - }; |
| 651 | | - |
| 652 | | - |
| 653 | | - // Create a new instance of SVGInject |
| 654 | | - SVGInject.create = createSVGInject; |
| 655 | | - |
| 656 | | - |
| 657 | | - /** |
| 658 | | - * Used in onerror Event of an `<img>` element to handle cases when the loading the original src fails |
| 659 | | - * (for example if file is not found or if the browser does not support SVG). This triggers a call to the |
| 660 | | - * options onFail hook if available. The optional second parameter will be set as the new src attribute |
| 661 | | - * for the img element. |
| 662 | | - * |
| 663 | | - * @param {HTMLImageElement} img - an img element |
| 664 | | - * @param {String} [fallbackSrc] - optional parameter fallback src |
| 665 | | - */ |
| 666 | | - SVGInject.err = function(img, fallbackSrc) { |
| 667 | | - if (img) { |
| 668 | | - if (img[__SVGINJECT] != FAIL) { |
| 669 | | - removeEventListeners(img); |
| 670 | | - |
| 671 | | - if (!IS_SVG_SUPPORTED) { |
| 672 | | - svgNotSupported(img, defaultOptions); |
| 673 | | - } else { |
| 674 | | - removeOnLoadAttribute(img); |
| 675 | | - loadFail(img, defaultOptions); |
| 676 | | - } |
| 677 | | - if (fallbackSrc) { |
| 678 | | - removeOnLoadAttribute(img); |
| 679 | | - img.src = fallbackSrc; |
| 680 | | - } |
| 681 | | - } |
| 682 | | - } else { |
| 683 | | - imgNotSet(); |
| 684 | | - } |
| 685 | | - }; |
| 686 | | - |
| 687 | | - window[globalName] = SVGInject; |
| 688 | | - |
| 689 | | - return SVGInject; |
| 690 | | - } |
| 691 | | - |
| 692 | | - var SVGInjectInstance = createSVGInject('SVGInject'); |
| 693 | | - |
| 694 | | - if (typeof module == 'object' && typeof module.exports == 'object') { |
| 695 | | - module.exports = SVGInjectInstance; |
| 696 | | - } |
| 697 | | -})(window, document); |
| 697 | |
\ No newline at end of file |