fix(confirm): 完善confirm组件,谢谢岛风酱 @frg2089

Co-authored-by: 舰队的偶像-岛风酱! <frg2089@outlook.com>
Signed-off-by: 舰队的偶像-岛风酱! <frg2089@outlook.com>
This commit is contained in:
BTMuli
2023-04-04 23:50:34 +08:00
committed by 舰队的偶像-岛风酱!
parent 3edf2c40aa
commit 2bb5659674
3 changed files with 150 additions and 806 deletions

View File

@@ -1,67 +1,72 @@
<template>
<v-overlay v-model="showVal">
<v-overlay v-model="visible">
<div class="confirm-div">
<div class="confirm-box">
<div class="confirm-title">{{ title }}</div>
<div class="confirm-title">
{{ title }}
</div>
<div class="confirm-btn-box">
<button class="confirm-btn" @click="setCancel">
<button class="confirm-btn" @click="onCancel">
<img class="btn-icon" src="../assets/icons/circle-cancel.svg" alt="cancel" />
<span class="btn-text">{{ cancel }}</span>
<span class="btn-text">
{{ cancel }}
</span>
</button>
<button class="confirm-btn" @click="setConfirm">
<button class="confirm-btn" @click="onConfirm">
<img class="btn-icon" src="../assets/icons/circle-check.svg" alt="confirm" />
<span class="btn-text">{{ confirm }}</span>
<span class="btn-text">
{{ confirm }}
</span>
</button>
</div>
</div>
</div>
</v-overlay>
</template>
<script lang="ts" setup>
// vue
import { ref } from "vue";
import { computed } from "vue";
withDefaults(
defineProps<{
title: string;
cancel?: string;
confirm?: string;
value: boolean;
}>(),
{
title: "确认",
cancel: "取消",
confirm: "确定",
value: false,
}
);
interface TConfirmProps {
title: string;
cancel?: string;
confirm?: string;
/** 此值为 true 时显示对话框 */
modelValue: boolean;
}
const showVal = ref(false);
interface TConfirmEmits {
(e: "update:show", v: boolean): void;
(e: "update:modelValue", v: boolean): void;
(e: "confirm"): void;
(e: "cancel"): void;
}
// emits
const emitConfirm = defineEmits(["update:value"]);
// expose
defineExpose({
showConfirm,
const emits = defineEmits<TConfirmEmits>();
const props = withDefaults(defineProps<TConfirmProps>(), {
title: "确认",
cancel: "取消",
confirm: "确定",
});
// methods
function showConfirm() {
showVal.value = true;
}
const visible = computed({
get: () => props.modelValue,
set: v => emits("update:modelValue", v),
});
function setCancel() {
emitConfirm("update:value", false);
showVal.value = false;
}
const onCancel = () => {
visible.value = false;
emits("cancel");
};
function setConfirm() {
emitConfirm("update:value", true);
showVal.value = false;
}
const onConfirm = () => {
visible.value = false;
emits("confirm");
};
</script>
<style lang="css" scoped>
<style scoped>
.confirm-div {
position: absolute;
width: 40vw;

View File

@@ -4,8 +4,16 @@
</div>
<div v-else>
<v-list class="config-list">
<v-list-subheader inset class="config-header">关于</v-list-subheader>
<v-list-subheader inset class="config-header">应用信息</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template v-slot:prepend>
<img class="config-icon" src="/tauri.webp" alt="Tauri" />
</template>
<template v-slot:append>
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<img class="config-icon" src="/icon.webp" alt="App" />
@@ -23,14 +31,6 @@
<v-list-item-subtitle>{{ versionApp }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="Tauri 版本" @click="toOuter('https://next--tauri.netlify.app/')">
<template v-slot:prepend>
<img class="config-icon" src="/tauri.webp" alt="Tauri" />
</template>
<template v-slot:append>
<v-list-item-subtitle>{{ versionTauri }}</v-list-item-subtitle>
</template>
</v-list-item>
<v-list-item title="成就版本">
<template v-slot:prepend>
<img class="config-icon" src="../assets/icons/achievements.svg" alt="Achievements" />
@@ -42,9 +42,9 @@
<v-list-subheader inset class="config-header">设置</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item @click="openMergeData" prepend-icon="mdi-folder" title="打开用户数据目录" />
<v-list-item @click="deleteData" prepend-icon="mdi-delete" title="清除用户缓存" />
<v-list-item @click="deleteTemp" prepend-icon="mdi-delete" title="清除临时数据" />
<v-list-item @click="setDefaultConfig" prepend-icon="mdi-cog" title="恢复默认设置" />
<v-list-item @click="tryConfirm('delUser')" prepend-icon="mdi-delete" title="清除用户缓存" />
<v-list-item @click="tryConfirm('delTemp')" prepend-icon="mdi-delete" title="清除临时数据" />
<v-list-item @click="tryConfirm('delApp')" prepend-icon="mdi-cog" title="恢复默认设置" />
<v-list-subheader inset class="config-header">调试</v-list-subheader>
<v-divider inset class="border-opacity-75" />
<v-list-item title="开发者模式" subtitle="开启后将显示调试信息">
@@ -75,7 +75,8 @@
<template v-slot:append>
<v-btn @click="submitHome" class="card-btn">
<template v-slot:prepend>
<img src="../assets/icons/circle-check.svg" alt="check" />提交
<img src="../assets/icons/circle-check.svg" alt="check" />
提交
</template>
</v-btn>
</template>
@@ -96,7 +97,7 @@
{{ snackbarText }}
</v-snackbar>
<!-- 确认弹窗 -->
<t-confirm :title="confirmText" v-model:value="confirmValue" ref="confirmRef" />
<t-confirm :title="confirmText" v-model="confirmShow" @confirm="doConfirm(confirmOper)" />
</div>
</template>
@@ -121,23 +122,25 @@ const appStore = useAppStore();
const homeStore = useHomeStore();
const achievementsStore = useAchievementsStore();
// About
const loading = ref(true);
const versionApp = ref("");
const versionTauri = ref("");
// About App
const versionApp = ref("" as string);
const versionTauri = ref("" as string);
// loading
const loading = ref(true as boolean);
// data
const showHome = ref(homeStore.getShowValue());
const showHome = ref(homeStore.getShowValue() as string[]);
// snackbar
const snackbar = ref(false);
const snackbarText = ref("");
const snackbarColor = ref("success");
const snackbar = ref(false as boolean);
const snackbarText = ref("" as string);
const snackbarColor = ref("success" as string);
// confirm
const confirmRef = ref();
const confirmText = ref("");
const confirmValue = ref(false);
const confirmText = ref("" as string);
const confirmOper = ref("" as string);
const confirmShow = ref(false as boolean);
// load version
onMounted(async () => {
@@ -160,48 +163,87 @@ async function openMergeData() {
filters: [],
});
}
// 删除本地数据
async function deleteData() {
confirmText.value = "确定要删除用户数据吗?";
confirmRef.value.showConfirm();
await new Promise(resolve => setTimeout(resolve, 1500));
const res = confirmValue.value;
if (res) {
await fs.removeDir("userData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
await fs.removeDir("tempData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
getDataList.map(async item => {
await WriteTGData(item.name, item.data);
});
snackbarText.value = "用户数据已删除!";
snackbar.value = true;
await achievementsStore.init();
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
// open confirm
function tryConfirm(oper: string) {
switch (oper) {
case "delTemp":
confirmText.value = "确认清除临时数据吗?";
confirmOper.value = "delTemp";
confirmShow.value = true;
break;
case "delUser":
confirmText.value = "确认清除用户缓存吗?";
confirmOper.value = "delUser";
confirmShow.value = true;
break;
case "delApp":
confirmText.value = "确认恢复默认设置吗?";
confirmOper.value = "delApp";
confirmShow.value = true;
break;
}
}
// 删除临时数据
async function deleteTemp() {
confirmText.value = "确定要删除临时数据吗?";
confirmRef.value.showConfirm();
await new Promise(resolve => setTimeout(resolve, 1500));
const res = confirmValue.value;
if (res) {
await fs.removeDir("tempData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
snackbarText.value = "临时数据已删除!";
snackbar.value = true;
// transfer confirm oper
function doConfirm(oper: string) {
switch (oper) {
case "delTemp":
delTempData();
break;
case "delUser":
delUserData();
break;
case "delApp":
initAppData();
break;
default:
break;
}
}
// confirmOper
async function delTempData() {
await fs.removeDir("tempData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
snackbarText.value = "临时数据已删除!";
snackbar.value = true;
}
async function delUserData() {
await fs.removeDir("userData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
await fs.removeDir("tempData", {
dir: fs.BaseDirectory.AppLocalData,
recursive: true,
});
getDataList.map(async item => {
await WriteTGData(item.name, item.data);
});
snackbarText.value = "用户数据已删除!";
snackbar.value = true;
await achievementsStore.init();
await fs.createDir("userData", { dir: fs.BaseDirectory.AppLocalData });
await fs.createDir("tempData", { dir: fs.BaseDirectory.AppLocalData });
}
// 恢复默认配置
async function initAppData() {
await appStore.init();
await homeStore.init();
await achievementsStore.init();
snackbarText.value = "已恢复默认配置!";
snackbar.value = true;
setTimeout(() => {
window.location.reload();
}, 1500);
}
// 开启 dev 模式
async function submitDevMode() {
await new Promise(resolve => setTimeout(resolve, 200));
@@ -228,24 +270,6 @@ async function submitHome() {
snackbarColor.value = "success";
snackbar.value = true;
}
// 恢复默认配置
async function setDefaultConfig() {
confirmText.value = "确定要恢复默认配置吗?";
confirmRef.value.showConfirm();
await new Promise(resolve => setTimeout(resolve, 1500));
const res = confirmValue.value;
if (res) {
await appStore.init();
await homeStore.init();
await achievementsStore.init();
snackbarText.value = "已恢复默认配置!";
snackbar.value = true;
setTimeout(() => {
window.location.reload();
}, 1500);
}
}
</script>
<style lang="css" scoped>

View File

@@ -1,685 +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);