mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-20 14:10:39 +01:00
unvendor: Replace SVGInject
This commit is contained in:
parent
2725bae275
commit
8b8f2f1144
@ -55,7 +55,6 @@ module.exports = {
|
|||||||
Popper: 'readonly',
|
Popper: 'readonly',
|
||||||
showdown: 'readonly',
|
showdown: 'readonly',
|
||||||
showdownKatex: 'readonly',
|
showdownKatex: 'readonly',
|
||||||
SVGInject: 'readonly',
|
|
||||||
toastr: 'readonly',
|
toastr: 'readonly',
|
||||||
isProbablyReaderable: 'readonly',
|
isProbablyReaderable: 'readonly',
|
||||||
SillyTavern: 'readonly',
|
SillyTavern: 'readonly',
|
||||||
|
7
package-lock.json
generated
7
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"@adobe/css-tools": "^4.4.0",
|
"@adobe/css-tools": "^4.4.0",
|
||||||
"@agnai/sentencepiece-js": "^1.1.1",
|
"@agnai/sentencepiece-js": "^1.1.1",
|
||||||
"@agnai/web-tokenizers": "^0.1.3",
|
"@agnai/web-tokenizers": "^0.1.3",
|
||||||
|
"@iconfu/svg-inject": "^1.2.3",
|
||||||
"@mozilla/readability": "^0.5.0",
|
"@mozilla/readability": "^0.5.0",
|
||||||
"@zeldafan0225/ai_horde": "^5.1.0",
|
"@zeldafan0225/ai_horde": "^5.1.0",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
@ -287,6 +288,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/@iconfu/svg-inject": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconfu/svg-inject/-/svg-inject-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-3v1MUAJqmJS4jmhHoCkSxt+EdJrjPHlLXrWocCT25kCxnxJto8028Z6CC406EL11KA53SDZgI/QQA5GEJAoiRw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"@adobe/css-tools": "^4.4.0",
|
"@adobe/css-tools": "^4.4.0",
|
||||||
"@agnai/sentencepiece-js": "^1.1.1",
|
"@agnai/sentencepiece-js": "^1.1.1",
|
||||||
"@agnai/web-tokenizers": "^0.1.3",
|
"@agnai/web-tokenizers": "^0.1.3",
|
||||||
|
"@iconfu/svg-inject": "^1.2.3",
|
||||||
"@mozilla/readability": "^0.5.0",
|
"@mozilla/readability": "^0.5.0",
|
||||||
"@zeldafan0225/ai_horde": "^5.1.0",
|
"@zeldafan0225/ai_horde": "^5.1.0",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
|
1
public/global.d.ts
vendored
1
public/global.d.ts
vendored
@ -3,7 +3,6 @@ declare var droll;
|
|||||||
declare var Popper;
|
declare var Popper;
|
||||||
declare var showdown;
|
declare var showdown;
|
||||||
declare var showdownKatex;
|
declare var showdownKatex;
|
||||||
declare var SVGInject;
|
|
||||||
declare var ai;
|
declare var ai;
|
||||||
|
|
||||||
declare var SillyTavern: {
|
declare var SillyTavern: {
|
||||||
|
@ -6759,7 +6759,6 @@
|
|||||||
<script src="lib/droll.js"></script>
|
<script src="lib/droll.js"></script>
|
||||||
<script src="lib/pagination.js"></script>
|
<script src="lib/pagination.js"></script>
|
||||||
<script src="lib/toolcool-color-picker.js"></script>
|
<script src="lib/toolcool-color-picker.js"></script>
|
||||||
<script src="lib/svg-inject.js"></script>
|
|
||||||
<script src="lib/jquery.izoomify.js"></script>
|
<script src="lib/jquery.izoomify.js"></script>
|
||||||
<script type="module" src="lib/structured-clone/monkey-patch.js"></script>
|
<script type="module" src="lib/structured-clone/monkey-patch.js"></script>
|
||||||
<script type="module" src="lib/swiped-events.js"></script>
|
<script type="module" src="lib/swiped-events.js"></script>
|
||||||
|
@ -14,6 +14,7 @@ import ePub from 'epubjs';
|
|||||||
import * as pdfjsLib from 'pdfjs-dist/webpack.mjs';
|
import * as pdfjsLib from 'pdfjs-dist/webpack.mjs';
|
||||||
import DiffMatchPatch from 'diff-match-patch';
|
import DiffMatchPatch from 'diff-match-patch';
|
||||||
import { isProbablyReaderable, Readability } from '@mozilla/readability';
|
import { isProbablyReaderable, Readability } from '@mozilla/readability';
|
||||||
|
import SVGInject from '@iconfu/svg-inject';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose the libraries to the 'window' object.
|
* Expose the libraries to the 'window' object.
|
||||||
@ -48,6 +49,10 @@ export function initLibraryShims() {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.diff_match_patch = DiffMatchPatch;
|
window.diff_match_patch = DiffMatchPatch;
|
||||||
}
|
}
|
||||||
|
if (!('SVGInject' in window)) {
|
||||||
|
// @ts-ignore
|
||||||
|
window.SVGInject = SVGInject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -64,6 +69,7 @@ export default {
|
|||||||
DiffMatchPatch,
|
DiffMatchPatch,
|
||||||
Readability,
|
Readability,
|
||||||
isProbablyReaderable,
|
isProbablyReaderable,
|
||||||
|
SVGInject,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -80,4 +86,5 @@ export {
|
|||||||
DiffMatchPatch,
|
DiffMatchPatch,
|
||||||
Readability,
|
Readability,
|
||||||
isProbablyReaderable,
|
isProbablyReaderable,
|
||||||
|
SVGInject,
|
||||||
};
|
};
|
||||||
|
@ -1,697 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 <defs> 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 <defs> 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 <head> 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 <img> 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 `<defs>` 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 `<img>` 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);
|
|
@ -5,6 +5,7 @@ import {
|
|||||||
localforage,
|
localforage,
|
||||||
Handlebars,
|
Handlebars,
|
||||||
DiffMatchPatch,
|
DiffMatchPatch,
|
||||||
|
SVGInject,
|
||||||
initLibraryShims,
|
initLibraryShims,
|
||||||
default as libs,
|
default as libs,
|
||||||
} from './lib.js';
|
} from './lib.js';
|
||||||
@ -2136,7 +2137,7 @@ function insertSVGIcon(mes, extra) {
|
|||||||
mes.find('.timestamp').after(image);
|
mes.find('.timestamp').after(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
await SVGInject(this);
|
await SVGInject(image);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user