您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
一款优雅的原生JS页面消息提示插件,兼容性良好,无任何依赖。 An elegant native JS page message prompt plug-in, good compatibility, no dependency.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/462234/1598376/Message.js
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Qmsg = factory()); })(this, (function () { 'use strict'; /** * 兼容处理 */ function CompatibleProcessing() { /* 处理Object.assign不存在的问题 */ try { if (typeof Object.assign !== "function") { Object.assign = function (target) { target = Object(target); if (arguments.length > 1) { let sourceList = [...arguments].splice(1, arguments.length - 1); sourceList.forEach((sourceItem) => { for (var sourceKey in sourceItem) { if (Object.prototype.hasOwnProperty.call(sourceItem, sourceKey)) { target[sourceKey] = sourceItem[sourceKey]; } } }); } return target; }; } } catch (error) { console.warn("Qmsg CompatibleProcessing Object.assign error", error); } /* 'classList' 兼容处理,add,remove不支持传入多个cls参数 */ try { if (!("classList" in document.documentElement)) { Object.defineProperty(HTMLElement.prototype, "classList", { get: function () { var self = this; function update(fn) { return function (value) { var classes = self.className.split(/\s+/g), index = classes.indexOf(value); fn(classes, index, value); self.className = classes.join(" "); }; } return { add: update(function (classes, index, value) { if (!~index) classes.push(value); }), remove: update(function (classes, index) { if (~index) classes.splice(index, 1); }), toggle: update(function (classes, index, value) { if (~index) classes.splice(index, 1); else classes.push(value); }), contains: function (value) { return !!~self.className.split(/\s+/g).indexOf(value); }, item: function (index) { return self.className.split(/\s+/g)[index] || null; }, }; }, }); } } catch (error) { console.warn("Qmsg CompatibleProcessing HTMLElement.prototype.classList warning", error); } } const QmsgDefaultConfig = { /** 声明插件名称 */ get PLUGIN_NAME() { return "qmsg"; }, /** 命名空间,用于css和事件 */ get NAMESPACE() { return "qmsg"; }, /** 实例配置的固定的默认值,在初始化时会插入值 */ INS_DEFAULT: {}, /** 实例配置的默认值 */ get config() { return { parent: document.body || document.documentElement, shadowRootMode: "open", animation: true, autoClose: true, listenEventToPauseAutoClose: true, content: "", html: false, isHTML: false, position: "top", showClose: false, maxNums: 5, onClose: null, showIcon: true, showMoreContent: false, showReverse: false, timeout: 2500, type: "info", zIndex: 50000, style: "", customClass: "", isLimitWidth: false, limitWidthNum: 200, limitWidthWrap: "no-wrap", consoleLogContent: false, }; }, }; const QmsgHeaderCloseIcon = /*css*/ ` <svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="48" height="48" fill="white" fill-opacity="0.01"/> <path d="M14 14L34 34" stroke="#909399" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14 34L34 14" stroke="#909399" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> </svg>`; const QmsgIcon = { info: /*css*/ ` <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16"> <path d="M512 64q190.016 4.992 316.512 131.488T960 512q-4.992 190.016-131.488 316.512T512 960q-190.016-4.992-316.512-131.488T64 512q4.992-190.016 131.488-316.512T512 64zm67.008 275.008q26.016 0 43.008-15.488t16.992-41.504-16.992-41.504-42.496-15.488-42.496 15.488-16.992 41.504 16.992 41.504 42.016 15.488zm12 360q0-6.016.992-16T592 664l-52.992 60.992q-8 8.992-16.512 14.016T508 742.016q-8.992-4-8-14.016l88-276.992q4.992-28-8.992-48t-44.992-24q-35.008.992-76.512 29.504t-72.512 72.512v15.008q-.992 10.016 0 19.008l52.992-60.992q8-8.992 16.512-14.016T468 437.024q10.016 4.992 7.008 16l-87.008 276q-7.008 24.992 7.008 44.512T444 800.032q50.016-.992 84-28.992t63.008-72z" fill="#909399"/> </svg>`, warning: /*css*/ ` <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16"> <path d="M512 64C264.64 64 64 264.64 64 512c0 247.424 200.64 448 448 448 247.488 0 448-200.576 448-448 0-247.36-200.512-448-448-448zm0 704c-26.432 0-48-21.504-48-48s21.568-48 48-48c26.624 0 48 21.504 48 48s-21.376 48-48 48zm48-240c0 26.56-21.376 48-48 48-26.432 0-48-21.44-48-48V304c0-26.56 21.568-48 48-48 26.624 0 48 21.44 48 48v224z" fill="#E6A23C"/> </svg>`, error: /*css*/ ` <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16"> <path d="M512 64C264.58 64 64 264.58 64 512s200.58 448 448 448 448-200.57 448-448S759.42 64 512 64zm158.39 561.14a32 32 0 1 1-45.25 45.26L512 557.26 398.86 670.4a32 32 0 0 1-45.25-45.26L466.75 512 353.61 398.86a32 32 0 0 1 45.25-45.25L512 466.74l113.14-113.13a32 32 0 0 1 45.25 45.25L557.25 512z" fill="#F56C6C"/> </svg>`, success: /*css*/ ` <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16"> <path d="M512 64q190.016 4.992 316.512 131.488T960 512q-4.992 190.016-131.488 316.512T512 960q-190.016-4.992-316.512-131.488T64 512q4.992-190.016 131.488-316.512T512 64zm-56 536l-99.008-99.008q-12-11.008-27.488-11.008t-27.008 11.488-11.488 26.496 11.008 27.008l127.008 127.008q11.008 11.008 27.008 11.008t27.008-11.008l263.008-263.008q15.008-15.008 9.504-36.512t-27.008-27.008-36.512 9.504z" fill="#67C23A"/> </svg>`, loading: /*css*/ ` <svg class="animate-turn" width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill="#fff" fill-opacity=".01" d="M0 0h48v48H0z"/> <path d="M4 24c0 11.046 8.954 20 20 20s20-8.954 20-20S35.046 4 24 4" stroke="#409eff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <path d="M36 24c0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12" stroke="#409eff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> </svg>`, }; /** * Qmsg实例存储 */ const QmsgInstStorage = { /** * 存储的Qmsg实例信息 */ insInfoList: [], /** * 根据uuid移除Qmsg实例 * @param uuid 每个Qmsg实例的uuid * @returns * + true 移除成功 * + false 移除失败 */ remove(uuid) { let flag = false; for (let index = 0; index < QmsgInstStorage.insInfoList.length; index++) { if (QmsgInstStorage.insInfoList[index].uuid === uuid) { QmsgInstStorage.insInfoList.splice(index, 1); flag = true; break; } } return flag; }, }; const createCache = (lastNumberWeakMap) => { return (collection, nextNumber) => { lastNumberWeakMap.set(collection, nextNumber); return nextNumber; }; }; /* * The value of the constant Number.MAX_SAFE_INTEGER equals (2 ** 53 - 1) but it * is fairly new. */ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER === undefined ? 9007199254740991 : Number.MAX_SAFE_INTEGER; const TWO_TO_THE_POWER_OF_TWENTY_NINE = 536870912; const TWO_TO_THE_POWER_OF_THIRTY = TWO_TO_THE_POWER_OF_TWENTY_NINE * 2; const createGenerateUniqueNumber = (cache, lastNumberWeakMap) => { return (collection) => { const lastNumber = lastNumberWeakMap.get(collection); /* * Let's try the cheapest algorithm first. It might fail to produce a new * number, but it is so cheap that it is okay to take the risk. Just * increase the last number by one or reset it to 0 if we reached the upper * bound of SMIs (which stands for small integers). When the last number is * unknown it is assumed that the collection contains zero based consecutive * numbers. */ let nextNumber = lastNumber === undefined ? collection.size : lastNumber < TWO_TO_THE_POWER_OF_THIRTY ? lastNumber + 1 : 0; if (!collection.has(nextNumber)) { return cache(collection, nextNumber); } /* * If there are less than half of 2 ** 30 numbers stored in the collection, * the chance to generate a new random number in the range from 0 to 2 ** 30 * is at least 50%. It's benifitial to use only SMIs because they perform * much better in any environment based on V8. */ if (collection.size < TWO_TO_THE_POWER_OF_TWENTY_NINE) { while (collection.has(nextNumber)) { nextNumber = Math.floor(Math.random() * TWO_TO_THE_POWER_OF_THIRTY); } return cache(collection, nextNumber); } // Quickly check if there is a theoretical chance to generate a new number. if (collection.size > MAX_SAFE_INTEGER) { throw new Error('Congratulations, you created a collection of unique numbers which uses all available integers!'); } // Otherwise use the full scale of safely usable integers. while (collection.has(nextNumber)) { nextNumber = Math.floor(Math.random() * MAX_SAFE_INTEGER); } return cache(collection, nextNumber); }; }; const LAST_NUMBER_WEAK_MAP = new WeakMap(); const cache = createCache(LAST_NUMBER_WEAK_MAP); const generateUniqueNumber = createGenerateUniqueNumber(cache, LAST_NUMBER_WEAK_MAP); const isMessagePort = (sender) => { return typeof sender.start === 'function'; }; const PORT_MAP = new WeakMap(); const extendBrokerImplementation = (partialBrokerImplementation) => ({ ...partialBrokerImplementation, connect: ({ call }) => { return async () => { const { port1, port2 } = new MessageChannel(); const portId = await call('connect', { port: port1 }, [port1]); PORT_MAP.set(port2, portId); return port2; }; }, disconnect: ({ call }) => { return async (port) => { const portId = PORT_MAP.get(port); if (portId === undefined) { throw new Error('The given port is not connected.'); } await call('disconnect', { portId }); }; }, isSupported: ({ call }) => { return () => call('isSupported'); } }); const ONGOING_REQUESTS = new WeakMap(); const createOrGetOngoingRequests = (sender) => { if (ONGOING_REQUESTS.has(sender)) { // @todo TypeScript needs to be convinced that has() works as expected. return ONGOING_REQUESTS.get(sender); } const ongoingRequests = new Map(); ONGOING_REQUESTS.set(sender, ongoingRequests); return ongoingRequests; }; const createBroker = (brokerImplementation) => { const fullBrokerImplementation = extendBrokerImplementation(brokerImplementation); return (sender) => { const ongoingRequests = createOrGetOngoingRequests(sender); sender.addEventListener('message', (({ data: message }) => { const { id } = message; if (id !== null && ongoingRequests.has(id)) { const { reject, resolve } = ongoingRequests.get(id); ongoingRequests.delete(id); if (message.error === undefined) { resolve(message.result); } else { reject(new Error(message.error.message)); } } })); if (isMessagePort(sender)) { sender.start(); } const call = (method, params = null, transferables = []) => { return new Promise((resolve, reject) => { const id = generateUniqueNumber(ongoingRequests); ongoingRequests.set(id, { reject, resolve }); if (params === null) { sender.postMessage({ id, method }, transferables); } else { sender.postMessage({ id, method, params }, transferables); } }); }; const notify = (method, params, transferables = []) => { sender.postMessage({ id: null, method, params }, transferables); }; let functions = {}; for (const [key, handler] of Object.entries(fullBrokerImplementation)) { functions = { ...functions, [key]: handler({ call, notify }) }; } return { ...functions }; }; }; // Prefilling the Maps with a function indexed by zero is necessary to be compliant with the specification. const scheduledIntervalsState = new Map([[0, null]]); // tslint:disable-line no-empty const scheduledTimeoutsState = new Map([[0, null]]); // tslint:disable-line no-empty const wrap = createBroker({ clearInterval: ({ call }) => { return (timerId) => { if (typeof scheduledIntervalsState.get(timerId) === 'symbol') { scheduledIntervalsState.set(timerId, null); call('clear', { timerId, timerType: 'interval' }).then(() => { scheduledIntervalsState.delete(timerId); }); } }; }, clearTimeout: ({ call }) => { return (timerId) => { if (typeof scheduledTimeoutsState.get(timerId) === 'symbol') { scheduledTimeoutsState.set(timerId, null); call('clear', { timerId, timerType: 'timeout' }).then(() => { scheduledTimeoutsState.delete(timerId); }); } }; }, setInterval: ({ call }) => { return (func, delay = 0, ...args) => { const symbol = Symbol(); const timerId = generateUniqueNumber(scheduledIntervalsState); scheduledIntervalsState.set(timerId, symbol); const schedule = () => call('set', { delay, now: performance.timeOrigin + performance.now(), timerId, timerType: 'interval' }).then(() => { const state = scheduledIntervalsState.get(timerId); if (state === undefined) { throw new Error('The timer is in an undefined state.'); } if (state === symbol) { func(...args); // Doublecheck if the interval should still be rescheduled because it could have been cleared inside of func(). if (scheduledIntervalsState.get(timerId) === symbol) { schedule(); } } }); schedule(); return timerId; }; }, setTimeout: ({ call }) => { return (func, delay = 0, ...args) => { const symbol = Symbol(); const timerId = generateUniqueNumber(scheduledTimeoutsState); scheduledTimeoutsState.set(timerId, symbol); call('set', { delay, now: performance.timeOrigin + performance.now(), timerId, timerType: 'timeout' }).then(() => { const state = scheduledTimeoutsState.get(timerId); if (state === undefined) { throw new Error('The timer is in an undefined state.'); } if (state === symbol) { // A timeout can be savely deleted because it is only called once. scheduledTimeoutsState.delete(timerId); func(...args); } }); return timerId; }; } }); const load = (url) => { const worker = new Worker(url); return wrap(worker); }; const createLoadOrReturnBroker = (loadBroker, worker) => { let broker = null; return () => { if (broker !== null) { return broker; } const blob = new Blob([worker], { type: 'application/javascript; charset=utf-8' }); const url = URL.createObjectURL(blob); broker = loadBroker(url); // Bug #1: Edge up until v18 didn't like the URL to be revoked directly. setTimeout(() => URL.revokeObjectURL(url)); return broker; }; }; // This is the minified and stringified code of the worker-timers-worker package. const worker = `(()=>{var e={455:function(e,t){!function(e){"use strict";var t=function(e){return function(t){var r=e(t);return t.add(r),r}},r=function(e){return function(t,r){return e.set(t,r),r}},n=void 0===Number.MAX_SAFE_INTEGER?9007199254740991:Number.MAX_SAFE_INTEGER,o=536870912,s=2*o,a=function(e,t){return function(r){var a=t.get(r),i=void 0===a?r.size:a<s?a+1:0;if(!r.has(i))return e(r,i);if(r.size<o){for(;r.has(i);)i=Math.floor(Math.random()*s);return e(r,i)}if(r.size>n)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;r.has(i);)i=Math.floor(Math.random()*n);return e(r,i)}},i=new WeakMap,u=r(i),c=a(u,i),d=t(c);e.addUniqueNumber=d,e.generateUniqueNumber=c}(t)}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var s=t[n]={exports:{}};return e[n].call(s.exports,s,s.exports,r),s.exports}(()=>{"use strict";const e=-32603,t=-32602,n=-32601,o=(e,t)=>Object.assign(new Error(e),{status:t}),s=t=>o('The handler of the method called "'.concat(t,'" returned an unexpected result.'),e),a=(t,r)=>async({data:{id:a,method:i,params:u}})=>{const c=r[i];try{if(void 0===c)throw(e=>o('The requested method called "'.concat(e,'" is not supported.'),n))(i);const r=void 0===u?c():c(u);if(void 0===r)throw(t=>o('The handler of the method called "'.concat(t,'" returned no required result.'),e))(i);const d=r instanceof Promise?await r:r;if(null===a){if(void 0!==d.result)throw s(i)}else{if(void 0===d.result)throw s(i);const{result:e,transferables:r=[]}=d;t.postMessage({id:a,result:e},r)}}catch(e){const{message:r,status:n=-32603}=e;t.postMessage({error:{code:n,message:r},id:a})}};var i=r(455);const u=new Map,c=(e,r,n)=>({...r,connect:({port:t})=>{t.start();const n=e(t,r),o=(0,i.generateUniqueNumber)(u);return u.set(o,(()=>{n(),t.close(),u.delete(o)})),{result:o}},disconnect:({portId:e})=>{const r=u.get(e);if(void 0===r)throw(e=>o('The specified parameter called "portId" with the given value "'.concat(e,'" does not identify a port connected to this worker.'),t))(e);return r(),{result:null}},isSupported:async()=>{if(await new Promise((e=>{const t=new ArrayBuffer(0),{port1:r,port2:n}=new MessageChannel;r.onmessage=({data:t})=>e(null!==t),n.postMessage(t,[t])}))){const e=n();return{result:e instanceof Promise?await e:e}}return{result:!1}}}),d=(e,t,r=()=>!0)=>{const n=c(d,t,r),o=a(e,n);return e.addEventListener("message",o),()=>e.removeEventListener("message",o)},l=e=>t=>{const r=e.get(t);if(void 0===r)return Promise.resolve(!1);const[n,o]=r;return clearTimeout(n),e.delete(t),o(!1),Promise.resolve(!0)},f=(e,t,r)=>(n,o,s)=>{const{expected:a,remainingDelay:i}=e(n,o);return new Promise((e=>{t.set(s,[setTimeout(r,i,a,t,e,s),e])}))},m=(e,t)=>{const r=performance.now(),n=e+t-r-performance.timeOrigin;return{expected:r+n,remainingDelay:n}},p=(e,t,r,n)=>{const o=e-performance.now();o>0?t.set(n,[setTimeout(p,o,e,t,r,n),r]):(t.delete(n),r(!0))},h=new Map,v=l(h),w=new Map,g=l(w),M=f(m,h,p),y=f(m,w,p);d(self,{clear:async({timerId:e,timerType:t})=>({result:await("interval"===t?v(e):g(e))}),set:async({delay:e,now:t,timerId:r,timerType:n})=>({result:await("interval"===n?M:y)(e,t,r)})})})()})();`; // tslint:disable-line:max-line-length const loadOrReturnBroker = createLoadOrReturnBroker(load, worker); const clearInterval = (timerId) => loadOrReturnBroker().clearInterval(timerId); const clearTimeout = (timerId) => loadOrReturnBroker().clearTimeout(timerId); const setInterval = (...args) => loadOrReturnBroker().setInterval(...args); const setTimeout$1 = (...args) => loadOrReturnBroker().setTimeout(...args); const QmsgUtils = { /** * 生成带插件名的名称 * @param args */ getNameSpacify(...args) { let result = QmsgDefaultConfig.NAMESPACE; for (let index = 0; index < args.length; ++index) { result += "-" + args[index]; } return result; }, /** * 判断字符是否是数字 * @param text 需要判断的字符串 */ isNumber(text) { let isNumberPattern = /^\d+$/; return isNumberPattern.test(text); }, /** * 获取唯一性的UUID */ getUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (value) { let randValue = (Math.random() * 16) | 0, newValue = value == "x" ? randValue : (randValue & 0x3) | 0x8; return newValue.toString(16); }); }, /** * 合并参数为配置信息,用于创建Msg实例 * @param content 文本内容 * @param config 配置 */ mergeArgs(content = "", config) { let opts = {}; if (arguments.length === 0) { return opts; } if (config != null) { // 传入了2个参数 // string object // object object opts.content = content; if (typeof config === "object" && config != null) { return Object.assign(opts, config); } } else { // 传入了1个参数 // object // string if (typeof content === "object" && content != null) { return Object.assign(opts, content); } else { opts.content = content; } } return opts; }, /** * 转换为动态对象 * @param obj 需要配置的对象 * @param other_obj 获取的其它对象 */ toDynamicObject(obj, ...other_objs) { let __obj__ = Object.assign({}, obj ?? {}); Object.keys(__obj__).forEach((keyName) => { // @ts-ignore let objValue = __obj__[keyName]; Object.defineProperty(__obj__, keyName, { get() { let findIndex = other_objs.findIndex((other_obj) => { // 判断其他对象中是否有该属性 return (typeof other_obj === "object" && other_obj != null && other_obj.hasOwnProperty.call(other_obj, keyName)); }); if (findIndex !== -1) { // @ts-ignore let other_objValue = other_objs[findIndex][keyName]; return other_objValue; } else { return objValue; } }, set(newValue) { objValue = newValue; }, }); }); return __obj__; }, /** * 自动使用 Worker 执行 setTimeout */ setTimeout(callback, timeout) { try { return setTimeout$1(callback, timeout); } catch (error) { return globalThis.setTimeout(callback, timeout); } }, /** * 配合 QmsgUtils.setTimeout 使用 */ clearTimeout(timeId) { try { if (timeId != null) { clearTimeout(timeId); } } catch (error) { } finally { globalThis.clearTimeout(timeId); } }, /** * 自动使用 Worker 执行 setInterval */ setInterval(callback, timeout) { try { return setInterval(callback, timeout); } catch (error) { return globalThis.setInterval(callback, timeout); } }, /** * 配合 QmsgUtils.setInterval 使用 */ clearInterval(timeId) { try { if (timeId != null) { clearInterval(timeId); } } catch (error) { } finally { globalThis.clearInterval(timeId); } }, /** * 设置安全的html */ setSafeHTML($el, text) { // 创建 TrustedHTML 策略(需 CSP 允许) try { $el.innerHTML = text; } catch (error) { // @ts-ignore if (globalThis.trustedTypes) { // @ts-ignore const policy = globalThis.trustedTypes.createPolicy("safe-innerHTML", { createHTML: (html) => html, }); $el.innerHTML = policy.createHTML(text); } else { throw new Error("QmsgUtils trustedTypes is not defined"); } } }, }; const QmsgAnimation = { /** 状态 & 动画 */ $state: { opening: "MessageMoveIn", done: "", closing: "MessageMoveOut", }, $name: { startNameList: [ "animationName", "WebkitAnimationName", "MozAnimationName", "msAnimationName", "OAnimationName", ], endNameList: [ "animationend", "webkitAnimationEnd", "mozAnimationEnd", "MSAnimationEnd", "oanimationend", ], }, /** * 是否支持动画属性 * @private */ __CAN_ANIMATION__: void 0, /** * 是否支持动画属性 */ get CAN_ANIMATION() { this.__CAN_ANIMATION__ = this.__CAN_ANIMATION__ ?? this.getStyleAnimationNameValue(document.createElement("div")) != null; return this.__CAN_ANIMATION__; }, /** * 获取元素上的animationName属性 * @param element */ getStyleAnimationNameValue(element) { for (let index = 0; index < this.$name.startNameList.length; index++) { let animationName = this.$name.startNameList[index]; let animationNameValue = element.style[animationName]; if (animationNameValue != null) { return animationNameValue; } } }, /** * 设置元素上的animationName属性 * @param element * @param animationNameValue */ setStyleAnimationName(element, animationNameValue = "") { this.$name.startNameList.forEach((animationName) => { if (animationName in element.style) { element.style[animationName] = animationNameValue; } }); }, }; const QmsgCSS = { css: /*css*/ `@charset "utf-8"; .qmsg.qmsg-wrapper{position:fixed;top:16px;left:0;z-index:50000;display:flex;box-sizing:border-box;margin:0;padding:0;width:100%;color:rgba(0,0,0,.55);list-style:none;font-variant:tabular-nums;font-size:13px;line-height:1;font-feature-settings:"tnum";pointer-events:none;flex-direction:column;} .qmsg.qmsg-data-position-center,.qmsg.qmsg-data-position-left,.qmsg.qmsg-data-position-right{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);} .qmsg.qmsg-data-position-bottom,.qmsg.qmsg-data-position-bottomleft,.qmsg.qmsg-data-position-bottomright{position:fixed;top:unset;bottom:0;bottom:8px;left:50%;transform:translate(-50%,0);} .qmsg.qmsg-data-position-bottomleft .qmsg-item,.qmsg.qmsg-data-position-left .qmsg-item,.qmsg.qmsg-data-position-topleft .qmsg-item{text-align:left;} .qmsg.qmsg-data-position-bottom .qmsg-item,.qmsg.qmsg-data-position-center .qmsg-item,.qmsg.qmsg-data-position-top .qmsg-item{text-align:center;} .qmsg.qmsg-data-position-bottomright .qmsg-item,.qmsg.qmsg-data-position-right .qmsg-item,.qmsg.qmsg-data-position-topright .qmsg-item{text-align:right;} .qmsg .qmsg-item{position:relative;padding:8px;text-align:center;-webkit-animation-duration:.3s;animation-duration:.3s;} .qmsg .qmsg-item .qmsg-count{position:absolute;top:-4px;left:-4px;display:inline-block;height:16px;min-width:16px;border-radius:2px;background-color:red;color:#fff;text-align:center;font-size:12px;line-height:16px;-webkit-animation-duration:.3s;animation-duration:.3s;} .qmsg .qmsg-item:first-child{margin-top:-8px;} .qmsg .qmsg-content{position:relative;display:inline-block;padding:10px 12px;max-width:80%;min-width:40px;border-radius:4px;background:#fff;box-shadow:0 4px 12px rgba(0,0,0,.15);text-align:center;pointer-events:all;} .qmsg .qmsg-content [class^=qmsg-content-]{display:flex;align-items:center;} .qmsg .qmsg-icon{position:relative;top:1px;display:inline-block;margin-right:8px;color:inherit;vertical-align:-.125em;text-align:center;text-transform:none;font-style:normal;font-size:16px;line-height:0;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;} .qmsg .qmsg-icon svg{display:inline-block;} .qmsg .qmsg-content .qmsg-show-more-content{display:flex;align-items:center;white-space:unset;overflow:unset;text-overflow:unset;padding-right:unset} .qmsg .qmsg-content-info .qmsg-icon{color:#1890ff;} .qmsg .qmsg-icon-close{margin:0;margin-left:8px;padding:0;outline:0;border:none;background-color:transparent;color:rgba(0,0,0,.45);font-size:12px;cursor:pointer;transition:color .3s;} .qmsg .qmsg-icon-close:hover>svg path{stroke:#555;} .qmsg .qmsg-icon-close.qmsg-show-more-content{position:unset;overflow:unset;padding-left:6px;margin-right:0} .qmsg .animate-turn{animation:MessageTurn 1s linear infinite;-webkit-animation:MessageTurn 1s linear infinite;} @keyframes MessageTurn{ 0%{-webkit-transform:rotate(0);} 25%{-webkit-transform:rotate(90deg);} 50%{-webkit-transform:rotate(180deg);} 75%{-webkit-transform:rotate(270deg);} 100%{-webkit-transform:rotate(360deg);} } @-webkit-keyframes MessageTurn{ 0%{-webkit-transform:rotate(0);} 25%{-webkit-transform:rotate(90deg);} 50%{-webkit-transform:rotate(180deg);} 75%{-webkit-transform:rotate(270deg);} 100%{-webkit-transform:rotate(360deg);} } @-webkit-keyframes MessageMoveOut{ 0%{max-height:150px;opacity:1;} to{max-height:0;opacity:0;} } @keyframes MessageMoveOut{ 0%{max-height:150px;opacity:1;} to{max-height:0;opacity:0;} } @-webkit-keyframes MessageMoveIn{ 0%{opacity:0;transform:translateY(-100%);transform-origin:0 0;} to{opacity:1;transform:translateY(0);transform-origin:0 0;} } @keyframes MessageMoveIn{ 0%{opacity:0;transform:translateY(-100%);transform-origin:0 0;} to{opacity:1;transform:translateY(0);transform-origin:0 0;} } @-webkit-keyframes MessageShake{ 0%,100%{opacity:1;transform:translateX(0);} 25%,75%{opacity:.75;transform:translateX(-4px);} 50%{opacity:.25;transform:translateX(4px);} } @keyframes MessageShake{ 0%,100%{opacity:1;transform:translateX(0);} 25%,75%{opacity:.75;transform:translateX(-4px);} 50%{opacity:.25;transform:translateX(4px);} }`, /** * 获取CSS元素 */ getStyleElement() { let $style = document.createElement("style"); $style.setAttribute("type", "text/css"); $style.setAttribute("data-type", QmsgDefaultConfig.PLUGIN_NAME); QmsgUtils.setSafeHTML($style, this.css); return $style; }, }; /** * 每条消息的构造函数 */ class QmsgMsg { /** * setTimeout的id */ timeId = void 0; /** * 启动时间 */ startTime; /** * 关闭时间 */ endTime; /** * Qmsg的配置 */ setting; /** * uuid */ uuid; /** * 当前动画状态 */ state; /** * 当前相同消息的数量 */ repeatNum; /** * 主元素 */ $Qmsg; constructor(config, uuid) { this.timeId = void 0; this.startTime = Date.now(); this.endTime = null; // this.#setting = Object.assign({}, QmsgStore.DEFAULT, this.option); this.setting = QmsgUtils.toDynamicObject(QmsgDefaultConfig.config, config, QmsgDefaultConfig.INS_DEFAULT); this.uuid = uuid; this.state = "opening"; this.$Qmsg = document.createElement("div"); this.repeatNum = 1; this.detectionType(); this.init(); if (this.setting.consoleLogContent) { // 控制台输出content console.log(this.setting.content); } } /** * 获取当前配置 * @returns */ getSetting() { return this.setting; } /** * 获取当前相同的数量 * @returns */ getRepeatNum() { return this.repeatNum; } /** * 设置repeatNum值 * @param num 重复的数量 */ setRepeatNum(num) { this.repeatNum = num; } /** * 设置repeatNum自增 */ setRepeatNumIncreasing() { this.repeatNum++; } /** * 初始化元素 */ init() { let QmsgContext = this; if (this.setting.customClass && typeof this.setting.customClass === "string") { /* 设置自定义类名 */ this.$Qmsg.classList.add(this.setting.customClass); } // 设置svg图标 let $svg = QmsgIcon[this.setting.type || "info"]; let contentClassName = QmsgUtils.getNameSpacify("content-" + this.setting.type || "info"); if (this.setting.showClose) { // 显示 关闭图标 contentClassName += " " + QmsgUtils.getNameSpacify("content-with-close"); } // 内容兼容处理 let content = this.setting.content || ""; // 关闭图标 自定义额外className let extraCloseIconClassName = ""; // 关闭图标svg let $closeSvg = QmsgHeaderCloseIcon; if (this.setting.showMoreContent) { // 显示更多内容 contentClassName += "qmsg-show-more-content"; extraCloseIconClassName += "qmsg-show-more-content"; } let $closeIcon = ""; if (this.setting.showClose) { /* 显示右上角的关闭图标按钮 */ $closeIcon = /*html*/ `<i class="qmsg-icon qmsg-icon-close ${extraCloseIconClassName}">${$closeSvg}</i>`; } /* 内容 */ let $content = document.createElement("span"); let $positionClassName = QmsgUtils.getNameSpacify("data-position", this.setting.position.toLowerCase()); if (this.setting.html || this.setting.isHTML) { /* 内容是html */ QmsgUtils.setSafeHTML($content, content); } else { /* 内容是纯文本 */ $content.innerText = content; } if (this.setting.isLimitWidth) { /* 限制宽度 */ let limitWidthNum = this.setting.limitWidthNum; if (typeof limitWidthNum === "string") { if (QmsgUtils.isNumber(limitWidthNum)) { limitWidthNum = limitWidthNum + "px"; } } else { limitWidthNum = limitWidthNum.toString() + "px"; } $content.style.maxWidth = limitWidthNum; $content.style.width = limitWidthNum; /* 设置换行 */ if (this.setting.limitWidthWrap === "no-wrap") { /* 禁止换行 */ $content.style.whiteSpace = "nowrap"; } else if (this.setting.limitWidthWrap === "ellipsis") { /* 禁止换行且显示省略号 */ $content.style.whiteSpace = "nowrap"; $content.style.overflow = "hidden"; $content.style.textOverflow = "ellipsis"; } else if (this.setting.limitWidthWrap === "wrap") { /* 允许换行 */ /* 默认的 */ $content.style.whiteSpace = ""; } } QmsgUtils.setSafeHTML(this.$Qmsg, /*html*/ ` <div class="qmsg-content"> <div class="${contentClassName}"> ${this.setting.showIcon ? `<i class="qmsg-icon">${$svg}</i>` : ""} ${$content.outerHTML} ${$closeIcon} </div> </div> `); /** 内容容器 */ let $contentContainer = this.$Qmsg.querySelector(".qmsg-content"); this.$Qmsg.classList.add(QmsgUtils.getNameSpacify("item")); this.$Qmsg.setAttribute(QmsgUtils.getNameSpacify("uuid"), this.uuid); /** 总根元素 */ let $shadowContainer; /** 根元素 */ let $shadowRoot; /** 容器包裹的元素 */ let $wrapper; $shadowContainer = document.querySelector(".qmsg-shadow-container"); $shadowRoot = this.setting.useShadowRoot ? $shadowContainer?.shadowRoot : $shadowContainer; if (!$shadowContainer) { // 页面中不存在ShadowRoot容器元素 // 添加新增的ShadowRoot容器元素 $shadowContainer = document.createElement("div"); $shadowContainer.className = "qmsg-shadow-container"; if (this.setting.useShadowRoot) { $shadowRoot = $shadowContainer.attachShadow({ mode: this.setting.shadowRootMode, }); } else { $shadowRoot = $shadowContainer; } $shadowRoot.appendChild(QmsgCSS.getStyleElement()); if (this.setting.style != null) { // 插入自定义的style // 这里需要插入到每一条的Qmsg内,以便移除实例时把style也移除 let __$ownStyle__ = document.createElement("style"); __$ownStyle__.setAttribute("type", "text/css"); __$ownStyle__.setAttribute("data-id", this.uuid); QmsgUtils.setSafeHTML(__$ownStyle__, this.setting.style); $contentContainer.insertAdjacentElement("afterend", __$ownStyle__); } this.setting.parent.appendChild($shadowContainer); } if ($shadowRoot == null) { throw new Error("QmsgInst " + QmsgDefaultConfig.PLUGIN_NAME + " $shadowRoot is null"); } $wrapper = $shadowRoot.querySelector(`.${QmsgDefaultConfig.NAMESPACE}.${$positionClassName}`); if (!$wrapper) { $wrapper = document.createElement("div"); $wrapper.classList.add(QmsgDefaultConfig.NAMESPACE, QmsgUtils.getNameSpacify("wrapper"), QmsgUtils.getNameSpacify("is-initialized"), $positionClassName); $shadowRoot.appendChild($wrapper); } if (this.setting.showReverse) { $wrapper.style.flexDirection = "column-reverse"; } else { $wrapper.style.flexDirection = "column"; } let zIndex = this.setting.zIndex; if (typeof zIndex === "function") { zIndex = zIndex(); } if (!isNaN(zIndex)) { $wrapper.style.zIndex = zIndex.toString(); } $wrapper.appendChild(this.$Qmsg); this.setState(this.$Qmsg, "opening"); if (this.setting.showClose) { /* 关闭按钮绑定点击事件 */ let $closeIcon = this.$Qmsg.querySelector(".qmsg-icon-close"); if ($closeIcon) { $closeIcon.addEventListener("click", (evt) => { QmsgContext.close(); }); } } /* 监听动画完成 */ let animationendEvent = (event) => { let animationNameValue = QmsgAnimation.getStyleAnimationNameValue(QmsgContext.$Qmsg); if (animationNameValue === QmsgAnimation.$state.closing) { // 当前触发的是关闭 QmsgContext.endTime = Date.now(); QmsgContext.destroy(); } QmsgAnimation.setStyleAnimationName(QmsgContext.$Qmsg); }; QmsgAnimation.$name.endNameList.forEach(function (animationendName) { QmsgContext.$Qmsg.addEventListener(animationendName, animationendEvent); }); /* 自动关闭 */ if (this.setting.autoClose && this.setting.listenEventToPauseAutoClose) { // 鼠标|触摸滑入时,清除自动关闭的定时器 // 鼠标|触摸滑出时,重新设置定时器 this.resetAutoCloseTimer(); /** * 鼠标滑入 * * + 清除定时器 * + 清除开始时间 * + 清除结束时间 */ let enterEvent = (event) => { this.clearAutoCloseTimer(); }; /** 鼠标滑出,重启定时器,创建新的开始时间和timeId */ let leaveEvent = (event) => { if (this.timeId != null) { // 似乎enterEvent函数未正确调用? console.warn("QmsgInst timeId is not null,mouseenter may be not first trigger,timeId:" + this.timeId); return; } this.startAutoCloseTimer(); }; let isRemoveMouseEvent = false; this.$Qmsg.addEventListener("mouseenter", enterEvent); this.$Qmsg.addEventListener("mouseleave", leaveEvent); this.$Qmsg.addEventListener("touchstart", (evt) => { // 由于移动端不支持mouseout且会触发mouseenter // 那么需要移除该监听 if (!isRemoveMouseEvent) { isRemoveMouseEvent = true; this.$Qmsg.removeEventListener("mouseenter", enterEvent); this.$Qmsg.removeEventListener("mouseleave", leaveEvent); } enterEvent(); }); this.$Qmsg.addEventListener("touchend", leaveEvent); this.$Qmsg.addEventListener("touchcancel", leaveEvent); } } /** * 对timeout进行检测并转换 * 当timeout为string时,转换为number * timeout必须在规定范围内 */ detectionType() { if (this.setting.timeout != null && typeof this.setting.timeout === "string") { this.setting.timeout = parseInt(this.setting.timeout); } if (isNaN(this.setting.timeout)) { this.setting.timeout = QmsgDefaultConfig.config.timeout; } if (!(this.setting.timeout != null && parseInt(this.setting.timeout.toString()) >= 0 && parseInt(this.setting.timeout.toString()) <= Number.MAX_VALUE)) { this.setting.timeout = QmsgDefaultConfig.config.timeout; } if (typeof this.setting.zIndex === "function") { this.setting.zIndex = this.setting.zIndex(); } if (this.setting.zIndex != null && typeof this.setting.zIndex === "string") { this.setting.zIndex = parseInt(this.setting.zIndex); } if (isNaN(this.setting.zIndex)) { this.setting.zIndex = typeof QmsgDefaultConfig.config.zIndex === "function" ? QmsgDefaultConfig.config.zIndex() : QmsgDefaultConfig.config.zIndex; } } /** * 设置元素动画状态 开启/关闭 * @param QmsgMsg * @param state */ setState(element, state) { if (!state || !QmsgAnimation.$state[state]) return; this.state = state; QmsgAnimation.setStyleAnimationName(element, QmsgAnimation.$state[state]); } /** * 设置消息数量统计 */ setMsgCount() { let countClassName = QmsgUtils.getNameSpacify("count"); let wrapperClassName = `div.${QmsgUtils.getNameSpacify("data-position", this.setting.position.toLowerCase())} [class^="qmsg-content-"]`; let $content = this.$Qmsg.querySelector(wrapperClassName); if (!$content) { throw new Error("QmsgInst $content is null"); } let $count = $content.querySelector("." + countClassName); if (!$count) { $count = document.createElement("span"); $count.classList.add(countClassName); $content.appendChild($count); } // 获取重复显示内容的实例数量 let repeatNum = this.getRepeatNum(); QmsgUtils.setSafeHTML($count, repeatNum.toString()); QmsgAnimation.setStyleAnimationName($count); QmsgAnimation.setStyleAnimationName($count, "MessageShake"); this.resetAutoCloseTimer(); } /** * 清除旧的自动关闭定时器 */ clearAutoCloseTimer() { /* 重置定时器 */ QmsgUtils.clearTimeout(this.timeId); this.timeId = void 0; this.startTime = null; this.endTime = null; } /** * 开始自动关闭定时器 */ startAutoCloseTimer() { if (this.setting.autoClose && this.setting.listenEventToPauseAutoClose) { this.startTime = Date.now(); this.endTime = null; this.timeId = QmsgUtils.setTimeout(() => { this.close(); }, this.setting.timeout); } } /** * 重置自动关闭定时器(会自动清理旧的定时器) */ resetAutoCloseTimer() { this.clearAutoCloseTimer(); this.startAutoCloseTimer(); } /** * 关闭Qmsg(会触发动画) */ close() { this.setState(this.$Qmsg, "closing"); if (QmsgAnimation.CAN_ANIMATION) { /* 支持动画 */ QmsgInstStorage.remove(this.uuid); } else { /* 不支持动画 */ this.destroy(); } let onCloseCallBack = this.setting.onClose; if (onCloseCallBack && typeof onCloseCallBack === "function") { onCloseCallBack.call(this); } } /** * 销毁Qmsg */ destroy() { this.endTime = Date.now(); this.$Qmsg.remove(); QmsgUtils.clearTimeout(this.timeId); QmsgInstStorage.remove(this.uuid); this.timeId = void 0; } /** * 获取内容元素 */ get $content() { let $content = this.$Qmsg.querySelector("div[class^=qmsg-content-] > span"); if (!$content) { throw new Error("QmsgInst $content is null"); } return $content; } /** * 设置内容文本 */ setText(text) { let $content = this.$content; $content.innerText = text; this.setting.content = text; } /** * 设置内容超文本 */ setHTML(text) { let $content = this.$content; QmsgUtils.setSafeHTML($content, text); this.setting.content = text; } } /** * 通过配置信息 来判断是否为同一条消息,并返回消息实例 * @param config 配置项 */ function QmsgInstHandler(config = {}) { let optionString = JSON.stringify(config); /* 寻找已生成的实例是否存在配置相同的 */ let findQmsgItemInfo = QmsgInstStorage.insInfoList.find((item) => { return item.config === optionString; }); let QmsgInstance = findQmsgItemInfo?.instance; if (QmsgInstance == null) { /* 不存在,创建个新的 */ let uuid = QmsgUtils.getUUID(); let qmsgInstStorageInfo = { uuid: uuid, config: optionString, instance: new QmsgMsg(config, uuid), }; QmsgInstStorage.insInfoList.push(qmsgInstStorageInfo); let QmsgListLength = QmsgInstStorage.insInfoList.length; let maxNums = qmsgInstStorageInfo.instance.getSetting().maxNums; /** * 关闭多余的消息 */ if (QmsgListLength > maxNums) { for (let index = 0; index < QmsgListLength - maxNums; index++) { let item = QmsgInstStorage.insInfoList[index]; item && item.instance.getSetting().autoClose && item.instance.close(); } } findQmsgItemInfo = qmsgInstStorageInfo; QmsgInstance = qmsgInstStorageInfo.instance; } else { if (!QmsgInstance.getRepeatNum()) { QmsgInstance.setRepeatNum(2); } else { if (QmsgInstance.getRepeatNum() >= 99) ; else { QmsgInstance.setRepeatNumIncreasing(); } } QmsgInstance.setMsgCount(); } if (QmsgInstance) { QmsgInstance.$Qmsg.setAttribute("data-count", QmsgInstance?.getRepeatNum().toString()); } else { throw new Error("QmsgInst is null"); } return QmsgInstance; } const QmsgEvent = { visibilitychange: { eventConfig: { /** * 添加visibilitychange事件监听 * 当页面切换时,如果切换前的页面存在Qmsg实例且未关闭,切换后,页面活跃度会降低,导致setTimeout/setInterval失效或丢失事件 * 监听visibilitychange,判断切换回来时,如果当前时间-开始时间大于timeout,则关闭 * 如果设置了动画,使用close,否则使用destroy */ callback() { if (document.visibilityState === "visible") { // 回到页面 for (let index = 0; index < QmsgInstStorage.insInfoList.length; index++) { let qmsgInst = QmsgInstStorage.insInfoList[index]; if ( // loading类型不被自动关闭 qmsgInst.instance.setting.type !== "loading" && qmsgInst.instance.endTime == null && qmsgInst.instance.startTime != null && Date.now() - qmsgInst.instance.startTime >= qmsgInst.instance.getSetting().timeout) { // 超出时间,关闭 qmsgInst.instance.close(); } } } }, option: { capture: true, }, }, addEvent() { if ("visibilityState" in document) { document.addEventListener("visibilitychange", QmsgEvent.visibilitychange.eventConfig.callback, QmsgEvent.visibilitychange.eventConfig.option); } else { console.error("Qmsg addEvent visibilityState not support"); } }, removeEvent() { document.removeEventListener("visibilitychange", QmsgEvent.visibilitychange.eventConfig.callback, QmsgEvent.visibilitychange.eventConfig.option); }, }, }; /* 执行兼容 */ CompatibleProcessing(); class Qmsg { /** 数据 */ $data; /** * 事件工具类 */ $eventUtils; /** * 实例化 * @param config 配置 */ constructor(config) { this.$data = { version: "2025.5.30", config: QmsgDefaultConfig, icon: QmsgIcon, instanceStorage: QmsgInstStorage, }; this.$eventUtils = QmsgEvent; this.$eventUtils.visibilitychange.addEvent(); this.config(config); } /** * 修改默认配置 * @param config 配置 */ config(config) { if (config == null) return; if (typeof config !== "object") return; // @ts-ignore QmsgDefaultConfig.INS_DEFAULT = null; // @ts-ignore QmsgDefaultConfig.INS_DEFAULT = config; } info(content, config) { let params = QmsgUtils.mergeArgs(content, config); params.type = "info"; return QmsgInstHandler.call(this, params); } warning(content, config) { let params = QmsgUtils.mergeArgs(content, config); params.type = "warning"; return QmsgInstHandler.call(this, params); } success(content, config) { let params = QmsgUtils.mergeArgs(content, config); params.type = "success"; return QmsgInstHandler.call(this, params); } error(content, config) { let params = QmsgUtils.mergeArgs(content, config); params.type = "error"; return QmsgInstHandler.call(this, params); } loading(content, config) { let params = QmsgUtils.mergeArgs(content, config); params.type = "loading"; params.autoClose = false; return QmsgInstHandler.call(this, params); } /** * 根据uuid删除Qmsg实例和元素 * @param uuid 唯一值 */ remove(uuid) { QmsgInstStorage.remove(uuid); } /** * 关闭当前Qmsg创建的所有的实例 */ closeAll() { for (let index = QmsgInstStorage.insInfoList.length - 1; index >= 0; index--) { let item = QmsgInstStorage.insInfoList[index]; item && item.instance && item.instance.close(); } } } let qmsg = new Qmsg(); return qmsg; })); //# sourceMappingURL=index.umd.js.map