@iconfu/svg-inject
Version:
A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM.
1 lines • 34.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/copy-attributes.ts","../src/make-ids-unique.ts","../src/sanitize.ts","../src/svg-inject.ts"],"sourcesContent":["export { SVGInject, createSVGInject } from './svg-inject';\nexport { SVGInject as default } from './svg-inject';\nexport type { SVGInjectOptions, SVGInjectFunction, FailStatus, CacheEntry } from './types';\n","// Maps lowercased HTML attribute names to correct SVG casing.\n// HTML parsers lowercase all attributes, but SVG requires specific casing.\nconst SVG_CASE_MAP: Record<string, string> = {\n viewbox: 'viewBox',\n preserveaspectratio: 'preserveAspectRatio',\n basefrequency: 'baseFrequency',\n baseprofile: 'baseProfile',\n calcmode: 'calcMode',\n clippathunits: 'clipPathUnits',\n diffuseconstant: 'diffuseConstant',\n edgemode: 'edgeMode',\n filterunits: 'filterUnits',\n glyphref: 'glyphRef',\n gradienttransform: 'gradientTransform',\n gradientunits: 'gradientUnits',\n kernelmatrix: 'kernelMatrix',\n kernelunitlength: 'kernelUnitLength',\n keypoints: 'keyPoints',\n keysplines: 'keySplines',\n keytimes: 'keyTimes',\n lengthadjust: 'lengthAdjust',\n limitingconeangle: 'limitingConeAngle',\n markerheight: 'markerHeight',\n markerunits: 'markerUnits',\n markerwidth: 'markerWidth',\n maskcontentunits: 'maskContentUnits',\n maskunits: 'maskUnits',\n numoctaves: 'numOctaves',\n pathlength: 'pathLength',\n patterncontentunits: 'patternContentUnits',\n patterntransform: 'patternTransform',\n patternunits: 'patternUnits',\n pointsatx: 'pointsAtX',\n pointsaty: 'pointsAtY',\n pointsatz: 'pointsAtZ',\n primitiveunits: 'primitiveUnits',\n refx: 'refX',\n refy: 'refY',\n repeatcount: 'repeatCount',\n repeatdur: 'repeatDur',\n requiredextensions: 'requiredExtensions',\n requiredfeatures: 'requiredFeatures',\n specularconstant: 'specularConstant',\n specularexponent: 'specularExponent',\n spreadmethod: 'spreadMethod',\n startoffset: 'startOffset',\n stddeviation: 'stdDeviation',\n stitchtiles: 'stitchTiles',\n surfacescale: 'surfaceScale',\n systemlanguage: 'systemLanguage',\n tablevalues: 'tableValues',\n targetx: 'targetX',\n targety: 'targetY',\n textlength: 'textLength',\n xchannelselector: 'xChannelSelector',\n ychannelselector: 'yChannelSelector',\n zoomandpan: 'zoomAndPan',\n};\n\nconst EXCLUDED_ATTRIBUTES = new Set(['src', 'alt', 'onload', 'onerror']);\n\n/**\n * Merges two semicolon-delimited inline style strings.\n * Properties from `imgStyle` win on conflicts.\n */\nexport function mergeStyles(svgStyle: string, imgStyle: string): string {\n const declarations = new Map<string, string>();\n\n for (const style of [svgStyle, imgStyle]) {\n if (!style) continue;\n for (const decl of style.split(';')) {\n const colon = decl.indexOf(':');\n if (colon === -1) continue;\n const prop = decl.substring(0, colon).trim();\n const val = decl.substring(colon + 1).trim();\n if (prop) {\n declarations.set(prop, val);\n }\n }\n }\n\n if (declarations.size === 0) return '';\n return Array.from(declarations.entries())\n .map(([prop, val]) => `${prop}: ${val}`)\n .join('; ');\n}\n\n/**\n * Copies attributes from an <img> element to an <svg> element.\n *\n * - Accessibility: role=\"img\" + aria-label for meaningful images,\n * role=\"none\" + aria-hidden for decorative images (alt=\"\") (#50, #47)\n * - Converts title to <title> child element\n * - Merges style instead of overwriting (#48)\n * - Remaps lowercased attribute names to correct SVG casing (#63)\n * - Skips src, alt, onload, onerror\n */\nexport function copyAttributes(imgElem: HTMLImageElement, svgElem: Element): void {\n const alt = imgElem.getAttribute('alt');\n\n if (alt === '') {\n // Decorative image — hide from assistive technology\n svgElem.setAttribute('role', 'none');\n svgElem.setAttribute('aria-hidden', 'true');\n } else {\n // Meaningful image\n svgElem.setAttribute('role', 'img');\n if (alt !== null) {\n svgElem.setAttribute('aria-label', alt);\n }\n }\n\n const attributes = imgElem.attributes;\n for (let i = 0; i < attributes.length; i++) {\n const attribute = attributes[i];\n let attributeName = attribute.name;\n\n if (EXCLUDED_ATTRIBUTES.has(attributeName)) continue;\n\n const attributeValue = attribute.value;\n\n if (attributeName === 'title') {\n // Convert title attribute to <title> SVG element\n const firstChild = svgElem.firstElementChild;\n let titleElem: Element;\n\n if (firstChild && firstChild.localName.toLowerCase() === 'title') {\n titleElem = firstChild;\n } else {\n titleElem = (svgElem.ownerDocument || document).createElementNS('http://www.w3.org/2000/svg', 'title');\n svgElem.insertBefore(titleElem, firstChild);\n }\n titleElem.textContent = attributeValue;\n } else if (attributeName === 'style') {\n // Merge styles instead of overwriting (#48)\n const svgStyle = svgElem.getAttribute('style') || '';\n const merged = mergeStyles(svgStyle, attributeValue);\n if (merged) {\n svgElem.setAttribute('style', merged);\n }\n } else {\n // Remap lowercased names to correct SVG casing (#63)\n attributeName = SVG_CASE_MAP[attributeName] || attributeName;\n svgElem.setAttribute(attributeName, attributeValue);\n }\n }\n}\n","const ID_SUFFIX = '--inject-';\n\n// Map of IRI referenceable tag names to properties that can reference them.\n// https://www.w3.org/TR/SVG11/linking.html#processingIRI\nconst IRI_TAG_PROPERTIES_MAP: Record<string, string[] | null> = {\n clipPath: ['clip-path'],\n 'color-profile': null,\n cursor: null,\n filter: null,\n linearGradient: ['fill', 'stroke'],\n marker: ['marker', 'marker-end', 'marker-mid', 'marker-start'],\n mask: null,\n pattern: ['fill', 'stroke'],\n radialGradient: ['fill', 'stroke'],\n};\n\n// ARIA attributes that contain space-separated ID references (#52)\nconst ARIA_ID_REF_ATTRIBUTES = [\n 'aria-labelledby',\n 'aria-describedby',\n 'aria-controls',\n 'aria-owns',\n 'aria-flowto',\n 'aria-activedescendant',\n 'aria-errormessage',\n 'aria-details',\n];\n\n// Regex for url() references with optional single or double quotes (#64)\n// Matches: url(#id), url(\"#id\"), url('#id')\nconst FUNC_IRI_REGEX = /url\\([\"']?#([a-zA-Z][\\w:.-]*)[\"']?\\)/g;\n\nlet uniqueIdCounter = 1;\n\n/**\n * Resets the unique ID counter. Primarily for testing.\n */\nexport function resetIdCounter(value = 1): void {\n uniqueIdCounter = value;\n}\n\n/**\n * Makes all IDs in the SVG element unique by appending \"--inject-N\" suffix.\n * Updates all references to those IDs (url(), href, xlink:href, ARIA attributes).\n *\n * Returns true if any IDs were found and modified.\n */\nexport function makeIdsUnique(svgElem: Element): boolean {\n const idSuffix = ID_SUFFIX + uniqueIdCounter++;\n const idElements = svgElem.querySelectorAll('[id]');\n\n if (idElements.length === 0) return false;\n\n // Collect all IDs that exist in the SVG\n const existingIds = new Set<string>();\n const iriTagNames = new Set<string>();\n\n for (let i = 0; i < idElements.length; i++) {\n existingIds.add(idElements[i].id);\n const tagName = idElements[i].localName;\n if (tagName in IRI_TAG_PROPERTIES_MAP) {\n iriTagNames.add(tagName);\n }\n }\n\n // Build list of CSS/SVG properties that may contain url(#id) references\n const iriProperties: string[] = [];\n for (const tagName of iriTagNames) {\n const mapped = IRI_TAG_PROPERTIES_MAP[tagName] || [tagName];\n for (const prop of mapped) {\n if (!iriProperties.includes(prop)) {\n iriProperties.push(prop);\n }\n }\n }\n if (iriProperties.length > 0) {\n iriProperties.push('style');\n }\n\n // Replace function for url(#id) references\n const replaceIriRef = (_match: string, id: string): string => {\n return 'url(#' + id + idSuffix + ')';\n };\n\n // Process all descendant elements + the SVG root itself\n const descendants = svgElem.getElementsByTagName('*');\n\n // Process svgElem first (index -1), then all descendants\n for (let i = -1; i < descendants.length; i++) {\n const element = i === -1 ? svgElem : descendants[i];\n\n if (element.localName === 'style') {\n // Replace IDs in <style> text content\n const text = element.textContent;\n if (text) {\n const newText = text.replace(FUNC_IRI_REGEX, replaceIriRef);\n if (newText !== text) {\n element.textContent = newText;\n }\n }\n } else if (element.hasAttributes()) {\n // Replace url(#id) in IRI-referencing properties\n for (const propertyName of iriProperties) {\n const value = element.getAttribute(propertyName);\n if (value) {\n const newValue = value.replace(FUNC_IRI_REGEX, replaceIriRef);\n if (newValue !== value) {\n element.setAttribute(propertyName, newValue);\n }\n }\n }\n\n // Replace internal #id references in href and xlink:href\n for (const refAttr of ['xlink:href', 'href']) {\n const iri = element.getAttribute(refAttr);\n if (iri && /^\\s*#/.test(iri)) {\n element.setAttribute(refAttr, iri.trim() + idSuffix);\n }\n }\n\n // Replace IDs in ARIA attributes (#52)\n for (const ariaAttr of ARIA_ID_REF_ATTRIBUTES) {\n const value = element.getAttribute(ariaAttr);\n if (value) {\n const ids = value.split(/\\s+/);\n const newIds = ids.map(id => existingIds.has(id) ? id + idSuffix : id);\n const newValue = newIds.join(' ');\n if (newValue !== value) {\n element.setAttribute(ariaAttr, newValue);\n }\n }\n }\n }\n }\n\n // Suffix all element IDs\n for (let i = 0; i < idElements.length; i++) {\n idElements[i].id += idSuffix;\n }\n\n return true;\n}\n\n","// Elements that can execute scripts or embed arbitrary HTML\nconst DANGEROUS_ELEMENTS = new Set([\n 'script',\n 'foreignobject',\n]);\n\n// URI schemes that can execute code\nconst DANGEROUS_URI = /^\\s*(javascript|data|vbscript)\\s*:/i;\n\n/**\n * Removes dangerous elements and attributes from a parsed SVG element.\n * This is a lightweight sanitizer — it catches common XSS vectors but is\n * not a replacement for a full sanitizer like DOMPurify on untrusted content.\n */\nexport function sanitizeSvg(svgElem: Element): void {\n // Remove dangerous elements\n const allElements = svgElem.getElementsByTagName('*');\n // Collect first, then remove (live collection changes during removal)\n const toRemove: Element[] = [];\n for (let i = 0; i < allElements.length; i++) {\n if (DANGEROUS_ELEMENTS.has(allElements[i].localName.toLowerCase())) {\n toRemove.push(allElements[i]);\n }\n }\n for (const el of toRemove) {\n el.parentNode?.removeChild(el);\n }\n\n // Remove event handler attributes and dangerous URIs from all remaining elements\n const remaining = svgElem.getElementsByTagName('*');\n for (let i = -1; i < remaining.length; i++) {\n const el = i === -1 ? svgElem : remaining[i];\n if (!el.hasAttributes()) continue;\n\n // Collect attributes to remove (can't modify during iteration)\n const attrsToRemove: string[] = [];\n for (let j = 0; j < el.attributes.length; j++) {\n const attr = el.attributes[j];\n const name = attr.name.toLowerCase();\n\n // Remove on* event handlers (onclick, onload, onerror, etc.)\n if (name.startsWith('on')) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // Remove dangerous URIs in href/xlink:href\n if ((name === 'href' || name === 'xlink:href') && DANGEROUS_URI.test(attr.value)) {\n attrsToRemove.push(attr.name);\n }\n }\n\n for (const attrName of attrsToRemove) {\n el.removeAttribute(attrName);\n }\n }\n}\n","import type { SVGInjectOptions, FailStatus, CacheEntry, SVGInjectFunction } from './types';\nimport { copyAttributes as copyAttrs } from './copy-attributes';\nimport { makeIdsUnique } from './make-ids-unique';\nimport { sanitizeSvg } from './sanitize';\n\nconst LOAD_FAIL: FailStatus = 'LOAD_FAIL';\nconst SVG_NOT_SUPPORTED: FailStatus = 'SVG_NOT_SUPPORTED';\nconst SVG_INVALID: FailStatus = 'SVG_INVALID';\n\nconst __SVGINJECT = '__svgInject';\nconst INJECTED = 1;\nconst FAIL = 2;\n\nconst DEFAULT_OPTIONS: Required<Pick<SVGInjectOptions, 'useCache' | 'copyAttributes' | 'makeIdsUnique' | 'sanitize' | 'injectStyleTag'>> = {\n useCache: true,\n copyAttributes: true,\n makeIdsUnique: true,\n sanitize: false,\n injectStyleTag: true,\n};\n\n// Lazy-initialized at call time (SSR-safe)\nlet domParser: DOMParser | null = null;\nlet xmlSerializer: XMLSerializer | null = null;\nlet aElement: HTMLAnchorElement | null = null;\n\nfunction getAbsoluteUrl(url: string): string {\n aElement = aElement || document.createElement('a');\n aElement.href = url;\n return aElement.href;\n}\n\nfunction svgStringToSvgDoc(svgStr: string): Document {\n domParser = domParser || new DOMParser();\n return domParser.parseFromString(svgStr, 'text/xml');\n}\n\nfunction svgElemToSvgString(svgElement: Element): string {\n xmlSerializer = xmlSerializer || new XMLSerializer();\n return xmlSerializer.serializeToString(svgElement);\n}\n\nfunction buildSvgElement(svgStr: string, verify: boolean): Element | null {\n let svgDoc: Document;\n try {\n svgDoc = svgStringToSvgDoc(svgStr);\n } catch {\n return null;\n }\n if (verify && svgDoc.getElementsByTagName('parsererror').length) {\n return null;\n }\n // adoptNode moves the node into the current document, preserving ID registration\n // for getElementById. importNode can lose ID registration in some browsers.\n return document.adoptNode(svgDoc.documentElement);\n}\n\nasync function loadSvg(url: string): Promise<string> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to load SVG: ${response.status}`);\n }\n return (await response.text()).trim();\n}\n\nfunction mergeOptions(base: SVGInjectOptions, override?: SVGInjectOptions): SVGInjectOptions {\n if (!override) return base;\n return { ...base, ...override };\n}\n\nfunction addStyleToHead(css: string): void {\n const head = document.getElementsByTagName('head')[0];\n if (head) {\n const style = document.createElement('style');\n style.appendChild(document.createTextNode(css));\n head.appendChild(style);\n }\n}\n\nfunction isSvgElement(el: unknown): el is Element {\n return el != null && typeof el === 'object' && 'localName' in el && (el as Element).localName === 'svg';\n}\n\nfunction errorMessage(msg: string): void {\n console.error('SVGInject: ' + msg);\n}\n\n/**\n * Creates an SVGInject instance with its own cache and default options.\n */\nexport function createSVGInject(globalName: string, options?: SVGInjectOptions, _assignToWindow = true): SVGInjectFunction {\n let defaultOptions = mergeOptions({ ...DEFAULT_OPTIONS }, options);\n const svgLoadCache = new Map<string, CacheEntry>();\n\n // Inject style tag immediately to prevent unstyled image flash.\n // Must happen at script load time, before any <img> onload fires.\n if (defaultOptions.injectStyleTag && typeof document !== 'undefined') {\n addStyleToHead('img[onload^=\"' + globalName + '(\"]{visibility:hidden;}');\n }\n\n function fail(imgElem: HTMLImageElement, status: FailStatus, opts: SVGInjectOptions): void {\n (imgElem as any)[__SVGINJECT] = FAIL;\n if (opts.onFail) {\n opts.onFail(imgElem, status);\n } else {\n errorMessage(status);\n }\n }\n\n function inject(\n imgElem: HTMLImageElement,\n svgElem: Element | null,\n absUrl: string,\n opts: SVGInjectOptions,\n ): void {\n if (!svgElem) {\n imgElem.removeAttribute('onload');\n fail(imgElem, SVG_INVALID, opts);\n return;\n }\n\n svgElem.setAttribute('data-inject-url', absUrl);\n const parentNode = imgElem.parentNode;\n if (!parentNode) return;\n\n if (opts.copyAttributes) {\n copyAttrs(imgElem, svgElem);\n }\n\n const injectElem = (opts.beforeInject && opts.beforeInject(imgElem, svgElem as SVGSVGElement)) || svgElem;\n parentNode.replaceChild(injectElem, imgElem);\n (imgElem as any)[__SVGINJECT] = INJECTED;\n imgElem.removeAttribute('onload');\n\n if (opts.afterInject) {\n opts.afterInject(imgElem, injectElem);\n }\n }\n\n async function injectElement(imgElem: HTMLImageElement, opts: SVGInjectOptions): Promise<void> {\n if (!imgElem) {\n errorMessage('no img element');\n return;\n }\n\n const svgInjectValue = (imgElem as any)[__SVGINJECT];\n if (!svgInjectValue) {\n // Clear JS event handlers (not just HTML attributes) on first processing\n imgElem.onload = null;\n imgElem.onerror = null;\n }\n if (svgInjectValue) {\n // Already injected or failed — if pending, wait for it\n if (svgInjectValue instanceof Promise) {\n await svgInjectValue;\n }\n return;\n }\n\n // Check SVG support\n if (typeof SVGRect === 'undefined') {\n imgElem.removeAttribute('onload');\n fail(imgElem, SVG_NOT_SUPPORTED, opts);\n return;\n }\n\n\n\n const beforeLoad = opts.beforeLoad;\n const src = (beforeLoad && beforeLoad(imgElem)) || imgElem.getAttribute('src');\n\n if (src === null || src === undefined) {\n return;\n }\n if (src === '') {\n fail(imgElem, LOAD_FAIL, opts);\n return;\n }\n\n const absUrl = getAbsoluteUrl(src);\n const useCache = opts.useCache;\n const doMakeIdsUnique = opts.makeIdsUnique;\n\n // Mark as in-progress with a promise\n const { promise, resolve } = createDeferred();\n (imgElem as any)[__SVGINJECT] = promise;\n\n try {\n let svgString: string;\n\n if (useCache) {\n svgString = await getCachedSvg(absUrl, opts);\n } else {\n svgString = await loadAndProcess(absUrl, opts);\n }\n\n // Parse fresh for each injection\n let svgElem = buildSvgElement(svgString, false);\n if (svgElem) {\n if (opts.sanitize) sanitizeSvg(svgElem);\n if (doMakeIdsUnique) makeIdsUnique(svgElem);\n }\n\n inject(imgElem, svgElem, absUrl, opts);\n } catch (err) {\n const status = (err instanceof SvgError) ? err.status : LOAD_FAIL;\n imgElem.removeAttribute('onload');\n fail(imgElem, status, opts);\n } finally {\n resolve();\n }\n }\n\n async function loadAndProcess(absUrl: string, opts: SVGInjectOptions): Promise<string> {\n let svgString: string;\n try {\n svgString = await loadSvg(absUrl);\n } catch {\n throw new SvgError(LOAD_FAIL);\n }\n\n // Verify it's valid SVG\n const svgElem = buildSvgElement(svgString, true);\n if (!svgElem || !isSvgElement(svgElem)) {\n throw new SvgError(SVG_INVALID);\n }\n\n // afterLoad hook\n if (opts.afterLoad) {\n const result = opts.afterLoad(svgElem as SVGSVGElement, svgString);\n if (result) {\n if (typeof result === 'string') {\n svgString = result;\n } else {\n svgString = svgElemToSvgString(result);\n }\n } else {\n // afterLoad may have modified svgElem in place\n svgString = svgElemToSvgString(svgElem);\n }\n }\n\n return svgString;\n }\n\n async function getCachedSvg(absUrl: string, opts: SVGInjectOptions): Promise<string> {\n const cached = svgLoadCache.get(absUrl);\n\n if (cached) {\n if (cached.type === 'loaded') {\n return cached.svgString;\n }\n if (cached.type === 'failed') {\n throw new SvgError(cached.status);\n }\n // Pending — wait for it\n return new Promise<string>((resolve, reject) => {\n cached.callbacks.push((entry: CacheEntry) => {\n if (entry.type === 'loaded') resolve(entry.svgString);\n else if (entry.type === 'failed') reject(new SvgError(entry.status));\n });\n });\n }\n\n // Not in cache — load it\n const pending: CacheEntry = { type: 'pending', callbacks: [] };\n svgLoadCache.set(absUrl, pending);\n\n try {\n const svgString = await loadAndProcess(absUrl, opts);\n const loaded: CacheEntry = { type: 'loaded', svgString };\n svgLoadCache.set(absUrl, loaded);\n pending.callbacks.forEach(cb => cb(loaded));\n return svgString;\n } catch (err) {\n const status = (err instanceof SvgError) ? err.status : LOAD_FAIL;\n const failed: CacheEntry = { type: 'failed', status };\n svgLoadCache.set(absUrl, failed);\n pending.callbacks.forEach(cb => cb(failed));\n throw err;\n }\n }\n\n // --- Public API ---\n\n function SVGInject(\n img: HTMLImageElement | HTMLImageElement[] | NodeListOf<HTMLImageElement>,\n options?: SVGInjectOptions,\n ): Promise<void> {\n const opts = mergeOptions(defaultOptions, options);\n\n const elements: HTMLImageElement[] = img instanceof HTMLImageElement\n ? [img]\n : Array.from(img as ArrayLike<HTMLImageElement>);\n\n const promises = elements.map(el => injectElement(el, opts));\n return Promise.all(promises).then(() => {\n if (opts.onAllFinish) opts.onAllFinish();\n });\n }\n\n SVGInject.setOptions = function (options: SVGInjectOptions): void {\n defaultOptions = mergeOptions(defaultOptions, options);\n };\n\n SVGInject.create = createSVGInject;\n\n SVGInject.err = function (img: HTMLImageElement, fallbackSrc?: string): void {\n if (!img) {\n errorMessage('no img element');\n return;\n }\n\n if ((img as any)[__SVGINJECT] === FAIL) return;\n\n img.onload = null;\n img.onerror = null;\n img.removeAttribute('onload');\n\n if (typeof SVGRect === 'undefined') {\n fail(img, SVG_NOT_SUPPORTED, defaultOptions);\n } else {\n fail(img, LOAD_FAIL, defaultOptions);\n }\n\n if (fallbackSrc) {\n img.src = fallbackSrc;\n }\n };\n\n // Assign to window for onload=\"Name(this)\" usage in HTML (v1 compat)\n if (_assignToWindow && typeof window !== 'undefined') {\n (window as any)[globalName] = SVGInject;\n }\n\n return SVGInject;\n}\n\n// --- Helper types ---\n\nclass SvgError extends Error {\n constructor(public status: FailStatus) {\n super(status);\n }\n}\n\nfunction createDeferred(): { promise: Promise<void>; resolve: () => void } {\n let resolve!: () => void;\n const promise = new Promise<void>(r => { resolve = r; });\n return { promise, resolve };\n}\n\n// Create the default instance — no window assignment here; the IIFE footer handles that.\n// User-created instances via SVGInject.create() DO assign to window (v1 compat).\nexport const SVGInject = createSVGInject('SVGInject', undefined, false);\n"],"mappings":"yaAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,eAAAE,EAAA,oBAAAC,EAAA,YAAAD,IAAA,eAAAE,EAAAJ,ICEA,IAAMK,EAAuC,CAC3C,QAAS,UACT,oBAAqB,sBACrB,cAAe,gBACf,YAAa,cACb,SAAU,WACV,cAAe,gBACf,gBAAiB,kBACjB,SAAU,WACV,YAAa,cACb,SAAU,WACV,kBAAmB,oBACnB,cAAe,gBACf,aAAc,eACd,iBAAkB,mBAClB,UAAW,YACX,WAAY,aACZ,SAAU,WACV,aAAc,eACd,kBAAmB,oBACnB,aAAc,eACd,YAAa,cACb,YAAa,cACb,iBAAkB,mBAClB,UAAW,YACX,WAAY,aACZ,WAAY,aACZ,oBAAqB,sBACrB,iBAAkB,mBAClB,aAAc,eACd,UAAW,YACX,UAAW,YACX,UAAW,YACX,eAAgB,iBAChB,KAAM,OACN,KAAM,OACN,YAAa,cACb,UAAW,YACX,mBAAoB,qBACpB,iBAAkB,mBAClB,iBAAkB,mBAClB,iBAAkB,mBAClB,aAAc,eACd,YAAa,cACb,aAAc,eACd,YAAa,cACb,aAAc,eACd,eAAgB,iBAChB,YAAa,cACb,QAAS,UACT,QAAS,UACT,WAAY,aACZ,iBAAkB,mBAClB,iBAAkB,mBAClB,WAAY,YACd,EAEMC,EAAsB,IAAI,IAAI,CAAC,MAAO,MAAO,SAAU,SAAS,CAAC,EAMhE,SAASC,EAAYC,EAAkBC,EAA0B,CACtE,IAAMC,EAAe,IAAI,IAEzB,QAAWC,IAAS,CAACH,EAAUC,CAAQ,EACrC,GAAKE,EACL,QAAWC,KAAQD,EAAM,MAAM,GAAG,EAAG,CACnC,IAAME,EAAQD,EAAK,QAAQ,GAAG,EAC9B,GAAIC,IAAU,GAAI,SAClB,IAAMC,EAAOF,EAAK,UAAU,EAAGC,CAAK,EAAE,KAAK,EACrCE,EAAMH,EAAK,UAAUC,EAAQ,CAAC,EAAE,KAAK,EACvCC,GACFJ,EAAa,IAAII,EAAMC,CAAG,CAE9B,CAGF,OAAIL,EAAa,OAAS,EAAU,GAC7B,MAAM,KAAKA,EAAa,QAAQ,CAAC,EACrC,IAAI,CAAC,CAACI,EAAMC,CAAG,IAAM,GAAGD,CAAI,KAAKC,CAAG,EAAE,EACtC,KAAK,IAAI,CACd,CAYO,SAASC,EAAeC,EAA2BC,EAAwB,CAChF,IAAMC,EAAMF,EAAQ,aAAa,KAAK,EAElCE,IAAQ,IAEVD,EAAQ,aAAa,OAAQ,MAAM,EACnCA,EAAQ,aAAa,cAAe,MAAM,IAG1CA,EAAQ,aAAa,OAAQ,KAAK,EAC9BC,IAAQ,MACVD,EAAQ,aAAa,aAAcC,CAAG,GAI1C,IAAMC,EAAaH,EAAQ,WAC3B,QAASI,EAAI,EAAGA,EAAID,EAAW,OAAQC,IAAK,CAC1C,IAAMC,EAAYF,EAAWC,CAAC,EAC1BE,EAAgBD,EAAU,KAE9B,GAAIhB,EAAoB,IAAIiB,CAAa,EAAG,SAE5C,IAAMC,EAAiBF,EAAU,MAEjC,GAAIC,IAAkB,QAAS,CAE7B,IAAME,EAAaP,EAAQ,kBACvBQ,EAEAD,GAAcA,EAAW,UAAU,YAAY,IAAM,QACvDC,EAAYD,GAEZC,GAAaR,EAAQ,eAAiB,UAAU,gBAAgB,6BAA8B,OAAO,EACrGA,EAAQ,aAAaQ,EAAWD,CAAU,GAE5CC,EAAU,YAAcF,CAC1B,SAAWD,IAAkB,QAAS,CAEpC,IAAMf,EAAWU,EAAQ,aAAa,OAAO,GAAK,GAC5CS,EAASpB,EAAYC,EAAUgB,CAAc,EAC/CG,GACFT,EAAQ,aAAa,QAASS,CAAM,CAExC,MAEEJ,EAAgBlB,EAAakB,CAAa,GAAKA,EAC/CL,EAAQ,aAAaK,EAAeC,CAAc,CAEtD,CACF,CClJA,IAAMI,EAAY,YAIZC,EAA0D,CAC9D,SAAU,CAAC,WAAW,EACtB,gBAAiB,KACjB,OAAQ,KACR,OAAQ,KACR,eAAgB,CAAC,OAAQ,QAAQ,EACjC,OAAQ,CAAC,SAAU,aAAc,aAAc,cAAc,EAC7D,KAAM,KACN,QAAS,CAAC,OAAQ,QAAQ,EAC1B,eAAgB,CAAC,OAAQ,QAAQ,CACnC,EAGMC,GAAyB,CAC7B,kBACA,mBACA,gBACA,YACA,cACA,wBACA,oBACA,cACF,EAIMC,EAAiB,wCAEnBC,GAAkB,EAef,SAASC,EAAcC,EAA2B,CACvD,IAAMC,EAAWC,EAAYC,KACvBC,EAAaJ,EAAQ,iBAAiB,MAAM,EAElD,GAAII,EAAW,SAAW,EAAG,MAAO,GAGpC,IAAMC,EAAc,IAAI,IAClBC,EAAc,IAAI,IAExB,QAAS,EAAI,EAAG,EAAIF,EAAW,OAAQ,IAAK,CAC1CC,EAAY,IAAID,EAAW,CAAC,EAAE,EAAE,EAChC,IAAMG,EAAUH,EAAW,CAAC,EAAE,UAC1BG,KAAWC,GACbF,EAAY,IAAIC,CAAO,CAE3B,CAGA,IAAME,EAA0B,CAAC,EACjC,QAAWF,KAAWD,EAAa,CACjC,IAAMI,EAASF,EAAuBD,CAAO,GAAK,CAACA,CAAO,EAC1D,QAAWI,KAAQD,EACZD,EAAc,SAASE,CAAI,GAC9BF,EAAc,KAAKE,CAAI,CAG7B,CACIF,EAAc,OAAS,GACzBA,EAAc,KAAK,OAAO,EAI5B,IAAMG,EAAgB,CAACC,EAAgBC,IAC9B,QAAUA,EAAKb,EAAW,IAI7Bc,EAAcf,EAAQ,qBAAqB,GAAG,EAGpD,QAAS,EAAI,GAAI,EAAIe,EAAY,OAAQ,IAAK,CAC5C,IAAMC,EAAU,IAAM,GAAKhB,EAAUe,EAAY,CAAC,EAElD,GAAIC,EAAQ,YAAc,QAAS,CAEjC,IAAMC,EAAOD,EAAQ,YACrB,GAAIC,EAAM,CACR,IAAMC,EAAUD,EAAK,QAAQE,EAAgBP,CAAa,EACtDM,IAAYD,IACdD,EAAQ,YAAcE,EAE1B,CACF,SAAWF,EAAQ,cAAc,EAAG,CAElC,QAAWI,KAAgBX,EAAe,CACxC,IAAMY,EAAQL,EAAQ,aAAaI,CAAY,EAC/C,GAAIC,EAAO,CACT,IAAMC,EAAWD,EAAM,QAAQF,EAAgBP,CAAa,EACxDU,IAAaD,GACfL,EAAQ,aAAaI,EAAcE,CAAQ,CAE/C,CACF,CAGA,QAAWC,IAAW,CAAC,aAAc,MAAM,EAAG,CAC5C,IAAMC,EAAMR,EAAQ,aAAaO,CAAO,EACpCC,GAAO,QAAQ,KAAKA,CAAG,GACzBR,EAAQ,aAAaO,EAASC,EAAI,KAAK,EAAIvB,CAAQ,CAEvD,CAGA,QAAWwB,KAAYC,GAAwB,CAC7C,IAAML,EAAQL,EAAQ,aAAaS,CAAQ,EAC3C,GAAIJ,EAAO,CAGT,IAAMC,EAFMD,EAAM,MAAM,KAAK,EACV,IAAIP,GAAMT,EAAY,IAAIS,CAAE,EAAIA,EAAKb,EAAWa,CAAE,EAC7C,KAAK,GAAG,EAC5BQ,IAAaD,GACfL,EAAQ,aAAaS,EAAUH,CAAQ,CAE3C,CACF,CACF,CACF,CAGA,QAAS,EAAI,EAAG,EAAIlB,EAAW,OAAQ,IACrCA,EAAW,CAAC,EAAE,IAAMH,EAGtB,MAAO,EACT,CC5IA,IAAM0B,GAAqB,IAAI,IAAI,CACjC,SACA,eACF,CAAC,EAGKC,GAAgB,sCAOf,SAASC,EAAYC,EAAwB,CAdpD,IAAAC,EAgBE,IAAMC,EAAcF,EAAQ,qBAAqB,GAAG,EAE9CG,EAAsB,CAAC,EAC7B,QAASC,EAAI,EAAGA,EAAIF,EAAY,OAAQE,IAClCP,GAAmB,IAAIK,EAAYE,CAAC,EAAE,UAAU,YAAY,CAAC,GAC/DD,EAAS,KAAKD,EAAYE,CAAC,CAAC,EAGhC,QAAWC,KAAMF,GACfF,EAAAI,EAAG,aAAH,MAAAJ,EAAe,YAAYI,GAI7B,IAAMC,EAAYN,EAAQ,qBAAqB,GAAG,EAClD,QAASI,EAAI,GAAIA,EAAIE,EAAU,OAAQF,IAAK,CAC1C,IAAMC,EAAKD,IAAM,GAAKJ,EAAUM,EAAUF,CAAC,EAC3C,GAAI,CAACC,EAAG,cAAc,EAAG,SAGzB,IAAME,EAA0B,CAAC,EACjC,QAASC,EAAI,EAAGA,EAAIH,EAAG,WAAW,OAAQG,IAAK,CAC7C,IAAMC,EAAOJ,EAAG,WAAWG,CAAC,EACtBE,EAAOD,EAAK,KAAK,YAAY,EAGnC,GAAIC,EAAK,WAAW,IAAI,EAAG,CACzBH,EAAc,KAAKE,EAAK,IAAI,EAC5B,QACF,EAGKC,IAAS,QAAUA,IAAS,eAAiBZ,GAAc,KAAKW,EAAK,KAAK,GAC7EF,EAAc,KAAKE,EAAK,IAAI,CAEhC,CAEA,QAAWE,KAAYJ,EACrBF,EAAG,gBAAgBM,CAAQ,CAE/B,CACF,CCnDA,IAAMC,EAAwB,YACxBC,EAAgC,oBAChCC,EAA0B,cAE1BC,EAAc,cACdC,GAAW,EACXC,EAAO,EAEPC,GAAqI,CACzI,SAAU,GACV,eAAgB,GAChB,cAAe,GACf,SAAU,GACV,eAAgB,EAClB,EAGIC,EAA8B,KAC9BC,EAAsC,KACtCC,EAAqC,KAEzC,SAASC,GAAeC,EAAqB,CAC3C,OAAAF,EAAWA,GAAY,SAAS,cAAc,GAAG,EACjDA,EAAS,KAAOE,EACTF,EAAS,IAClB,CAEA,SAASG,GAAkBC,EAA0B,CACnD,OAAAN,EAAYA,GAAa,IAAI,UACtBA,EAAU,gBAAgBM,EAAQ,UAAU,CACrD,CAEA,SAASC,EAAmBC,EAA6B,CACvD,OAAAP,EAAgBA,GAAiB,IAAI,cAC9BA,EAAc,kBAAkBO,CAAU,CACnD,CAEA,SAASC,EAAgBH,EAAgBI,EAAiC,CACxE,IAAIC,EACJ,GAAI,CACFA,EAASN,GAAkBC,CAAM,CACnC,OAAQM,EAAA,CACN,OAAO,IACT,CACA,OAAIF,GAAUC,EAAO,qBAAqB,aAAa,EAAE,OAChD,KAIF,SAAS,UAAUA,EAAO,eAAe,CAClD,CAEA,eAAeE,GAAQT,EAA8B,CACnD,IAAMU,EAAW,MAAM,MAAMV,CAAG,EAChC,GAAI,CAACU,EAAS,GACZ,MAAM,IAAI,MAAM,uBAAuBA,EAAS,MAAM,EAAE,EAE1D,OAAQ,MAAMA,EAAS,KAAK,GAAG,KAAK,CACtC,CAEA,SAASC,EAAaC,EAAwBC,EAA+C,CAC3F,OAAKA,EACE,CAAE,GAAGD,EAAM,GAAGC,CAAS,EADRD,CAExB,CAEA,SAASE,GAAeC,EAAmB,CACzC,IAAMC,EAAO,SAAS,qBAAqB,MAAM,EAAE,CAAC,EACpD,GAAIA,EAAM,CACR,IAAMC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAY,SAAS,eAAeF,CAAG,CAAC,EAC9CC,EAAK,YAAYC,CAAK,CACxB,CACF,CAEA,SAASC,GAAaC,EAA4B,CAChD,OAAOA,GAAM,MAAQ,OAAOA,GAAO,UAAY,cAAeA,GAAOA,EAAe,YAAc,KACpG,CAEA,SAASC,EAAaC,EAAmB,CACvC,QAAQ,MAAM,cAAgBA,CAAG,CACnC,CAKO,SAASC,EAAgBC,EAAoBC,EAA4BC,EAAkB,GAAyB,CACzH,IAAIC,EAAiBf,EAAa,CAAE,GAAGhB,EAAgB,EAAG6B,CAAO,EAC3DG,EAAe,IAAI,IAIrBD,EAAe,gBAAkB,OAAO,UAAa,aACvDZ,GAAe,gBAAkBS,EAAa,yBAAyB,EAGzE,SAASK,EAAKC,EAA2BC,EAAoBC,EAA8B,CACxFF,EAAgBrC,CAAW,EAAIE,EAC5BqC,EAAK,OACPA,EAAK,OAAOF,EAASC,CAAM,EAE3BV,EAAaU,CAAM,CAEvB,CAEA,SAASE,EACPH,EACAI,EACAC,EACAH,EACM,CACN,GAAI,CAACE,EAAS,CACZJ,EAAQ,gBAAgB,QAAQ,EAChCD,EAAKC,EAAStC,EAAawC,CAAI,EAC/B,MACF,CAEAE,EAAQ,aAAa,kBAAmBC,CAAM,EAC9C,IAAMC,EAAaN,EAAQ,WAC3B,GAAI,CAACM,EAAY,OAEbJ,EAAK,gBACPK,EAAUP,EAASI,CAAO,EAG5B,IAAMI,EAAcN,EAAK,cAAgBA,EAAK,aAAaF,EAASI,CAAwB,GAAMA,EAClGE,EAAW,aAAaE,EAAYR,CAAO,EAC1CA,EAAgBrC,CAAW,EAAIC,GAChCoC,EAAQ,gBAAgB,QAAQ,EAE5BE,EAAK,aACPA,EAAK,YAAYF,EAASQ,CAAU,CAExC,CAEA,eAAeC,EAAcT,EAA2BE,EAAuC,CAC7F,GAAI,CAACF,EAAS,CACZT,EAAa,gBAAgB,EAC7B,MACF,CAEA,IAAMmB,EAAkBV,EAAgBrC,CAAW,EAMnD,GALK+C,IAEHV,EAAQ,OAAS,KACjBA,EAAQ,QAAU,MAEhBU,EAAgB,CAEdA,aAA0B,SAC5B,MAAMA,EAER,MACF,CAGA,GAAI,OAAO,SAAY,YAAa,CAClCV,EAAQ,gBAAgB,QAAQ,EAChCD,EAAKC,EAASvC,EAAmByC,CAAI,EACrC,MACF,CAIA,IAAMS,EAAaT,EAAK,WAClBU,EAAOD,GAAcA,EAAWX,CAAO,GAAMA,EAAQ,aAAa,KAAK,EAE7E,GAAIY,GAAQ,KACV,OAEF,GAAIA,IAAQ,GAAI,CACdb,EAAKC,EAASxC,EAAW0C,CAAI,EAC7B,MACF,CAEA,IAAMG,EAASnC,GAAe0C,CAAG,EAC3BC,EAAWX,EAAK,SAChBY,EAAkBZ,EAAK,cAGvB,CAAE,QAAAa,EAAS,QAAAC,CAAQ,EAAIC,GAAe,EAC3CjB,EAAgBrC,CAAW,EAAIoD,EAEhC,GAAI,CACF,IAAIG,EAEAL,EACFK,EAAY,MAAMC,EAAad,EAAQH,CAAI,EAE3CgB,EAAY,MAAME,EAAef,EAAQH,CAAI,EAI/C,IAAIE,EAAU5B,EAAgB0C,EAAW,EAAK,EAC1Cd,IACEF,EAAK,UAAUmB,EAAYjB,CAAO,EAClCU,GAAiBQ,EAAclB,CAAO,GAG5CD,EAAOH,EAASI,EAASC,EAAQH,CAAI,CACvC,OAASqB,EAAK,CACZ,IAAMtB,EAAUsB,aAAeC,EAAYD,EAAI,OAAS/D,EACxDwC,EAAQ,gBAAgB,QAAQ,EAChCD,EAAKC,EAASC,EAAQC,CAAI,CAC5B,QAAE,CACAc,EAAQ,CACV,CACF,CAEA,eAAeI,EAAef,EAAgBH,EAAyC,CACrF,IAAIgB,EACJ,GAAI,CACFA,EAAY,MAAMtC,GAAQyB,CAAM,CAClC,OAAQ1B,EAAA,CACN,MAAM,IAAI6C,EAAShE,CAAS,CAC9B,CAGA,IAAM4C,EAAU5B,EAAgB0C,EAAW,EAAI,EAC/C,GAAI,CAACd,GAAW,CAACf,GAAae,CAAO,EACnC,MAAM,IAAIoB,EAAS9D,CAAW,EAIhC,GAAIwC,EAAK,UAAW,CAClB,IAAMuB,EAASvB,EAAK,UAAUE,EAA0Bc,CAAS,EAC7DO,EACE,OAAOA,GAAW,SACpBP,EAAYO,EAEZP,EAAY5C,EAAmBmD,CAAM,EAIvCP,EAAY5C,EAAmB8B,CAAO,CAE1C,CAEA,OAAOc,CACT,CAEA,eAAeC,EAAad,EAAgBH,EAAyC,CACnF,IAAMwB,EAAS5B,EAAa,IAAIO,CAAM,EAEtC,GAAIqB,EAAQ,CACV,GAAIA,EAAO,OAAS,SAClB,OAAOA,EAAO,UAEhB,GAAIA,EAAO,OAAS,SAClB,MAAM,IAAIF,EAASE,EAAO,MAAM,EAGlC,OAAO,IAAI,QAAgB,CAACV,EAASW,IAAW,CAC9CD,EAAO,UAAU,KAAME,GAAsB,CACvCA,EAAM,OAAS,SAAUZ,EAAQY,EAAM,SAAS,EAC3CA,EAAM,OAAS,UAAUD,EAAO,IAAIH,EAASI,EAAM,MAAM,CAAC,CACrE,CAAC,CACH,CAAC,CACH,CAGA,IAAMC,EAAsB,CAAE,KAAM,UAAW,UAAW,CAAC,CAAE,EAC7D/B,EAAa,IAAIO,EAAQwB,CAAO,EAEhC,GAAI,CACF,IAAMX,EAAY,MAAME,EAAef,EAAQH,CAAI,EAC7C4B,EAAqB,CAAE,KAAM,SAAU,UAAAZ,CAAU,EACvD,OAAApB,EAAa,IAAIO,EAAQyB,CAAM,EAC/BD,EAAQ,UAAU,QAAQE,GAAMA,EAAGD,CAAM,CAAC,EACnCZ,CACT,OAASK,EAAK,CAEZ,IAAMS,EAAqB,CAAE,KAAM,SAAU,OAD7BT,aAAeC,EAAYD,EAAI,OAAS/D,CACJ,EACpD,MAAAsC,EAAa,IAAIO,EAAQ2B,CAAM,EAC/BH,EAAQ,UAAU,QAAQE,GAAMA,EAAGC,CAAM,CAAC,EACpCT,CACR,CACF,CAIA,SAASU,EACPC,EACAvC,EACe,CACf,IAAMO,EAAOpB,EAAae,EAAgBF,CAAO,EAM3CwC,GAJ+BD,aAAe,iBAChD,CAACA,CAAG,EACJ,MAAM,KAAKA,CAAkC,GAEvB,IAAI5C,GAAMmB,EAAcnB,EAAIY,CAAI,CAAC,EAC3D,OAAO,QAAQ,IAAIiC,CAAQ,EAAE,KAAK,IAAM,CAClCjC,EAAK,aAAaA,EAAK,YAAY,CACzC,CAAC,CACH,CAEA,OAAA+B,EAAU,WAAa,SAAUtC,EAAiC,CAChEE,EAAiBf,EAAae,EAAgBF,CAAO,CACvD,EAEAsC,EAAU,OAASxC,EAEnBwC,EAAU,IAAM,SAAUC,EAAuBE,EAA4B,CAC3E,GAAI,CAACF,EAAK,CACR3C,EAAa,gBAAgB,EAC7B,MACF,CAEK2C,EAAYvE,CAAW,IAAME,IAElCqE,EAAI,OAAS,KACbA,EAAI,QAAU,KACdA,EAAI,gBAAgB,QAAQ,EAExB,OAAO,SAAY,YACrBnC,EAAKmC,EAAKzE,EAAmBoC,CAAc,EAE3CE,EAAKmC,EAAK1E,EAAWqC,CAAc,EAGjCuC,IACFF,EAAI,IAAME,GAEd,EAGIxC,GAAmB,OAAO,QAAW,cACtC,OAAeF,CAAU,EAAIuC,GAGzBA,CACT,CAIA,IAAMT,EAAN,cAAuB,KAAM,CAC3B,YAAmBvB,EAAoB,CACrC,MAAMA,CAAM,EADK,YAAAA,CAEnB,CACF,EAEA,SAASgB,IAAkE,CACzE,IAAID,EAEJ,MAAO,CAAE,QADO,IAAI,QAAc,GAAK,CAAEA,EAAU,CAAG,CAAC,EACrC,QAAAA,CAAQ,CAC5B,CAIO,IAAMiB,EAAYxC,EAAgB,YAAa,OAAW,EAAK","names":["src_exports","__export","SVGInject","createSVGInject","__toCommonJS","SVG_CASE_MAP","EXCLUDED_ATTRIBUTES","mergeStyles","svgStyle","imgStyle","declarations","style","decl","colon","prop","val","copyAttributes","imgElem","svgElem","alt","attributes","i","attribute","attributeName","attributeValue","firstChild","titleElem","merged","ID_SUFFIX","IRI_TAG_PROPERTIES_MAP","ARIA_ID_REF_ATTRIBUTES","FUNC_IRI_REGEX","uniqueIdCounter","makeIdsUnique","svgElem","idSuffix","ID_SUFFIX","uniqueIdCounter","idElements","existingIds","iriTagNames","tagName","IRI_TAG_PROPERTIES_MAP","iriProperties","mapped","prop","replaceIriRef","_match","id","descendants","element","text","newText","FUNC_IRI_REGEX","propertyName","value","newValue","refAttr","iri","ariaAttr","ARIA_ID_REF_ATTRIBUTES","DANGEROUS_ELEMENTS","DANGEROUS_URI","sanitizeSvg","svgElem","_a","allElements","toRemove","i","el","remaining","attrsToRemove","j","attr","name","attrName","LOAD_FAIL","SVG_NOT_SUPPORTED","SVG_INVALID","__SVGINJECT","INJECTED","FAIL","DEFAULT_OPTIONS","domParser","xmlSerializer","aElement","getAbsoluteUrl","url","svgStringToSvgDoc","svgStr","svgElemToSvgString","svgElement","buildSvgElement","verify","svgDoc","e","loadSvg","response","mergeOptions","base","override","addStyleToHead","css","head","style","isSvgElement","el","errorMessage","msg","createSVGInject","globalName","options","_assignToWindow","defaultOptions","svgLoadCache","fail","imgElem","status","opts","inject","svgElem","absUrl","parentNode","copyAttributes","injectElem","injectElement","svgInjectValue","beforeLoad","src","useCache","doMakeIdsUnique","promise","resolve","createDeferred","svgString","getCachedSvg","loadAndProcess","sanitizeSvg","makeIdsUnique","err","SvgError","result","cached","reject","entry","pending","loaded","cb","failed","SVGInject","img","promises","fallbackSrc"]}