UNPKG

27.2 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global.BrowserSprite = factory());
5}(this, (function () { 'use strict';
6
7var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
8
9
10
11
12
13function createCommonjsModule(fn, module) {
14 return module = { exports: {} }, fn(module, module.exports), module.exports;
15}
16
17var deepmerge = createCommonjsModule(function (module, exports) {
18(function (root, factory) {
19 if (typeof undefined === 'function' && undefined.amd) {
20 undefined(factory);
21 } else {
22 module.exports = factory();
23 }
24}(commonjsGlobal, function () {
25
26function isMergeableObject(val) {
27 var nonNullObject = val && typeof val === 'object';
28
29 return nonNullObject
30 && Object.prototype.toString.call(val) !== '[object RegExp]'
31 && Object.prototype.toString.call(val) !== '[object Date]'
32}
33
34function emptyTarget(val) {
35 return Array.isArray(val) ? [] : {}
36}
37
38function cloneIfNecessary(value, optionsArgument) {
39 var clone = optionsArgument && optionsArgument.clone === true;
40 return (clone && isMergeableObject(value)) ? deepmerge(emptyTarget(value), value, optionsArgument) : value
41}
42
43function defaultArrayMerge(target, source, optionsArgument) {
44 var destination = target.slice();
45 source.forEach(function(e, i) {
46 if (typeof destination[i] === 'undefined') {
47 destination[i] = cloneIfNecessary(e, optionsArgument);
48 } else if (isMergeableObject(e)) {
49 destination[i] = deepmerge(target[i], e, optionsArgument);
50 } else if (target.indexOf(e) === -1) {
51 destination.push(cloneIfNecessary(e, optionsArgument));
52 }
53 });
54 return destination
55}
56
57function mergeObject(target, source, optionsArgument) {
58 var destination = {};
59 if (isMergeableObject(target)) {
60 Object.keys(target).forEach(function (key) {
61 destination[key] = cloneIfNecessary(target[key], optionsArgument);
62 });
63 }
64 Object.keys(source).forEach(function (key) {
65 if (!isMergeableObject(source[key]) || !target[key]) {
66 destination[key] = cloneIfNecessary(source[key], optionsArgument);
67 } else {
68 destination[key] = deepmerge(target[key], source[key], optionsArgument);
69 }
70 });
71 return destination
72}
73
74function deepmerge(target, source, optionsArgument) {
75 var array = Array.isArray(source);
76 var options = optionsArgument || { arrayMerge: defaultArrayMerge };
77 var arrayMerge = options.arrayMerge || defaultArrayMerge;
78
79 if (array) {
80 return Array.isArray(target) ? arrayMerge(target, source, optionsArgument) : cloneIfNecessary(source, optionsArgument)
81 } else {
82 return mergeObject(target, source, optionsArgument)
83 }
84}
85
86deepmerge.all = function deepmergeAll(array, optionsArgument) {
87 if (!Array.isArray(array) || array.length < 2) {
88 throw new Error('first argument should be an array with at least two elements')
89 }
90
91 // we are sure there are at least 2 values, so it is safe to have no initial value
92 return array.reduce(function(prev, next) {
93 return deepmerge(prev, next, optionsArgument)
94 })
95};
96
97return deepmerge
98
99}));
100});
101
102//
103// An event handler can take an optional event argument
104// and should not return a value
105
106// An array of all currently registered event handlers for a type
107
108// A map of event types and their corresponding event handlers.
109
110
111
112
113/** Mitt: Tiny (~200b) functional event emitter / pubsub.
114 * @name mitt
115 * @returns {Mitt}
116 */
117function mitt(all ) {
118 all = all || Object.create(null);
119
120 return {
121 /**
122 * Register an event handler for the given type.
123 *
124 * @param {String} type Type of event to listen for, or `"*"` for all events
125 * @param {Function} handler Function to call in response to given event
126 * @memberOf mitt
127 */
128 on: function on(type , handler ) {
129 (all[type] || (all[type] = [])).push(handler);
130 },
131
132 /**
133 * Remove an event handler for the given type.
134 *
135 * @param {String} type Type of event to unregister `handler` from, or `"*"`
136 * @param {Function} handler Handler function to remove
137 * @memberOf mitt
138 */
139 off: function off(type , handler ) {
140 if (all[type]) {
141 all[type].splice(all[type].indexOf(handler) >>> 0, 1);
142 }
143 },
144
145 /**
146 * Invoke all handlers for the given type.
147 * If present, `"*"` handlers are invoked after type-matched handlers.
148 *
149 * @param {String} type The event type to invoke
150 * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
151 * @memberof mitt
152 */
153 emit: function emit(type , evt ) {
154 (all[type] || []).map(function (handler) { handler(evt); });
155 (all['*'] || []).map(function (handler) { handler(type, evt); });
156 }
157 };
158}
159
160var namespaces_1 = createCommonjsModule(function (module, exports) {
161var namespaces = {
162 svg: {
163 name: 'xmlns',
164 uri: 'http://www.w3.org/2000/svg'
165 },
166 xlink: {
167 name: 'xmlns:xlink',
168 uri: 'http://www.w3.org/1999/xlink'
169 }
170};
171
172exports.default = namespaces;
173module.exports = exports.default;
174});
175
176/**
177 * @param {Object} attrs
178 * @return {string}
179 */
180var objectToAttrsString = function (attrs) {
181 return Object.keys(attrs).map(function (attr) {
182 var value = attrs[attr].toString().replace(/"/g, '&quot;');
183 return (attr + "=\"" + value + "\"");
184 }).join(' ');
185};
186
187var svg = namespaces_1.svg;
188var xlink = namespaces_1.xlink;
189
190var defaultAttrs = {};
191defaultAttrs[svg.name] = svg.uri;
192defaultAttrs[xlink.name] = xlink.uri;
193
194/**
195 * @param {string} [content]
196 * @param {Object} [attributes]
197 * @return {string}
198 */
199var wrapInSvgString = function (content, attributes) {
200 if ( content === void 0 ) content = '';
201
202 var attrs = deepmerge(defaultAttrs, attributes || {});
203 var attrsRendered = objectToAttrsString(attrs);
204 return ("<svg " + attrsRendered + ">" + content + "</svg>");
205};
206
207var svg$1 = namespaces_1.svg;
208var xlink$1 = namespaces_1.xlink;
209
210var defaultConfig = {
211 attrs: ( obj = {
212 style: ['position: absolute', 'width: 0', 'height: 0'].join('; '),
213 'aria-hidden': 'true'
214 }, obj[svg$1.name] = svg$1.uri, obj[xlink$1.name] = xlink$1.uri, obj )
215};
216var obj;
217
218var Sprite = function Sprite(config) {
219 this.config = deepmerge(defaultConfig, config || {});
220 this.symbols = [];
221};
222
223/**
224 * Add new symbol. If symbol with the same id exists it will be replaced.
225 * @param {SpriteSymbol} symbol
226 * @return {boolean} `true` - symbol was added, `false` - replaced
227 */
228Sprite.prototype.add = function add (symbol) {
229 var ref = this;
230 var symbols = ref.symbols;
231 var existing = this.find(symbol.id);
232
233 if (existing) {
234 symbols[symbols.indexOf(existing)] = symbol;
235 return false;
236 }
237
238 symbols.push(symbol);
239 return true;
240};
241
242/**
243 * Remove symbol & destroy it
244 * @param {string} id
245 * @return {boolean} `true` - symbol was found & successfully destroyed, `false` - otherwise
246 */
247Sprite.prototype.remove = function remove (id) {
248 var ref = this;
249 var symbols = ref.symbols;
250 var symbol = this.find(id);
251
252 if (symbol) {
253 symbols.splice(symbols.indexOf(symbol), 1);
254 symbol.destroy();
255 return true;
256 }
257
258 return false;
259};
260
261/**
262 * @param {string} id
263 * @return {SpriteSymbol|null}
264 */
265Sprite.prototype.find = function find (id) {
266 return this.symbols.filter(function (s) { return s.id === id; })[0] || null;
267};
268
269/**
270 * @param {string} id
271 * @return {boolean}
272 */
273Sprite.prototype.has = function has (id) {
274 return this.find(id) !== null;
275};
276
277/**
278 * @return {string}
279 */
280Sprite.prototype.stringify = function stringify () {
281 var ref = this.config;
282 var attrs = ref.attrs;
283 var stringifiedSymbols = this.symbols.map(function (s) { return s.stringify(); }).join('');
284 return wrapInSvgString(stringifiedSymbols, attrs);
285};
286
287/**
288 * @return {string}
289 */
290Sprite.prototype.toString = function toString () {
291 return this.stringify();
292};
293
294Sprite.prototype.destroy = function destroy () {
295 this.symbols.forEach(function (s) { return s.destroy(); });
296};
297
298var SpriteSymbol = function SpriteSymbol(ref) {
299 var id = ref.id;
300 var viewBox = ref.viewBox;
301 var content = ref.content;
302
303 this.id = id;
304 this.viewBox = viewBox;
305 this.content = content;
306};
307
308/**
309 * @return {string}
310 */
311SpriteSymbol.prototype.stringify = function stringify () {
312 return this.content;
313};
314
315/**
316 * @return {string}
317 */
318SpriteSymbol.prototype.toString = function toString () {
319 return this.stringify();
320};
321
322SpriteSymbol.prototype.destroy = function destroy () {
323 var this$1 = this;
324
325 ['id', 'viewBox', 'content'].forEach(function (prop) { return delete this$1[prop]; });
326};
327
328/**
329 * @param {string} content
330 * @return {Element}
331 */
332var parse = function (content) {
333 var hasImportNode = !!document.importNode;
334 var doc = new DOMParser().parseFromString(content, 'image/svg+xml').documentElement;
335
336 /**
337 * Fix for browser which are throwing WrongDocumentError
338 * if you insert an element which is not part of the document
339 * @see http://stackoverflow.com/a/7986519/4624403
340 */
341 if (hasImportNode) {
342 return document.importNode(doc, true);
343 }
344
345 return doc;
346};
347
348var BrowserSpriteSymbol = (function (SpriteSymbol$$1) {
349 function BrowserSpriteSymbol () {
350 SpriteSymbol$$1.apply(this, arguments);
351 }
352
353 if ( SpriteSymbol$$1 ) BrowserSpriteSymbol.__proto__ = SpriteSymbol$$1;
354 BrowserSpriteSymbol.prototype = Object.create( SpriteSymbol$$1 && SpriteSymbol$$1.prototype );
355 BrowserSpriteSymbol.prototype.constructor = BrowserSpriteSymbol;
356
357 var prototypeAccessors = { isMounted: {} };
358
359 prototypeAccessors.isMounted.get = function () {
360 return !!this.node;
361 };
362
363 /**
364 * @param {Element} node
365 * @return {BrowserSpriteSymbol}
366 */
367 BrowserSpriteSymbol.createFromExistingNode = function createFromExistingNode (node) {
368 return new BrowserSpriteSymbol({
369 id: node.getAttribute('id'),
370 viewBox: node.getAttribute('viewBox'),
371 content: node.outerHTML
372 });
373 };
374
375 BrowserSpriteSymbol.prototype.destroy = function destroy () {
376 if (this.isMounted) {
377 this.unmount();
378 }
379 SpriteSymbol$$1.prototype.destroy.call(this);
380 };
381
382 /**
383 * @param {Element|string} target
384 * @return {Element}
385 */
386 BrowserSpriteSymbol.prototype.mount = function mount (target) {
387 if (this.isMounted) {
388 return this.node;
389 }
390
391 var mountTarget = typeof target === 'string' ? document.querySelector(target) : target;
392 var node = this.render();
393 this.node = node;
394
395 mountTarget.appendChild(node);
396
397 return node;
398 };
399
400 /**
401 * @return {Element}
402 */
403 BrowserSpriteSymbol.prototype.render = function render () {
404 var content = this.stringify();
405 return parse(wrapInSvgString(content)).childNodes[0];
406 };
407
408 BrowserSpriteSymbol.prototype.unmount = function unmount () {
409 this.node.parentNode.removeChild(this.node);
410 };
411
412 Object.defineProperties( BrowserSpriteSymbol.prototype, prototypeAccessors );
413
414 return BrowserSpriteSymbol;
415}(SpriteSymbol));
416
417var defaultConfig$1 = {
418 /**
419 * Should following options be automatically configured:
420 * - `syncUrlsWithBaseTag`
421 * - `locationChangeAngularEmitter`
422 * - `moveGradientsOutsideSymbol`
423 * @type {boolean}
424 */
425 autoConfigure: true,
426
427 /**
428 * Default mounting selector
429 * @type {string}
430 */
431 mountTo: 'body',
432
433 /**
434 * Fix disappearing SVG elements when <base href> exists.
435 * Executes when sprite mounted.
436 * @see http://stackoverflow.com/a/18265336/796152
437 * @see https://github.com/everdimension/angular-svg-base-fix
438 * @see https://github.com/angular/angular.js/issues/8934#issuecomment-56568466
439 * @type {boolean}
440 */
441 syncUrlsWithBaseTag: false,
442
443 /**
444 * Should sprite listen custom location change event
445 * @type {boolean}
446 */
447 listenLocationChangeEvent: true,
448
449 /**
450 * Custom window event name which should be emitted to update sprite urls
451 * @type {string}
452 */
453 locationChangeEvent: 'locationChange',
454
455 /**
456 * Emit location change event in Angular automatically
457 * @type {boolean}
458 */
459 locationChangeAngularEmitter: false,
460
461 /**
462 * Selector to find symbols usages when updating sprite urls
463 * @type {string}
464 */
465 usagesToUpdate: 'use[*|href]',
466
467 /**
468 * Fix Firefox bug when gradients and patterns don't work if they are within a symbol.
469 * Executes when sprite is rendered, but not mounted.
470 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=306674
471 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=353575
472 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1235364
473 * @type {boolean}
474 */
475 moveGradientsOutsideSymbol: false
476};
477
478/**
479 * @param {*} arrayLike
480 * @return {Array}
481 */
482var arrayFrom = function (arrayLike) {
483 return Array.prototype.slice.call(arrayLike, 0);
484};
485
486var browser = {
487 isChrome: function () { return /chrome/i.test(navigator.userAgent); },
488 isFirefox: function () { return /firefox/i.test(navigator.userAgent); },
489
490 // https://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
491 isIE: function () { return /msie/i.test(navigator.userAgent) || /trident/i.test(navigator.userAgent); },
492 isEdge: function () { return /edge/i.test(navigator.userAgent); }
493};
494
495/**
496 * @param {string} name
497 * @param {*} data
498 */
499var dispatchEvent = function (name, data) {
500 var event = document.createEvent('CustomEvent');
501 event.initCustomEvent(name, false, false, data);
502 window.dispatchEvent(event);
503};
504
505/**
506 * IE doesn't evaluate <style> tags in SVGs that are dynamically added to the page.
507 * This trick will trigger IE to read and use any existing SVG <style> tags.
508 * @see https://github.com/iconic/SVGInjector/issues/23
509 * @see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10898469/
510 *
511 * @param {Element} node DOM Element to search <style> tags in
512 * @return {Array<HTMLStyleElement>}
513 */
514var evalStylesIEWorkaround = function (node) {
515 var updatedNodes = [];
516
517 arrayFrom(node.querySelectorAll('style'))
518 .forEach(function (style) {
519 style.textContent += '';
520 updatedNodes.push(style);
521 });
522
523 return updatedNodes;
524};
525
526/**
527 * @param {string} [url] If not provided - current URL will be used
528 * @return {string}
529 */
530var getUrlWithoutFragment = function (url) {
531 return (url || window.location.href).split('#')[0];
532};
533
534/* global angular */
535/**
536 * @param {string} eventName
537 */
538var locationChangeAngularEmitter = function (eventName) {
539 angular.module('ng').run(['$rootScope', function ($rootScope) {
540 $rootScope.$on('$locationChangeSuccess', function (e, newUrl, oldUrl) {
541 dispatchEvent(eventName, { oldUrl: oldUrl, newUrl: newUrl });
542 });
543 }]);
544};
545
546var defaultSelector = 'linearGradient, radialGradient, pattern, mask, clipPath';
547
548/**
549 * @param {Element} svg
550 * @param {string} [selector]
551 * @return {Element}
552 */
553var moveGradientsOutsideSymbol = function (svg, selector) {
554 if ( selector === void 0 ) selector = defaultSelector;
555
556 arrayFrom(svg.querySelectorAll('symbol')).forEach(function (symbol) {
557 arrayFrom(symbol.querySelectorAll(selector)).forEach(function (node) {
558 symbol.parentNode.insertBefore(node, symbol);
559 });
560 });
561 return svg;
562};
563
564/**
565 * @param {NodeList} nodes
566 * @param {Function} [matcher]
567 * @return {Attr[]}
568 */
569function selectAttributes(nodes, matcher) {
570 var attrs = arrayFrom(nodes).reduce(function (acc, node) {
571 if (!node.attributes) {
572 return acc;
573 }
574
575 var arrayfied = arrayFrom(node.attributes);
576 var matched = matcher ? arrayfied.filter(matcher) : arrayfied;
577 return acc.concat(matched);
578 }, []);
579
580 return attrs;
581}
582
583/**
584 * @param {NodeList|Node} nodes
585 * @param {boolean} [clone=true]
586 * @return {string}
587 */
588
589var xLinkNS = namespaces_1.xlink.uri;
590var xLinkAttrName = 'xlink:href';
591
592// eslint-disable-next-line no-useless-escape
593var specialUrlCharsPattern = /[{}|\\\^\[\]`"<>]/g;
594
595function encoder(url) {
596 return url.replace(specialUrlCharsPattern, function (match) {
597 return ("%" + (match[0].charCodeAt(0).toString(16).toUpperCase()));
598 });
599}
600
601function escapeRegExp(str) {
602 return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
603}
604
605/**
606 * @param {NodeList} nodes
607 * @param {string} startsWith
608 * @param {string} replaceWith
609 * @return {NodeList}
610 */
611function updateReferences(nodes, startsWith, replaceWith) {
612 arrayFrom(nodes).forEach(function (node) {
613 var href = node.getAttribute(xLinkAttrName);
614 if (href && href.indexOf(startsWith) === 0) {
615 var newUrl = href.replace(startsWith, replaceWith);
616 node.setAttributeNS(xLinkNS, xLinkAttrName, newUrl);
617 }
618 });
619
620 return nodes;
621}
622
623/**
624 * List of SVG attributes to update url() target in them
625 */
626var attList = [
627 'clipPath',
628 'colorProfile',
629 'src',
630 'cursor',
631 'fill',
632 'filter',
633 'marker',
634 'markerStart',
635 'markerMid',
636 'markerEnd',
637 'mask',
638 'stroke',
639 'style'
640];
641
642var attSelector = attList.map(function (attr) { return ("[" + attr + "]"); }).join(',');
643
644/**
645 * Update URLs in svg image (like `fill="url(...)"`) and update referencing elements
646 * @param {Element} svg
647 * @param {NodeList} references
648 * @param {string|RegExp} startsWith
649 * @param {string} replaceWith
650 * @return {void}
651 *
652 * @example
653 * const sprite = document.querySelector('svg.sprite');
654 * const usages = document.querySelectorAll('use');
655 * updateUrls(sprite, usages, '#', 'prefix#');
656 */
657var updateUrls = function (svg, references, startsWith, replaceWith) {
658 var startsWithEncoded = encoder(startsWith);
659 var replaceWithEncoded = encoder(replaceWith);
660
661 var nodes = svg.querySelectorAll(attSelector);
662 var attrs = selectAttributes(nodes, function (ref) {
663 var localName = ref.localName;
664 var value = ref.value;
665
666 return attList.indexOf(localName) !== -1 && value.indexOf(("url(" + startsWithEncoded)) !== -1;
667 });
668
669 attrs.forEach(function (attr) { return attr.value = attr.value.replace(new RegExp(escapeRegExp(startsWithEncoded), 'g'), replaceWithEncoded); });
670 updateReferences(references, startsWithEncoded, replaceWithEncoded);
671};
672
673/**
674 * Internal emitter events
675 * @enum
676 * @private
677 */
678var Events = {
679 MOUNT: 'mount',
680 SYMBOL_MOUNT: 'symbol_mount'
681};
682
683var BrowserSprite = (function (Sprite$$1) {
684 function BrowserSprite(cfg) {
685 var this$1 = this;
686 if ( cfg === void 0 ) cfg = {};
687
688 Sprite$$1.call(this, deepmerge(defaultConfig$1, cfg));
689
690 var emitter = mitt();
691 this._emitter = emitter;
692 this.node = null;
693
694 var ref = this;
695 var config = ref.config;
696
697 if (config.autoConfigure) {
698 this._autoConfigure(cfg);
699 }
700
701 if (config.syncUrlsWithBaseTag) {
702 var baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
703 emitter.on(Events.MOUNT, function () { return this$1.updateUrls('#', baseUrl); });
704 }
705
706 var handleLocationChange = this._handleLocationChange.bind(this);
707 this._handleLocationChange = handleLocationChange;
708
709 // Provide way to update sprite urls externally via dispatching custom window event
710 if (config.listenLocationChangeEvent) {
711 window.addEventListener(config.locationChangeEvent, handleLocationChange);
712 }
713
714 // Emit location change event in Angular automatically
715 if (config.locationChangeAngularEmitter) {
716 locationChangeAngularEmitter(config.locationChangeEvent);
717 }
718
719 // After sprite mounted
720 emitter.on(Events.MOUNT, function (spriteNode) {
721 if (config.moveGradientsOutsideSymbol) {
722 moveGradientsOutsideSymbol(spriteNode);
723 }
724 });
725
726 // After symbol mounted into sprite
727 emitter.on(Events.SYMBOL_MOUNT, function (symbolNode) {
728 if (config.moveGradientsOutsideSymbol) {
729 moveGradientsOutsideSymbol(symbolNode.parentNode);
730 }
731
732 if (browser.isIE() || browser.isEdge()) {
733 evalStylesIEWorkaround(symbolNode);
734 }
735 });
736 }
737
738 if ( Sprite$$1 ) BrowserSprite.__proto__ = Sprite$$1;
739 BrowserSprite.prototype = Object.create( Sprite$$1 && Sprite$$1.prototype );
740 BrowserSprite.prototype.constructor = BrowserSprite;
741
742 var prototypeAccessors = { isMounted: {} };
743
744 /**
745 * @return {boolean}
746 */
747 prototypeAccessors.isMounted.get = function () {
748 return !!this.node;
749 };
750
751 /**
752 * Automatically configure following options
753 * - `syncUrlsWithBaseTag`
754 * - `locationChangeAngularEmitter`
755 * - `moveGradientsOutsideSymbol`
756 * @param {Object} cfg
757 * @private
758 */
759 BrowserSprite.prototype._autoConfigure = function _autoConfigure (cfg) {
760 var ref = this;
761 var config = ref.config;
762
763 if (typeof cfg.syncUrlsWithBaseTag === 'undefined') {
764 config.syncUrlsWithBaseTag = typeof document.getElementsByTagName('base')[0] !== 'undefined';
765 }
766
767 if (typeof cfg.locationChangeAngularEmitter === 'undefined') {
768 config.locationChangeAngularEmitter = typeof window.angular !== 'undefined';
769 }
770
771 if (typeof cfg.moveGradientsOutsideSymbol === 'undefined') {
772 config.moveGradientsOutsideSymbol = browser.isFirefox();
773 }
774 };
775
776 /**
777 * @param {Event} event
778 * @param {Object} event.detail
779 * @param {string} event.detail.oldUrl
780 * @param {string} event.detail.newUrl
781 * @private
782 */
783 BrowserSprite.prototype._handleLocationChange = function _handleLocationChange (event) {
784 var ref = event.detail;
785 var oldUrl = ref.oldUrl;
786 var newUrl = ref.newUrl;
787 this.updateUrls(oldUrl, newUrl);
788 };
789
790 /**
791 * Add new symbol. If symbol with the same id exists it will be replaced.
792 * If sprite already mounted - `symbol.mount(sprite.node)` will be called.
793 * @fires Events#SYMBOL_MOUNT
794 * @param {BrowserSpriteSymbol} symbol
795 * @return {boolean} `true` - symbol was added, `false` - replaced
796 */
797 BrowserSprite.prototype.add = function add (symbol) {
798 var sprite = this;
799 var isNewSymbol = Sprite$$1.prototype.add.call(this, symbol);
800
801 if (this.isMounted && isNewSymbol) {
802 symbol.mount(sprite.node);
803 this._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
804 }
805
806 return isNewSymbol;
807 };
808
809 /**
810 * Attach to existing DOM node
811 * @param {string|Element} target
812 * @return {Element|null} attached DOM Element. null if node to attach not found.
813 */
814 BrowserSprite.prototype.attach = function attach (target) {
815 var this$1 = this;
816
817 var sprite = this;
818
819 if (sprite.isMounted) {
820 return sprite.node;
821 }
822
823 /** @type Element */
824 var node = typeof target === 'string' ? document.querySelector(target) : target;
825 sprite.node = node;
826
827 // Already added symbols needs to be mounted
828 this.symbols.forEach(function (symbol) {
829 symbol.mount(sprite.node);
830 this$1._emitter.emit(Events.SYMBOL_MOUNT, symbol.node);
831 });
832
833 // Create symbols from existing DOM nodes, add and mount them
834 arrayFrom(node.querySelectorAll('symbol'))
835 .forEach(function (symbolNode) {
836 var symbol = BrowserSpriteSymbol.createFromExistingNode(symbolNode);
837 symbol.node = symbolNode; // hack to prevent symbol mounting to sprite when adding
838 sprite.add(symbol);
839 });
840
841 this._emitter.emit(Events.MOUNT, node);
842
843 return node;
844 };
845
846 BrowserSprite.prototype.destroy = function destroy () {
847 var ref = this;
848 var config = ref.config;
849 var symbols = ref.symbols;
850 var _emitter = ref._emitter;
851
852 symbols.forEach(function (s) { return s.destroy(); });
853
854 _emitter.off('*');
855 window.removeEventListener(config.locationChangeEvent, this._handleLocationChange);
856
857 if (this.isMounted) {
858 this.unmount();
859 }
860 };
861
862 /**
863 * @fires Events#MOUNT
864 * @param {string|Element} [target]
865 * @param {boolean} [prepend=false]
866 * @return {Element|null} rendered sprite node. null if mount node not found.
867 */
868 BrowserSprite.prototype.mount = function mount (target, prepend) {
869 if ( target === void 0 ) target = this.config.mountTo;
870 if ( prepend === void 0 ) prepend = false;
871
872 var sprite = this;
873
874 if (sprite.isMounted) {
875 return sprite.node;
876 }
877
878 var mountNode = typeof target === 'string' ? document.querySelector(target) : target;
879 var node = sprite.render();
880 this.node = node;
881
882 if (prepend && mountNode.childNodes[0]) {
883 mountNode.insertBefore(node, mountNode.childNodes[0]);
884 } else {
885 mountNode.appendChild(node);
886 }
887
888 this._emitter.emit(Events.MOUNT, node);
889
890 return node;
891 };
892
893 /**
894 * @return {Element}
895 */
896 BrowserSprite.prototype.render = function render () {
897 return parse(this.stringify());
898 };
899
900 /**
901 * Detach sprite from the DOM
902 */
903 BrowserSprite.prototype.unmount = function unmount () {
904 this.node.parentNode.removeChild(this.node);
905 };
906
907 /**
908 * Update URLs in sprite and usage elements
909 * @param {string} oldUrl
910 * @param {string} newUrl
911 * @return {boolean} `true` - URLs was updated, `false` - sprite is not mounted
912 */
913 BrowserSprite.prototype.updateUrls = function updateUrls$1 (oldUrl, newUrl) {
914 if (!this.isMounted) {
915 return false;
916 }
917
918 var usages = document.querySelectorAll(this.config.usagesToUpdate);
919
920 updateUrls(
921 this.node,
922 usages,
923 ((getUrlWithoutFragment(oldUrl)) + "#"),
924 ((getUrlWithoutFragment(newUrl)) + "#")
925 );
926
927 return true;
928 };
929
930 Object.defineProperties( BrowserSprite.prototype, prototypeAccessors );
931
932 return BrowserSprite;
933}(Sprite));
934
935var ready$1 = createCommonjsModule(function (module) {
936/*!
937 * domready (c) Dustin Diaz 2014 - License MIT
938 */
939!function (name, definition) {
940
941 { module.exports = definition(); }
942
943}('domready', function () {
944
945 var fns = [], listener
946 , doc = document
947 , hack = doc.documentElement.doScroll
948 , domContentLoaded = 'DOMContentLoaded'
949 , loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState);
950
951
952 if (!loaded)
953 { doc.addEventListener(domContentLoaded, listener = function () {
954 doc.removeEventListener(domContentLoaded, listener);
955 loaded = 1;
956 while (listener = fns.shift()) { listener(); }
957 }); }
958
959 return function (fn) {
960 loaded ? setTimeout(fn, 0) : fns.push(fn);
961 }
962
963});
964});
965
966var spriteNodeId = '__SVG_SPRITE_NODE__';
967var spriteGlobalVarName = '__SVG_SPRITE__';
968var isSpriteExists = !!window[spriteGlobalVarName];
969
970// eslint-disable-next-line import/no-mutable-exports
971var sprite;
972
973if (isSpriteExists) {
974 sprite = window[spriteGlobalVarName];
975} else {
976 sprite = new BrowserSprite({
977 attrs: {
978 id: spriteNodeId,
979 'aria-hidden': 'true'
980 }
981 });
982 window[spriteGlobalVarName] = sprite;
983}
984
985var loadSprite = function () {
986 /**
987 * Check for page already contains sprite node
988 * If found - attach to and reuse it's content
989 * If not - render and mount the new sprite
990 */
991 var existing = document.getElementById(spriteNodeId);
992
993 if (existing) {
994 sprite.attach(existing);
995 } else {
996 sprite.mount(document.body, true);
997 }
998};
999
1000if (document.body) {
1001 loadSprite();
1002} else {
1003 ready$1(loadSprite);
1004}
1005
1006var sprite$1 = sprite;
1007
1008return sprite$1;
1009
1010})));