diff --git a/public/img/ai21.svg b/public/img/ai21.svg
new file mode 100644
index 000000000..891beda5a
--- /dev/null
+++ b/public/img/ai21.svg
@@ -0,0 +1,20 @@
+
diff --git a/public/index.html b/public/index.html
index 512ca6368..7be7bf3e7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -44,6 +44,7 @@
+
diff --git a/public/lib/svg-inject.js b/public/lib/svg-inject.js
new file mode 100644
index 000000000..4e74af07c
--- /dev/null
+++ b/public/lib/svg-inject.js
@@ -0,0 +1,697 @@
+/**
+ * SVGInject - Version 1.2.3
+ * A tiny, intuitive, robust, caching solution for injecting SVG files inline into the DOM.
+ *
+ * https://github.com/iconfu/svg-inject
+ *
+ * Copyright (c) 2018 INCORS, the creators of iconfu.com
+ * @license MIT License - https://github.com/iconfu/svg-inject/blob/master/LICENSE
+ */
+
+(function(window, document) {
+ // constants for better minification
+ var _CREATE_ELEMENT_ = 'createElement';
+ var _GET_ELEMENTS_BY_TAG_NAME_ = 'getElementsByTagName';
+ var _LENGTH_ = 'length';
+ var _STYLE_ = 'style';
+ var _TITLE_ = 'title';
+ var _UNDEFINED_ = 'undefined';
+ var _SET_ATTRIBUTE_ = 'setAttribute';
+ var _GET_ATTRIBUTE_ = 'getAttribute';
+
+ var NULL = null;
+
+ // constants
+ var __SVGINJECT = '__svgInject';
+ var ID_SUFFIX = '--inject-';
+ var ID_SUFFIX_REGEX = new RegExp(ID_SUFFIX + '\\d+', "g");
+ var LOAD_FAIL = 'LOAD_FAIL';
+ var SVG_NOT_SUPPORTED = 'SVG_NOT_SUPPORTED';
+ var SVG_INVALID = 'SVG_INVALID';
+ var ATTRIBUTE_EXCLUSION_NAMES = ['src', 'alt', 'onload', 'onerror'];
+ var A_ELEMENT = document[_CREATE_ELEMENT_]('a');
+ var IS_SVG_SUPPORTED = typeof SVGRect != _UNDEFINED_;
+ var DEFAULT_OPTIONS = {
+ useCache: true,
+ copyAttributes: true,
+ makeIdsUnique: true
+ };
+ // Map of IRI referenceable tag names to properties that can reference them. This is defined in
+ // https://www.w3.org/TR/SVG11/linking.html#processingIRI
+ var IRI_TAG_PROPERTIES_MAP = {
+ clipPath: ['clip-path'],
+ 'color-profile': NULL,
+ cursor: NULL,
+ filter: NULL,
+ linearGradient: ['fill', 'stroke'],
+ marker: ['marker', 'marker-end', 'marker-mid', 'marker-start'],
+ mask: NULL,
+ pattern: ['fill', 'stroke'],
+ radialGradient: ['fill', 'stroke']
+ };
+ var INJECTED = 1;
+ var FAIL = 2;
+
+ var uniqueIdCounter = 1;
+ var xmlSerializer;
+ var domParser;
+
+
+ // creates an SVG document from an SVG string
+ function svgStringToSvgDoc(svgStr) {
+ domParser = domParser || new DOMParser();
+ return domParser.parseFromString(svgStr, 'text/xml');
+ }
+
+
+ // searializes an SVG element to an SVG string
+ function svgElemToSvgString(svgElement) {
+ xmlSerializer = xmlSerializer || new XMLSerializer();
+ return xmlSerializer.serializeToString(svgElement);
+ }
+
+
+ // Returns the absolute url for the specified url
+ function getAbsoluteUrl(url) {
+ A_ELEMENT.href = url;
+ return A_ELEMENT.href;
+ }
+
+
+ // Load svg with an XHR request
+ function loadSvg(url, callback, errorCallback) {
+ if (url) {
+ var req = new XMLHttpRequest();
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ // readyState is DONE
+ var status = req.status;
+ if (status == 200) {
+ // request status is OK
+ callback(req.responseXML, req.responseText.trim());
+ } else if (status >= 400) {
+ // request status is error (4xx or 5xx)
+ errorCallback();
+ } else if (status == 0) {
+ // request status 0 can indicate a failed cross-domain call
+ errorCallback();
+ }
+ }
+ };
+ req.open('GET', url, true);
+ req.send();
+ }
+ }
+
+
+ // Copy attributes from img element to svg element
+ function copyAttributes(imgElem, svgElem) {
+ var attribute;
+ var attributeName;
+ var attributeValue;
+ var attributes = imgElem.attributes;
+ for (var i = 0; i < attributes[_LENGTH_]; i++) {
+ attribute = attributes[i];
+ attributeName = attribute.name;
+ // Only copy attributes not explicitly excluded from copying
+ if (ATTRIBUTE_EXCLUSION_NAMES.indexOf(attributeName) == -1) {
+ attributeValue = attribute.value;
+ // If img attribute is "title", insert a title element into SVG element
+ if (attributeName == _TITLE_) {
+ var titleElem;
+ var firstElementChild = svgElem.firstElementChild;
+ if (firstElementChild && firstElementChild.localName.toLowerCase() == _TITLE_) {
+ // If the SVG element's first child is a title element, keep it as the title element
+ titleElem = firstElementChild;
+ } else {
+ // If the SVG element's first child element is not a title element, create a new title
+ // ele,emt and set it as the first child
+ titleElem = document[_CREATE_ELEMENT_ + 'NS']('http://www.w3.org/2000/svg', _TITLE_);
+ svgElem.insertBefore(titleElem, firstElementChild);
+ }
+ // Set new title content
+ titleElem.textContent = attributeValue;
+ } else {
+ // Set img attribute to svg element
+ svgElem[_SET_ATTRIBUTE_](attributeName, attributeValue);
+ }
+ }
+ }
+ }
+
+
+ // This function appends a suffix to IDs of referenced elements in the in order to to avoid ID collision
+ // between multiple injected SVGs. The suffix has the form "--inject-X", where X is a running number which is
+ // incremented with each injection. References to the IDs are adjusted accordingly.
+ // We assume tha all IDs within the injected SVG are unique, therefore the same suffix can be used for all IDs of one
+ // injected SVG.
+ // If the onlyReferenced argument is set to true, only those IDs will be made unique that are referenced from within the SVG
+ function makeIdsUnique(svgElem, onlyReferenced) {
+ var idSuffix = ID_SUFFIX + uniqueIdCounter++;
+ // Regular expression for functional notations of an IRI references. This will find occurences in the form
+ // url(#anyId) or url("#anyId") (for Internet Explorer) and capture the referenced ID
+ var funcIriRegex = /url\("?#([a-zA-Z][\w:.-]*)"?\)/g;
+ // Get all elements with an ID. The SVG spec recommends to put referenced elements inside elements, but
+ // this is not a requirement, therefore we have to search for IDs in the whole SVG.
+ var idElements = svgElem.querySelectorAll('[id]');
+ var idElem;
+ // An object containing referenced IDs as keys is used if only referenced IDs should be uniquified.
+ // If this object does not exist, all IDs will be uniquified.
+ var referencedIds = onlyReferenced ? [] : NULL;
+ var tagName;
+ var iriTagNames = {};
+ var iriProperties = [];
+ var changed = false;
+ var i, j;
+
+ if (idElements[_LENGTH_]) {
+ // Make all IDs unique by adding the ID suffix and collect all encountered tag names
+ // that are IRI referenceable from properities.
+ for (i = 0; i < idElements[_LENGTH_]; i++) {
+ tagName = idElements[i].localName; // Use non-namespaced tag name
+ // Make ID unique if tag name is IRI referenceable
+ if (tagName in IRI_TAG_PROPERTIES_MAP) {
+ iriTagNames[tagName] = 1;
+ }
+ }
+ // Get all properties that are mapped to the found IRI referenceable tags
+ for (tagName in iriTagNames) {
+ (IRI_TAG_PROPERTIES_MAP[tagName] || [tagName]).forEach(function (mappedProperty) {
+ // Add mapped properties to array of iri referencing properties.
+ // Use linear search here because the number of possible entries is very small (maximum 11)
+ if (iriProperties.indexOf(mappedProperty) < 0) {
+ iriProperties.push(mappedProperty);
+ }
+ });
+ }
+ if (iriProperties[_LENGTH_]) {
+ // Add "style" to properties, because it may contain references in the form 'style="fill:url(#myFill)"'
+ iriProperties.push(_STYLE_);
+ }
+ // Run through all elements of the SVG and replace IDs in references.
+ // To get all descending elements, getElementsByTagName('*') seems to perform faster than querySelectorAll('*').
+ // Since svgElem.getElementsByTagName('*') does not return the svg element itself, we have to handle it separately.
+ var descElements = svgElem[_GET_ELEMENTS_BY_TAG_NAME_]('*');
+ var element = svgElem;
+ var propertyName;
+ var value;
+ var newValue;
+ for (i = -1; element != NULL;) {
+ if (element.localName == _STYLE_) {
+ // If element is a style element, replace IDs in all occurences of "url(#anyId)" in text content
+ value = element.textContent;
+ newValue = value && value.replace(funcIriRegex, function(match, id) {
+ if (referencedIds) {
+ referencedIds[id] = 1;
+ }
+ return 'url(#' + id + idSuffix + ')';
+ });
+ if (newValue !== value) {
+ element.textContent = newValue;
+ }
+ } else if (element.hasAttributes()) {
+ // Run through all property names for which IDs were found
+ for (j = 0; j < iriProperties[_LENGTH_]; j++) {
+ propertyName = iriProperties[j];
+ value = element[_GET_ATTRIBUTE_](propertyName);
+ newValue = value && value.replace(funcIriRegex, function(match, id) {
+ if (referencedIds) {
+ referencedIds[id] = 1;
+ }
+ return 'url(#' + id + idSuffix + ')';
+ });
+ if (newValue !== value) {
+ element[_SET_ATTRIBUTE_](propertyName, newValue);
+ }
+ }
+ // Replace IDs in xlink:ref and href attributes
+ ['xlink:href', 'href'].forEach(function(refAttrName) {
+ var iri = element[_GET_ATTRIBUTE_](refAttrName);
+ if (/^\s*#/.test(iri)) { // Check if iri is non-null and internal reference
+ iri = iri.trim();
+ element[_SET_ATTRIBUTE_](refAttrName, iri + idSuffix);
+ if (referencedIds) {
+ // Add ID to referenced IDs
+ referencedIds[iri.substring(1)] = 1;
+ }
+ }
+ });
+ }
+ element = descElements[++i];
+ }
+ for (i = 0; i < idElements[_LENGTH_]; i++) {
+ idElem = idElements[i];
+ // If set of referenced IDs exists, make only referenced IDs unique,
+ // otherwise make all IDs unique.
+ if (!referencedIds || referencedIds[idElem.id]) {
+ // Add suffix to element's ID
+ idElem.id += idSuffix;
+ changed = true;
+ }
+ }
+ }
+ // return true if SVG element has changed
+ return changed;
+ }
+
+
+ // For cached SVGs the IDs are made unique by simply replacing the already inserted unique IDs with a
+ // higher ID counter. This is much more performant than a call to makeIdsUnique().
+ function makeIdsUniqueCached(svgString) {
+ return svgString.replace(ID_SUFFIX_REGEX, ID_SUFFIX + uniqueIdCounter++);
+ }
+
+
+ // Inject SVG by replacing the img element with the SVG element in the DOM
+ function inject(imgElem, svgElem, absUrl, options) {
+ if (svgElem) {
+ svgElem[_SET_ATTRIBUTE_]('data-inject-url', absUrl);
+ var parentNode = imgElem.parentNode;
+ if (parentNode) {
+ if (options.copyAttributes) {
+ copyAttributes(imgElem, svgElem);
+ }
+ // Invoke beforeInject hook if set
+ var beforeInject = options.beforeInject;
+ var injectElem = (beforeInject && beforeInject(imgElem, svgElem)) || svgElem;
+ // Replace img element with new element. This is the actual injection.
+ parentNode.replaceChild(injectElem, imgElem);
+ // Mark img element as injected
+ imgElem[__SVGINJECT] = INJECTED;
+ removeOnLoadAttribute(imgElem);
+ // Invoke afterInject hook if set
+ var afterInject = options.afterInject;
+ if (afterInject) {
+ afterInject(imgElem, injectElem);
+ }
+ }
+ } else {
+ svgInvalid(imgElem, options);
+ }
+ }
+
+
+ // Merges any number of options objects into a new object
+ function mergeOptions() {
+ var mergedOptions = {};
+ var args = arguments;
+ // Iterate over all specified options objects and add all properties to the new options object
+ for (var i = 0; i < args[_LENGTH_]; i++) {
+ var argument = args[i];
+ for (var key in argument) {
+ if (argument.hasOwnProperty(key)) {
+ mergedOptions[key] = argument[key];
+ }
+ }
+ }
+ return mergedOptions;
+ }
+
+
+ // Adds the specified CSS to the document's element
+ function addStyleToHead(css) {
+ var head = document[_GET_ELEMENTS_BY_TAG_NAME_]('head')[0];
+ if (head) {
+ var style = document[_CREATE_ELEMENT_](_STYLE_);
+ style.type = 'text/css';
+ style.appendChild(document.createTextNode(css));
+ head.appendChild(style);
+ }
+ }
+
+
+ // Builds an SVG element from the specified SVG string
+ function buildSvgElement(svgStr, verify) {
+ if (verify) {
+ var svgDoc;
+ try {
+ // Parse the SVG string with DOMParser
+ svgDoc = svgStringToSvgDoc(svgStr);
+ } catch(e) {
+ return NULL;
+ }
+ if (svgDoc[_GET_ELEMENTS_BY_TAG_NAME_]('parsererror')[_LENGTH_]) {
+ // DOMParser does not throw an exception, but instead puts parsererror tags in the document
+ return NULL;
+ }
+ return svgDoc.documentElement;
+ } else {
+ var div = document.createElement('div');
+ div.innerHTML = svgStr;
+ return div.firstElementChild;
+ }
+ }
+
+
+ function removeOnLoadAttribute(imgElem) {
+ // Remove the onload attribute. Should only be used to remove the unstyled image flash protection and
+ // make the element visible, not for removing the event listener.
+ imgElem.removeAttribute('onload');
+ }
+
+
+ function errorMessage(msg) {
+ console.error('SVGInject: ' + msg);
+ }
+
+
+ function fail(imgElem, status, options) {
+ imgElem[__SVGINJECT] = FAIL;
+ if (options.onFail) {
+ options.onFail(imgElem, status);
+ } else {
+ errorMessage(status);
+ }
+ }
+
+
+ function svgInvalid(imgElem, options) {
+ removeOnLoadAttribute(imgElem);
+ fail(imgElem, SVG_INVALID, options);
+ }
+
+
+ function svgNotSupported(imgElem, options) {
+ removeOnLoadAttribute(imgElem);
+ fail(imgElem, SVG_NOT_SUPPORTED, options);
+ }
+
+
+ function loadFail(imgElem, options) {
+ fail(imgElem, LOAD_FAIL, options);
+ }
+
+
+ function removeEventListeners(imgElem) {
+ imgElem.onload = NULL;
+ imgElem.onerror = NULL;
+ }
+
+
+ function imgNotSet(msg) {
+ errorMessage('no img element');
+ }
+
+
+ function createSVGInject(globalName, options) {
+ var defaultOptions = mergeOptions(DEFAULT_OPTIONS, options);
+ var svgLoadCache = {};
+
+ if (IS_SVG_SUPPORTED) {
+ // If the browser supports SVG, add a small stylesheet that hides the elements until
+ // injection is finished. This avoids showing the unstyled SVGs before style is applied.
+ addStyleToHead('img[onload^="' + globalName + '("]{visibility:hidden;}');
+ }
+
+
+ /**
+ * SVGInject
+ *
+ * Injects the SVG specified in the `src` attribute of the specified `img` element or array of `img`
+ * elements. Returns a Promise object which resolves if all passed in `img` elements have either been
+ * injected or failed to inject (Only if a global Promise object is available like in all modern browsers
+ * or through a polyfill).
+ *
+ * Options:
+ * useCache: If set to `true` the SVG will be cached using the absolute URL. Default value is `true`.
+ * copyAttributes: If set to `true` the attributes will be copied from `img` to `svg`. Dfault value
+ * is `true`.
+ * makeIdsUnique: If set to `true` the ID of elements in the `` element that can be references by
+ * property values (for example 'clipPath') are made unique by appending "--inject-X", where X is a
+ * running number which increases with each injection. This is done to avoid duplicate IDs in the DOM.
+ * beforeLoad: Hook before SVG is loaded. The `img` element is passed as a parameter. If the hook returns
+ * a string it is used as the URL instead of the `img` element's `src` attribute.
+ * afterLoad: Hook after SVG is loaded. The loaded `svg` element and `svg` string are passed as a
+ * parameters. If caching is active this hook will only get called once for injected SVGs with the
+ * same absolute path. Changes to the `svg` element in this hook will be applied to all injected SVGs
+ * with the same absolute path. It's also possible to return an `svg` string or `svg` element which
+ * will then be used for the injection.
+ * beforeInject: Hook before SVG is injected. The `img` and `svg` elements are passed as parameters. If
+ * any html element is returned it gets injected instead of applying the default SVG injection.
+ * afterInject: Hook after SVG is injected. The `img` and `svg` elements are passed as parameters.
+ * onAllFinish: Hook after all `img` elements passed to an SVGInject() call have either been injected or
+ * failed to inject.
+ * onFail: Hook after injection fails. The `img` element and a `status` string are passed as an parameter.
+ * The `status` can be either `'SVG_NOT_SUPPORTED'` (the browser does not support SVG),
+ * `'SVG_INVALID'` (the SVG is not in a valid format) or `'LOAD_FAILED'` (loading of the SVG failed).
+ *
+ * @param {HTMLImageElement} img - an img element or an array of img elements
+ * @param {Object} [options] - optional parameter with [options](#options) for this injection.
+ */
+ function SVGInject(img, options) {
+ options = mergeOptions(defaultOptions, options);
+
+ var run = function(resolve) {
+ var allFinish = function() {
+ var onAllFinish = options.onAllFinish;
+ if (onAllFinish) {
+ onAllFinish();
+ }
+ resolve && resolve();
+ };
+
+ if (img && typeof img[_LENGTH_] != _UNDEFINED_) {
+ // an array like structure of img elements
+ var injectIndex = 0;
+ var injectCount = img[_LENGTH_];
+
+ if (injectCount == 0) {
+ allFinish();
+ } else {
+ var finish = function() {
+ if (++injectIndex == injectCount) {
+ allFinish();
+ }
+ };
+
+ for (var i = 0; i < injectCount; i++) {
+ SVGInjectElement(img[i], options, finish);
+ }
+ }
+ } else {
+ // only one img element
+ SVGInjectElement(img, options, allFinish);
+ }
+ };
+
+ // return a Promise object if globally available
+ return typeof Promise == _UNDEFINED_ ? run() : new Promise(run);
+ }
+
+
+ // Injects a single svg element. Options must be already merged with the default options.
+ function SVGInjectElement(imgElem, options, callback) {
+ if (imgElem) {
+ var svgInjectAttributeValue = imgElem[__SVGINJECT];
+ if (!svgInjectAttributeValue) {
+ removeEventListeners(imgElem);
+
+ if (!IS_SVG_SUPPORTED) {
+ svgNotSupported(imgElem, options);
+ callback();
+ return;
+ }
+ // Invoke beforeLoad hook if set. If the beforeLoad returns a value use it as the src for the load
+ // URL path. Else use the imgElem's src attribute value.
+ var beforeLoad = options.beforeLoad;
+ var src = (beforeLoad && beforeLoad(imgElem)) || imgElem[_GET_ATTRIBUTE_]('src');
+
+ if (!src) {
+ // If no image src attribute is set do no injection. This can only be reached by using javascript
+ // because if no src attribute is set the onload and onerror events do not get called
+ if (src === '') {
+ loadFail(imgElem, options);
+ }
+ callback();
+ return;
+ }
+
+ // set array so later calls can register callbacks
+ var onFinishCallbacks = [];
+ imgElem[__SVGINJECT] = onFinishCallbacks;
+
+ var onFinish = function() {
+ callback();
+ onFinishCallbacks.forEach(function(onFinishCallback) {
+ onFinishCallback();
+ });
+ };
+
+ var absUrl = getAbsoluteUrl(src);
+ var useCacheOption = options.useCache;
+ var makeIdsUniqueOption = options.makeIdsUnique;
+
+ var setSvgLoadCacheValue = function(val) {
+ if (useCacheOption) {
+ svgLoadCache[absUrl].forEach(function(svgLoad) {
+ svgLoad(val);
+ });
+ svgLoadCache[absUrl] = val;
+ }
+ };
+
+ if (useCacheOption) {
+ var svgLoad = svgLoadCache[absUrl];
+
+ var handleLoadValue = function(loadValue) {
+ if (loadValue === LOAD_FAIL) {
+ loadFail(imgElem, options);
+ } else if (loadValue === SVG_INVALID) {
+ svgInvalid(imgElem, options);
+ } else {
+ var hasUniqueIds = loadValue[0];
+ var svgString = loadValue[1];
+ var uniqueIdsSvgString = loadValue[2];
+ var svgElem;
+
+ if (makeIdsUniqueOption) {
+ if (hasUniqueIds === NULL) {
+ // IDs for the SVG string have not been made unique before. This may happen if previous
+ // injection of a cached SVG have been run with the option makedIdsUnique set to false
+ svgElem = buildSvgElement(svgString, false);
+ hasUniqueIds = makeIdsUnique(svgElem, false);
+
+ loadValue[0] = hasUniqueIds;
+ loadValue[2] = hasUniqueIds && svgElemToSvgString(svgElem);
+ } else if (hasUniqueIds) {
+ // Make IDs unique for already cached SVGs with better performance
+ svgString = makeIdsUniqueCached(uniqueIdsSvgString);
+ }
+ }
+
+ svgElem = svgElem || buildSvgElement(svgString, false);
+
+ inject(imgElem, svgElem, absUrl, options);
+ }
+ onFinish();
+ };
+
+ if (typeof svgLoad != _UNDEFINED_) {
+ // Value for url exists in cache
+ if (svgLoad.isCallbackQueue) {
+ // Same url has been cached, but value has not been loaded yet, so add to callbacks
+ svgLoad.push(handleLoadValue);
+ } else {
+ handleLoadValue(svgLoad);
+ }
+ return;
+ } else {
+ var svgLoad = [];
+ // set property isCallbackQueue to Array to differentiate from array with cached loaded values
+ svgLoad.isCallbackQueue = true;
+ svgLoadCache[absUrl] = svgLoad;
+ }
+ }
+
+ // Load the SVG because it is not cached or caching is disabled
+ loadSvg(absUrl, function(svgXml, svgString) {
+ // Use the XML from the XHR request if it is an instance of Document. Otherwise
+ // (for example of IE9), create the svg document from the svg string.
+ var svgElem = svgXml instanceof Document ? svgXml.documentElement : buildSvgElement(svgString, true);
+
+ var afterLoad = options.afterLoad;
+ if (afterLoad) {
+ // Invoke afterLoad hook which may modify the SVG element. After load may also return a new
+ // svg element or svg string
+ var svgElemOrSvgString = afterLoad(svgElem, svgString) || svgElem;
+ if (svgElemOrSvgString) {
+ // Update svgElem and svgString because of modifications to the SVG element or SVG string in
+ // the afterLoad hook, so the modified SVG is also used for all later cached injections
+ var isString = typeof svgElemOrSvgString == 'string';
+ svgString = isString ? svgElemOrSvgString : svgElemToSvgString(svgElem);
+ svgElem = isString ? buildSvgElement(svgElemOrSvgString, true) : svgElemOrSvgString;
+ }
+ }
+
+ if (svgElem instanceof SVGElement) {
+ var hasUniqueIds = NULL;
+ if (makeIdsUniqueOption) {
+ hasUniqueIds = makeIdsUnique(svgElem, false);
+ }
+
+ if (useCacheOption) {
+ var uniqueIdsSvgString = hasUniqueIds && svgElemToSvgString(svgElem);
+ // set an array with three entries to the load cache
+ setSvgLoadCacheValue([hasUniqueIds, svgString, uniqueIdsSvgString]);
+ }
+
+ inject(imgElem, svgElem, absUrl, options);
+ } else {
+ svgInvalid(imgElem, options);
+ setSvgLoadCacheValue(SVG_INVALID);
+ }
+ onFinish();
+ }, function() {
+ loadFail(imgElem, options);
+ setSvgLoadCacheValue(LOAD_FAIL);
+ onFinish();
+ });
+ } else {
+ if (Array.isArray(svgInjectAttributeValue)) {
+ // svgInjectAttributeValue is an array. Injection is not complete so register callback
+ svgInjectAttributeValue.push(callback);
+ } else {
+ callback();
+ }
+ }
+ } else {
+ imgNotSet();
+ }
+ }
+
+
+ /**
+ * Sets the default [options](#options) for SVGInject.
+ *
+ * @param {Object} [options] - default [options](#options) for an injection.
+ */
+ SVGInject.setOptions = function(options) {
+ defaultOptions = mergeOptions(defaultOptions, options);
+ };
+
+
+ // Create a new instance of SVGInject
+ SVGInject.create = createSVGInject;
+
+
+ /**
+ * Used in onerror Event of an `` element to handle cases when the loading the original src fails
+ * (for example if file is not found or if the browser does not support SVG). This triggers a call to the
+ * options onFail hook if available. The optional second parameter will be set as the new src attribute
+ * for the img element.
+ *
+ * @param {HTMLImageElement} img - an img element
+ * @param {String} [fallbackSrc] - optional parameter fallback src
+ */
+ SVGInject.err = function(img, fallbackSrc) {
+ if (img) {
+ if (img[__SVGINJECT] != FAIL) {
+ removeEventListeners(img);
+
+ if (!IS_SVG_SUPPORTED) {
+ svgNotSupported(img, defaultOptions);
+ } else {
+ removeOnLoadAttribute(img);
+ loadFail(img, defaultOptions);
+ }
+ if (fallbackSrc) {
+ removeOnLoadAttribute(img);
+ img.src = fallbackSrc;
+ }
+ }
+ } else {
+ imgNotSet();
+ }
+ };
+
+ window[globalName] = SVGInject;
+
+ return SVGInject;
+ }
+
+ var SVGInjectInstance = createSVGInject('SVGInject');
+
+ if (typeof module == 'object' && typeof module.exports == 'object') {
+ module.exports = SVGInjectInstance;
+ }
+})(window, document);
\ No newline at end of file
diff --git a/public/script.js b/public/script.js
index 36582390c..1348e9381 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1446,25 +1446,25 @@ function insertSVGIcon(mes, extra) {
modelName = extra.api;
}
- // Fetch the SVG based on the modelName
- $.get(`/img/${modelName}.svg`, function (data) {
- // Extract the SVG content from the XML data
- let svg = $(data).find('svg');
-
- // Add classes for styling and identification
- svg.addClass('icon-svg timestamp-icon');
+ const image = new Image();
+ // Add classes for styling and identification
+ image.classList.add('icon-svg', 'timestamp-icon');
+ image.src = `/img/${modelName}.svg`;
+ image.onload = async function () {
// Check if an SVG already exists adjacent to the timestamp
let existingSVG = mes.find('.timestamp').next('.timestamp-icon');
if (existingSVG.length) {
// Replace existing SVG
- existingSVG.replaceWith(svg);
+ existingSVG.replaceWith(image);
} else {
// Append the new SVG if none exists
- mes.find('.timestamp').after(svg);
+ mes.find('.timestamp').after(image);
}
- });
+
+ await SVGInject(this);
+ };
}