Message

一款优雅的原生JS页面消息提示插件,兼容性良好,无任何依赖。 An elegant native JS page message prompt plug-in, good compatibility, no dependency.

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/462234/1603293/Message.js

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Qmsg = factory());
  5. })(this, (function () { 'use strict';
  6.  
  7. /**
  8. * 兼容处理
  9. */
  10. function CompatibleProcessing() {
  11. /* 处理Object.assign不存在的问题 */
  12. try {
  13. if (typeof Object.assign !== "function") {
  14. Object.assign = function (target) {
  15. target = Object(target);
  16. if (arguments.length > 1) {
  17. let sourceList = [...arguments].splice(1, arguments.length - 1);
  18. sourceList.forEach((sourceItem) => {
  19. for (var sourceKey in sourceItem) {
  20. if (Object.prototype.hasOwnProperty.call(sourceItem, sourceKey)) {
  21. target[sourceKey] = sourceItem[sourceKey];
  22. }
  23. }
  24. });
  25. }
  26. return target;
  27. };
  28. }
  29. }
  30. catch (error) {
  31. console.warn("Qmsg CompatibleProcessing Object.assign error", error);
  32. }
  33. /* 'classList' 兼容处理,add,remove不支持传入多个cls参数 */
  34. try {
  35. if (!("classList" in document.documentElement)) {
  36. Object.defineProperty(HTMLElement.prototype, "classList", {
  37. get: function () {
  38. var self = this;
  39. function update(fn) {
  40. return function (value) {
  41. var classes = self.className.split(/\s+/g), index = classes.indexOf(value);
  42. fn(classes, index, value);
  43. self.className = classes.join(" ");
  44. };
  45. }
  46. return {
  47. add: update(function (classes, index, value) {
  48. if (!~index)
  49. classes.push(value);
  50. }),
  51. remove: update(function (classes, index) {
  52. if (~index)
  53. classes.splice(index, 1);
  54. }),
  55. toggle: update(function (classes, index, value) {
  56. if (~index)
  57. classes.splice(index, 1);
  58. else
  59. classes.push(value);
  60. }),
  61. contains: function (value) {
  62. return !!~self.className.split(/\s+/g).indexOf(value);
  63. },
  64. item: function (index) {
  65. return self.className.split(/\s+/g)[index] || null;
  66. },
  67. };
  68. },
  69. });
  70. }
  71. }
  72. catch (error) {
  73. console.warn("Qmsg CompatibleProcessing HTMLElement.prototype.classList warning", error);
  74. }
  75. }
  76.  
  77. const QmsgDefaultConfig = {
  78. /** 声明插件名称 */
  79. get PLUGIN_NAME() {
  80. return "qmsg";
  81. },
  82. /** 命名空间,用于css和事件 */
  83. get NAMESPACE() {
  84. return "qmsg";
  85. },
  86. /** 实例配置的固定的默认值,在初始化时会插入值 */
  87. INS_DEFAULT: {},
  88. /** 实例配置的默认值 */
  89. get config() {
  90. return {
  91. parent: document.body || document.documentElement,
  92. useShadowRoot: true,
  93. shadowRootMode: "open",
  94. animation: true,
  95. autoClose: true,
  96. listenEventToPauseAutoClose: true,
  97. content: "",
  98. html: false,
  99. isHTML: false,
  100. position: "top",
  101. showClose: false,
  102. maxNums: 5,
  103. onClose: null,
  104. showIcon: true,
  105. showMoreContent: false,
  106. showReverse: false,
  107. timeout: 2500,
  108. type: "info",
  109. zIndex: 50000,
  110. style: "",
  111. customClass: "",
  112. isLimitWidth: false,
  113. limitWidthNum: 200,
  114. limitWidthWrap: "no-wrap",
  115. consoleLogContent: false,
  116. afterRender: null,
  117. };
  118. },
  119. };
  120.  
  121. const QmsgHeaderCloseIcon = /*css*/ `
  122. <svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
  123. <rect width="48" height="48" fill="white" fill-opacity="0.01"/>
  124. <path d="M14 14L34 34" stroke="#909399" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
  125. <path d="M14 34L34 14" stroke="#909399" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
  126. </svg>`;
  127. const QmsgIcon = {
  128. info: /*css*/ `
  129. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  130. <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"/>
  131. </svg>`,
  132. warning: /*css*/ `
  133.  
  134. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  135. <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"/>
  136. </svg>`,
  137. error: /*css*/ `
  138.  
  139. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  140. <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"/>
  141. </svg>`,
  142. success: /*css*/ `
  143.  
  144. <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  145. <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"/>
  146. </svg>`,
  147. loading: /*css*/ `
  148. <svg class="animate-turn" width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
  149. <path fill="#fff" fill-opacity=".01" d="M0 0h48v48H0z"/>
  150. <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"/>
  151. <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"/>
  152. </svg>`,
  153. };
  154.  
  155. /**
  156. * Qmsg实例存储
  157. */
  158. const QmsgInstStorage = {
  159. /**
  160. * 存储的Qmsg实例信息
  161. */
  162. insInfoList: [],
  163. /**
  164. * 根据uuid移除Qmsg实例
  165. * @param uuid 每个Qmsg实例的uuid
  166. * @returns
  167. * + true 移除成功
  168. * + false 移除失败
  169. */
  170. remove(uuid) {
  171. let flag = false;
  172. for (let index = 0; index < QmsgInstStorage.insInfoList.length; index++) {
  173. if (QmsgInstStorage.insInfoList[index].uuid === uuid) {
  174. QmsgInstStorage.insInfoList.splice(index, 1);
  175. flag = true;
  176. break;
  177. }
  178. }
  179. return flag;
  180. },
  181. };
  182.  
  183. const createCache = (lastNumberWeakMap) => {
  184. return (collection, nextNumber) => {
  185. lastNumberWeakMap.set(collection, nextNumber);
  186. return nextNumber;
  187. };
  188. };
  189.  
  190. /*
  191. * The value of the constant Number.MAX_SAFE_INTEGER equals (2 ** 53 - 1) but it
  192. * is fairly new.
  193. */
  194. const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER === undefined ? 9007199254740991 : Number.MAX_SAFE_INTEGER;
  195. const TWO_TO_THE_POWER_OF_TWENTY_NINE = 536870912;
  196. const TWO_TO_THE_POWER_OF_THIRTY = TWO_TO_THE_POWER_OF_TWENTY_NINE * 2;
  197. const createGenerateUniqueNumber = (cache, lastNumberWeakMap) => {
  198. return (collection) => {
  199. const lastNumber = lastNumberWeakMap.get(collection);
  200. /*
  201. * Let's try the cheapest algorithm first. It might fail to produce a new
  202. * number, but it is so cheap that it is okay to take the risk. Just
  203. * increase the last number by one or reset it to 0 if we reached the upper
  204. * bound of SMIs (which stands for small integers). When the last number is
  205. * unknown it is assumed that the collection contains zero based consecutive
  206. * numbers.
  207. */
  208. let nextNumber = lastNumber === undefined ? collection.size : lastNumber < TWO_TO_THE_POWER_OF_THIRTY ? lastNumber + 1 : 0;
  209. if (!collection.has(nextNumber)) {
  210. return cache(collection, nextNumber);
  211. }
  212. /*
  213. * If there are less than half of 2 ** 30 numbers stored in the collection,
  214. * the chance to generate a new random number in the range from 0 to 2 ** 30
  215. * is at least 50%. It's benifitial to use only SMIs because they perform
  216. * much better in any environment based on V8.
  217. */
  218. if (collection.size < TWO_TO_THE_POWER_OF_TWENTY_NINE) {
  219. while (collection.has(nextNumber)) {
  220. nextNumber = Math.floor(Math.random() * TWO_TO_THE_POWER_OF_THIRTY);
  221. }
  222. return cache(collection, nextNumber);
  223. }
  224. // Quickly check if there is a theoretical chance to generate a new number.
  225. if (collection.size > MAX_SAFE_INTEGER) {
  226. throw new Error('Congratulations, you created a collection of unique numbers which uses all available integers!');
  227. }
  228. // Otherwise use the full scale of safely usable integers.
  229. while (collection.has(nextNumber)) {
  230. nextNumber = Math.floor(Math.random() * MAX_SAFE_INTEGER);
  231. }
  232. return cache(collection, nextNumber);
  233. };
  234. };
  235.  
  236. const LAST_NUMBER_WEAK_MAP = new WeakMap();
  237. const cache = createCache(LAST_NUMBER_WEAK_MAP);
  238. const generateUniqueNumber = createGenerateUniqueNumber(cache, LAST_NUMBER_WEAK_MAP);
  239.  
  240. const isMessagePort = (sender) => {
  241. return typeof sender.start === 'function';
  242. };
  243.  
  244. const PORT_MAP = new WeakMap();
  245.  
  246. const extendBrokerImplementation = (partialBrokerImplementation) => ({
  247. ...partialBrokerImplementation,
  248. connect: ({ call }) => {
  249. return async () => {
  250. const { port1, port2 } = new MessageChannel();
  251. const portId = await call('connect', { port: port1 }, [port1]);
  252. PORT_MAP.set(port2, portId);
  253. return port2;
  254. };
  255. },
  256. disconnect: ({ call }) => {
  257. return async (port) => {
  258. const portId = PORT_MAP.get(port);
  259. if (portId === undefined) {
  260. throw new Error('The given port is not connected.');
  261. }
  262. await call('disconnect', { portId });
  263. };
  264. },
  265. isSupported: ({ call }) => {
  266. return () => call('isSupported');
  267. }
  268. });
  269.  
  270. const ONGOING_REQUESTS = new WeakMap();
  271. const createOrGetOngoingRequests = (sender) => {
  272. if (ONGOING_REQUESTS.has(sender)) {
  273. // @todo TypeScript needs to be convinced that has() works as expected.
  274. return ONGOING_REQUESTS.get(sender);
  275. }
  276. const ongoingRequests = new Map();
  277. ONGOING_REQUESTS.set(sender, ongoingRequests);
  278. return ongoingRequests;
  279. };
  280. const createBroker = (brokerImplementation) => {
  281. const fullBrokerImplementation = extendBrokerImplementation(brokerImplementation);
  282. return (sender) => {
  283. const ongoingRequests = createOrGetOngoingRequests(sender);
  284. sender.addEventListener('message', (({ data: message }) => {
  285. const { id } = message;
  286. if (id !== null && ongoingRequests.has(id)) {
  287. const { reject, resolve } = ongoingRequests.get(id);
  288. ongoingRequests.delete(id);
  289. if (message.error === undefined) {
  290. resolve(message.result);
  291. }
  292. else {
  293. reject(new Error(message.error.message));
  294. }
  295. }
  296. }));
  297. if (isMessagePort(sender)) {
  298. sender.start();
  299. }
  300. const call = (method, params = null, transferables = []) => {
  301. return new Promise((resolve, reject) => {
  302. const id = generateUniqueNumber(ongoingRequests);
  303. ongoingRequests.set(id, { reject, resolve });
  304. if (params === null) {
  305. sender.postMessage({ id, method }, transferables);
  306. }
  307. else {
  308. sender.postMessage({ id, method, params }, transferables);
  309. }
  310. });
  311. };
  312. const notify = (method, params, transferables = []) => {
  313. sender.postMessage({ id: null, method, params }, transferables);
  314. };
  315. let functions = {};
  316. for (const [key, handler] of Object.entries(fullBrokerImplementation)) {
  317. functions = { ...functions, [key]: handler({ call, notify }) };
  318. }
  319. return { ...functions };
  320. };
  321. };
  322.  
  323. // Prefilling the Maps with a function indexed by zero is necessary to be compliant with the specification.
  324. const scheduledIntervalsState = new Map([[0, null]]); // tslint:disable-line no-empty
  325. const scheduledTimeoutsState = new Map([[0, null]]); // tslint:disable-line no-empty
  326. const wrap = createBroker({
  327. clearInterval: ({ call }) => {
  328. return (timerId) => {
  329. if (typeof scheduledIntervalsState.get(timerId) === 'symbol') {
  330. scheduledIntervalsState.set(timerId, null);
  331. call('clear', { timerId, timerType: 'interval' }).then(() => {
  332. scheduledIntervalsState.delete(timerId);
  333. });
  334. }
  335. };
  336. },
  337. clearTimeout: ({ call }) => {
  338. return (timerId) => {
  339. if (typeof scheduledTimeoutsState.get(timerId) === 'symbol') {
  340. scheduledTimeoutsState.set(timerId, null);
  341. call('clear', { timerId, timerType: 'timeout' }).then(() => {
  342. scheduledTimeoutsState.delete(timerId);
  343. });
  344. }
  345. };
  346. },
  347. setInterval: ({ call }) => {
  348. return (func, delay = 0, ...args) => {
  349. const symbol = Symbol();
  350. const timerId = generateUniqueNumber(scheduledIntervalsState);
  351. scheduledIntervalsState.set(timerId, symbol);
  352. const schedule = () => call('set', {
  353. delay,
  354. now: performance.timeOrigin + performance.now(),
  355. timerId,
  356. timerType: 'interval'
  357. }).then(() => {
  358. const state = scheduledIntervalsState.get(timerId);
  359. if (state === undefined) {
  360. throw new Error('The timer is in an undefined state.');
  361. }
  362. if (state === symbol) {
  363. func(...args);
  364. // Doublecheck if the interval should still be rescheduled because it could have been cleared inside of func().
  365. if (scheduledIntervalsState.get(timerId) === symbol) {
  366. schedule();
  367. }
  368. }
  369. });
  370. schedule();
  371. return timerId;
  372. };
  373. },
  374. setTimeout: ({ call }) => {
  375. return (func, delay = 0, ...args) => {
  376. const symbol = Symbol();
  377. const timerId = generateUniqueNumber(scheduledTimeoutsState);
  378. scheduledTimeoutsState.set(timerId, symbol);
  379. call('set', {
  380. delay,
  381. now: performance.timeOrigin + performance.now(),
  382. timerId,
  383. timerType: 'timeout'
  384. }).then(() => {
  385. const state = scheduledTimeoutsState.get(timerId);
  386. if (state === undefined) {
  387. throw new Error('The timer is in an undefined state.');
  388. }
  389. if (state === symbol) {
  390. // A timeout can be savely deleted because it is only called once.
  391. scheduledTimeoutsState.delete(timerId);
  392. func(...args);
  393. }
  394. });
  395. return timerId;
  396. };
  397. }
  398. });
  399. const load = (url) => {
  400. const worker = new Worker(url);
  401. return wrap(worker);
  402. };
  403.  
  404. const createLoadOrReturnBroker = (loadBroker, worker) => {
  405. let broker = null;
  406. return () => {
  407. if (broker !== null) {
  408. return broker;
  409. }
  410. const blob = new Blob([worker], { type: 'application/javascript; charset=utf-8' });
  411. const url = URL.createObjectURL(blob);
  412. broker = loadBroker(url);
  413. // Bug #1: Edge up until v18 didn't like the URL to be revoked directly.
  414. setTimeout(() => URL.revokeObjectURL(url));
  415. return broker;
  416. };
  417. };
  418.  
  419. // This is the minified and stringified code of the worker-timers-worker package.
  420. 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
  421.  
  422. const loadOrReturnBroker = createLoadOrReturnBroker(load, worker);
  423. const clearInterval = (timerId) => loadOrReturnBroker().clearInterval(timerId);
  424. const clearTimeout = (timerId) => loadOrReturnBroker().clearTimeout(timerId);
  425. const setInterval = (...args) => loadOrReturnBroker().setInterval(...args);
  426. const setTimeout$1 = (...args) => loadOrReturnBroker().setTimeout(...args);
  427.  
  428. const QmsgUtils = {
  429. /**
  430. * 生成带插件名的名称
  431. * @param args
  432. */
  433. getNameSpacify(...args) {
  434. let result = QmsgDefaultConfig.NAMESPACE;
  435. for (let index = 0; index < args.length; ++index) {
  436. result += "-" + args[index];
  437. }
  438. return result;
  439. },
  440. /**
  441. * 判断字符是否是数字
  442. * @param text 需要判断的字符串
  443. */
  444. isNumber(text) {
  445. let isNumberPattern = /^\d+$/;
  446. return isNumberPattern.test(text);
  447. },
  448. /**
  449. * 获取唯一性的UUID
  450. */
  451. getUUID() {
  452. return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (value) {
  453. let randValue = (Math.random() * 16) | 0, newValue = value == "x" ? randValue : (randValue & 0x3) | 0x8;
  454. return newValue.toString(16);
  455. });
  456. },
  457. /**
  458. * 合并参数为配置信息,用于创建Msg实例
  459. * @param content 文本内容
  460. * @param config 配置
  461. */
  462. mergeArgs(content = "", config) {
  463. let opts = {};
  464. if (arguments.length === 0) {
  465. return opts;
  466. }
  467. if (config != null) {
  468. // 传入了2个参数
  469. // string object
  470. // object object
  471. opts.content = content;
  472. if (typeof config === "object" && config != null) {
  473. return Object.assign(opts, config);
  474. }
  475. }
  476. else {
  477. // 传入了1个参数
  478. // object
  479. // string
  480. if (typeof content === "object" && content != null) {
  481. return Object.assign(opts, content);
  482. }
  483. else {
  484. opts.content = content;
  485. }
  486. }
  487. return opts;
  488. },
  489. /**
  490. * 转换为动态对象
  491. * @param obj 需要配置的对象
  492. * @param other_obj 获取的其它对象
  493. */
  494. toDynamicObject(obj, ...other_objs) {
  495. let __obj__ = Object.assign({}, obj ?? {});
  496. Object.keys(__obj__).forEach((keyName) => {
  497. // @ts-ignore
  498. let objValue = __obj__[keyName];
  499. Object.defineProperty(__obj__, keyName, {
  500. get() {
  501. let findIndex = other_objs.findIndex((other_obj) => {
  502. // 判断其他对象中是否有该属性
  503. return (typeof other_obj === "object" &&
  504. other_obj != null &&
  505. other_obj.hasOwnProperty.call(other_obj, keyName));
  506. });
  507. if (findIndex !== -1) {
  508. // @ts-ignore
  509. let other_objValue = other_objs[findIndex][keyName];
  510. return other_objValue;
  511. }
  512. else {
  513. return objValue;
  514. }
  515. },
  516. set(newValue) {
  517. objValue = newValue;
  518. },
  519. });
  520. });
  521. return __obj__;
  522. },
  523. /**
  524. * 自动使用 Worker 执行 setTimeout
  525. */
  526. setTimeout(callback, timeout) {
  527. try {
  528. return setTimeout$1(callback, timeout);
  529. }
  530. catch (error) {
  531. return globalThis.setTimeout(callback, timeout);
  532. }
  533. },
  534. /**
  535. * 配合 QmsgUtils.setTimeout 使用
  536. */
  537. clearTimeout(timeId) {
  538. try {
  539. if (timeId != null) {
  540. clearTimeout(timeId);
  541. }
  542. }
  543. catch (error) {
  544. }
  545. finally {
  546. globalThis.clearTimeout(timeId);
  547. }
  548. },
  549. /**
  550. * 自动使用 Worker 执行 setInterval
  551. */
  552. setInterval(callback, timeout) {
  553. try {
  554. return setInterval(callback, timeout);
  555. }
  556. catch (error) {
  557. return globalThis.setInterval(callback, timeout);
  558. }
  559. },
  560. /**
  561. * 配合 QmsgUtils.setInterval 使用
  562. */
  563. clearInterval(timeId) {
  564. try {
  565. if (timeId != null) {
  566. clearInterval(timeId);
  567. }
  568. }
  569. catch (error) {
  570. }
  571. finally {
  572. globalThis.clearInterval(timeId);
  573. }
  574. },
  575. /**
  576. * 设置安全的html
  577. */
  578. setSafeHTML($el, text) {
  579. // 创建 TrustedHTML 策略(需 CSP 允许)
  580. try {
  581. $el.innerHTML = text;
  582. }
  583. catch (error) {
  584. // @ts-ignore
  585. if (globalThis.trustedTypes) {
  586. // @ts-ignore
  587. const policy = globalThis.trustedTypes.createPolicy("safe-innerHTML", {
  588. createHTML: (html) => html,
  589. });
  590. $el.innerHTML = policy.createHTML(text);
  591. }
  592. else {
  593. throw new Error("QmsgUtils trustedTypes is not defined");
  594. }
  595. }
  596. },
  597. };
  598.  
  599. const QmsgAnimation = {
  600. /** 状态 & 动画 */
  601. $state: {
  602. opening: "MessageMoveIn",
  603. done: "",
  604. closing: "MessageMoveOut",
  605. },
  606. $name: {
  607. startNameList: [
  608. "animationName",
  609. "WebkitAnimationName",
  610. "MozAnimationName",
  611. "msAnimationName",
  612. "OAnimationName",
  613. ],
  614. endNameList: [
  615. "animationend",
  616. "webkitAnimationEnd",
  617. "mozAnimationEnd",
  618. "MSAnimationEnd",
  619. "oanimationend",
  620. ],
  621. },
  622. /**
  623. * 是否支持动画属性
  624. * @private
  625. */
  626. __CAN_ANIMATION__: void 0,
  627. /**
  628. * 是否支持动画属性
  629. */
  630. get CAN_ANIMATION() {
  631. this.__CAN_ANIMATION__ =
  632. this.__CAN_ANIMATION__ ??
  633. this.getStyleAnimationNameValue(document.createElement("div")) != null;
  634. return this.__CAN_ANIMATION__;
  635. },
  636. /**
  637. * 获取元素上的animationName属性
  638. * @param element
  639. */
  640. getStyleAnimationNameValue(element) {
  641. for (let index = 0; index < this.$name.startNameList.length; index++) {
  642. let animationName = this.$name.startNameList[index];
  643. let animationNameValue = element.style[animationName];
  644. if (animationNameValue != null) {
  645. return animationNameValue;
  646. }
  647. }
  648. },
  649. /**
  650. * 设置元素上的animationName属性
  651. * @param element
  652. * @param animationNameValue
  653. */
  654. setStyleAnimationName(element, animationNameValue = "") {
  655. this.$name.startNameList.forEach((animationName) => {
  656. if (animationName in element.style) {
  657. element.style[animationName] = animationNameValue;
  658. }
  659. });
  660. },
  661. };
  662.  
  663. const QmsgCSS = {
  664. css: /*css*/ `@charset "utf-8";
  665. .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;}
  666. .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%);}
  667. .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);}
  668. .qmsg.qmsg-data-position-bottomleft .qmsg-item,.qmsg.qmsg-data-position-left .qmsg-item,.qmsg.qmsg-data-position-topleft .qmsg-item{text-align:left;}
  669. .qmsg.qmsg-data-position-bottom .qmsg-item,.qmsg.qmsg-data-position-center .qmsg-item,.qmsg.qmsg-data-position-top .qmsg-item{text-align:center;}
  670. .qmsg.qmsg-data-position-bottomright .qmsg-item,.qmsg.qmsg-data-position-right .qmsg-item,.qmsg.qmsg-data-position-topright .qmsg-item{text-align:right;}
  671. .qmsg .qmsg-item{position:relative;padding:8px;text-align:center;-webkit-animation-duration:.3s;animation-duration:.3s;}
  672. .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;}
  673. .qmsg .qmsg-item:first-child{margin-top:-8px;}
  674. .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;}
  675. .qmsg .qmsg-content [class^=qmsg-content-]{display:flex;align-items:center;}
  676. .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;}
  677. .qmsg .qmsg-icon svg{display:inline-block;}
  678. .qmsg .qmsg-content .qmsg-show-more-content{display:flex;align-items:center;white-space:unset;overflow:unset;text-overflow:unset;padding-right:unset}
  679. .qmsg .qmsg-content-info .qmsg-icon{color:#1890ff;}
  680. .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;}
  681. .qmsg .qmsg-icon-close:hover>svg path{stroke:#555;}
  682. .qmsg .qmsg-icon-close.qmsg-show-more-content{position:unset;overflow:unset;padding-left:6px;margin-right:0}
  683. .qmsg .animate-turn{animation:MessageTurn 1s linear infinite;-webkit-animation:MessageTurn 1s linear infinite;}
  684. @keyframes MessageTurn{
  685. 0%{-webkit-transform:rotate(0);}
  686. 25%{-webkit-transform:rotate(90deg);}
  687. 50%{-webkit-transform:rotate(180deg);}
  688. 75%{-webkit-transform:rotate(270deg);}
  689. 100%{-webkit-transform:rotate(360deg);}
  690. }
  691. @-webkit-keyframes MessageTurn{
  692. 0%{-webkit-transform:rotate(0);}
  693. 25%{-webkit-transform:rotate(90deg);}
  694. 50%{-webkit-transform:rotate(180deg);}
  695. 75%{-webkit-transform:rotate(270deg);}
  696. 100%{-webkit-transform:rotate(360deg);}
  697. }
  698. @-webkit-keyframes MessageMoveOut{
  699. 0%{max-height:150px;opacity:1;}
  700. to{max-height:0;opacity:0;}
  701. }
  702. @keyframes MessageMoveOut{
  703. 0%{max-height:150px;opacity:1;}
  704. to{max-height:0;opacity:0;}
  705. }
  706. @-webkit-keyframes MessageMoveIn{
  707. 0%{opacity:0;transform:translateY(-100%);transform-origin:0 0;}
  708. to{opacity:1;transform:translateY(0);transform-origin:0 0;}
  709. }
  710. @keyframes MessageMoveIn{
  711. 0%{opacity:0;transform:translateY(-100%);transform-origin:0 0;}
  712. to{opacity:1;transform:translateY(0);transform-origin:0 0;}
  713. }
  714. @-webkit-keyframes MessageShake{
  715. 0%,100%{opacity:1;transform:translateX(0);}
  716. 25%,75%{opacity:.75;transform:translateX(-4px);}
  717. 50%{opacity:.25;transform:translateX(4px);}
  718. }
  719. @keyframes MessageShake{
  720. 0%,100%{opacity:1;transform:translateX(0);}
  721. 25%,75%{opacity:.75;transform:translateX(-4px);}
  722. 50%{opacity:.25;transform:translateX(4px);}
  723. }`,
  724. /**
  725. * 获取CSS元素
  726. */
  727. getStyleElement() {
  728. let $style = document.createElement("style");
  729. $style.setAttribute("type", "text/css");
  730. $style.setAttribute("data-type", QmsgDefaultConfig.PLUGIN_NAME);
  731. QmsgUtils.setSafeHTML($style, this.css);
  732. return $style;
  733. },
  734. };
  735.  
  736. /**
  737. * 每条消息的构造函数
  738. */
  739. class QmsgMsg {
  740. /**
  741. * setTimeout的id
  742. */
  743. timeId = void 0;
  744. /**
  745. * 启动时间
  746. */
  747. startTime;
  748. /**
  749. * 关闭时间
  750. */
  751. endTime;
  752. /**
  753. * Qmsg的配置
  754. */
  755. setting;
  756. /**
  757. * uuid
  758. */
  759. uuid;
  760. /**
  761. * 当前动画状态
  762. */
  763. state;
  764. /**
  765. * 当前相同消息的数量
  766. */
  767. repeatNum;
  768. /**
  769. * 主元素
  770. */
  771. $Qmsg;
  772. constructor(config, uuid) {
  773. this.timeId = void 0;
  774. this.startTime = Date.now();
  775. this.endTime = null;
  776. // this.#setting = Object.assign({}, QmsgStore.DEFAULT, this.option);
  777. this.setting = QmsgUtils.toDynamicObject(QmsgDefaultConfig.config, config, QmsgDefaultConfig.INS_DEFAULT);
  778. this.uuid = uuid;
  779. this.state = "opening";
  780. this.$Qmsg = document.createElement("div");
  781. this.repeatNum = 1;
  782. this.detectionType();
  783. this.init();
  784. let consoleLogContent = typeof this.setting.consoleLogContent === "function"
  785. ? this.setting.consoleLogContent(this)
  786. : this.setting.consoleLogContent;
  787. if (consoleLogContent) {
  788. // 控制台输出content
  789. console.log(this.setting.content);
  790. }
  791. if (typeof this.setting.afterRender === "function") {
  792. this.setting.afterRender(this);
  793. }
  794. }
  795. /**
  796. * 获取当前配置
  797. * @returns
  798. */
  799. getSetting() {
  800. return this.setting;
  801. }
  802. /**
  803. * 获取当前相同的数量
  804. * @returns
  805. */
  806. getRepeatNum() {
  807. return this.repeatNum;
  808. }
  809. /**
  810. * 设置repeatNum值
  811. * @param num 重复的数量
  812. */
  813. setRepeatNum(num) {
  814. this.repeatNum = num;
  815. }
  816. /**
  817. * 设置repeatNum自增
  818. */
  819. setRepeatNumIncreasing() {
  820. this.repeatNum++;
  821. }
  822. /**
  823. * 初始化元素
  824. */
  825. init() {
  826. let QmsgContext = this;
  827. if (this.setting.customClass &&
  828. typeof this.setting.customClass === "string") {
  829. /* 设置自定义类名 */
  830. this.$Qmsg.classList.add(this.setting.customClass);
  831. }
  832. // 设置svg图标
  833. let $svg = QmsgIcon[this.setting.type || "info"];
  834. let contentClassName = QmsgUtils.getNameSpacify("content-" + this.setting.type || "info");
  835. if (this.setting.showClose) {
  836. // 显示 关闭图标
  837. contentClassName += " " + QmsgUtils.getNameSpacify("content-with-close");
  838. }
  839. // 内容兼容处理
  840. let content = this.setting.content || "";
  841. // 关闭图标 自定义额外className
  842. let extraCloseIconClassName = "";
  843. // 关闭图标svg
  844. let $closeSvg = QmsgHeaderCloseIcon;
  845. if (this.setting.showMoreContent) {
  846. // 显示更多内容
  847. contentClassName += "qmsg-show-more-content";
  848. extraCloseIconClassName += "qmsg-show-more-content";
  849. }
  850. let $closeIcon = "";
  851. if (this.setting.showClose) {
  852. /* 显示右上角的关闭图标按钮 */
  853. $closeIcon = /*html*/ `<i class="qmsg-icon qmsg-icon-close ${extraCloseIconClassName}">${$closeSvg}</i>`;
  854. }
  855. /* 内容 */
  856. let $content = document.createElement("span");
  857. let $positionClassName = QmsgUtils.getNameSpacify("data-position", this.setting.position.toLowerCase());
  858. if (this.setting.html || this.setting.isHTML) {
  859. /* 内容是html */
  860. QmsgUtils.setSafeHTML($content, content);
  861. }
  862. else {
  863. /* 内容是纯文本 */
  864. $content.innerText = content;
  865. }
  866. if (this.setting.isLimitWidth) {
  867. /* 限制宽度 */
  868. let limitWidthNum = this.setting.limitWidthNum;
  869. if (typeof limitWidthNum === "string") {
  870. if (QmsgUtils.isNumber(limitWidthNum)) {
  871. limitWidthNum = limitWidthNum + "px";
  872. }
  873. }
  874. else {
  875. limitWidthNum = limitWidthNum.toString() + "px";
  876. }
  877. $content.style.maxWidth = limitWidthNum;
  878. $content.style.width = limitWidthNum;
  879. /* 设置换行 */
  880. if (this.setting.limitWidthWrap === "no-wrap") {
  881. /* 禁止换行 */
  882. $content.style.whiteSpace = "nowrap";
  883. }
  884. else if (this.setting.limitWidthWrap === "ellipsis") {
  885. /* 禁止换行且显示省略号 */
  886. $content.style.whiteSpace = "nowrap";
  887. $content.style.overflow = "hidden";
  888. $content.style.textOverflow = "ellipsis";
  889. }
  890. else if (this.setting.limitWidthWrap === "wrap") {
  891. /* 允许换行 */
  892. /* 默认的 */
  893. $content.style.whiteSpace = "";
  894. }
  895. }
  896. QmsgUtils.setSafeHTML(this.$Qmsg,
  897. /*html*/ `
  898. <div class="qmsg-content">
  899. <div class="${contentClassName}">
  900. ${this.setting.showIcon ? `<i class="qmsg-icon">${$svg}</i>` : ""}
  901. ${$content.outerHTML}
  902. ${$closeIcon}
  903. </div>
  904. </div>
  905. `);
  906. /** 内容容器 */
  907. let $contentContainer = this.$Qmsg.querySelector(".qmsg-content");
  908. this.$Qmsg.classList.add(QmsgUtils.getNameSpacify("item"));
  909. this.$Qmsg.setAttribute(QmsgUtils.getNameSpacify("uuid"), this.uuid);
  910. /** 总根元素 */
  911. let $shadowContainer;
  912. /** 根元素 */
  913. let $shadowRoot;
  914. /** 容器包裹的元素 */
  915. let $wrapper;
  916. $shadowContainer = document.querySelector(".qmsg-shadow-container");
  917. $shadowRoot = this.setting.useShadowRoot
  918. ? $shadowContainer?.shadowRoot
  919. : $shadowContainer;
  920. if (!$shadowContainer) {
  921. // 页面中不存在ShadowRoot容器元素
  922. // 添加新增的ShadowRoot容器元素
  923. $shadowContainer = document.createElement("div");
  924. $shadowContainer.className = "qmsg-shadow-container";
  925. if (this.setting.useShadowRoot) {
  926. $shadowRoot = $shadowContainer.attachShadow({
  927. mode: this.setting.shadowRootMode,
  928. });
  929. }
  930. else {
  931. $shadowRoot = $shadowContainer;
  932. }
  933. $shadowRoot.appendChild(QmsgCSS.getStyleElement());
  934. if (this.setting.style != null) {
  935. // 插入自定义的style
  936. // 这里需要插入到每一条的Qmsg内,以便移除实例时把style也移除
  937. let __$ownStyle__ = document.createElement("style");
  938. __$ownStyle__.setAttribute("type", "text/css");
  939. __$ownStyle__.setAttribute("data-id", this.uuid);
  940. QmsgUtils.setSafeHTML(__$ownStyle__, this.setting.style);
  941. $contentContainer.insertAdjacentElement("afterend", __$ownStyle__);
  942. }
  943. this.setting.parent.appendChild($shadowContainer);
  944. }
  945. if ($shadowRoot == null) {
  946. throw new Error("QmsgInst " + QmsgDefaultConfig.PLUGIN_NAME + " $shadowRoot is null");
  947. }
  948. $wrapper = $shadowRoot.querySelector(`.${QmsgDefaultConfig.NAMESPACE}.${$positionClassName}`);
  949. if (!$wrapper) {
  950. $wrapper = document.createElement("div");
  951. $wrapper.classList.add(QmsgDefaultConfig.NAMESPACE, QmsgUtils.getNameSpacify("wrapper"), QmsgUtils.getNameSpacify("is-initialized"), $positionClassName);
  952. $shadowRoot.appendChild($wrapper);
  953. }
  954. if (this.setting.showReverse) {
  955. $wrapper.style.flexDirection = "column-reverse";
  956. }
  957. else {
  958. $wrapper.style.flexDirection = "column";
  959. }
  960. let zIndex = this.setting.zIndex;
  961. if (typeof zIndex === "function") {
  962. zIndex = zIndex();
  963. }
  964. if (!isNaN(zIndex)) {
  965. $wrapper.style.zIndex = zIndex.toString();
  966. }
  967. $wrapper.appendChild(this.$Qmsg);
  968. this.setState(this.$Qmsg, "opening");
  969. if (this.setting.showClose) {
  970. /* 关闭按钮绑定点击事件 */
  971. let $closeIcon = this.$Qmsg.querySelector(".qmsg-icon-close");
  972. if ($closeIcon) {
  973. $closeIcon.addEventListener("click", (evt) => {
  974. QmsgContext.close();
  975. });
  976. }
  977. }
  978. /* 监听动画完成 */
  979. let animationendEvent = (event) => {
  980. let animationNameValue = QmsgAnimation.getStyleAnimationNameValue(QmsgContext.$Qmsg);
  981. if (animationNameValue === QmsgAnimation.$state.closing) {
  982. // 当前触发的是关闭
  983. QmsgContext.endTime = Date.now();
  984. QmsgContext.destroy();
  985. }
  986. QmsgAnimation.setStyleAnimationName(QmsgContext.$Qmsg);
  987. };
  988. QmsgAnimation.$name.endNameList.forEach(function (animationendName) {
  989. QmsgContext.$Qmsg.addEventListener(animationendName, animationendEvent);
  990. });
  991. /* 自动关闭 */
  992. if (this.setting.autoClose && this.setting.listenEventToPauseAutoClose) {
  993. // 鼠标|触摸滑入时,清除自动关闭的定时器
  994. // 鼠标|触摸滑出时,重新设置定时器
  995. this.resetAutoCloseTimer();
  996. /**
  997. * 鼠标滑入
  998. *
  999. * + 清除定时器
  1000. * + 清除开始时间
  1001. * + 清除结束时间
  1002. */
  1003. let enterEvent = (event) => {
  1004. this.clearAutoCloseTimer();
  1005. };
  1006. /** 鼠标滑出,重启定时器,创建新的开始时间和timeId */
  1007. let leaveEvent = (event) => {
  1008. if (this.timeId != null) {
  1009. // 似乎enterEvent函数未正确调用?
  1010. console.warn("QmsgInst timeId is not null,mouseenter may be not first trigger,timeId:" +
  1011. this.timeId);
  1012. return;
  1013. }
  1014. this.startAutoCloseTimer();
  1015. };
  1016. let isRemoveMouseEvent = false;
  1017. this.$Qmsg.addEventListener("mouseenter", enterEvent);
  1018. this.$Qmsg.addEventListener("mouseleave", leaveEvent);
  1019. this.$Qmsg.addEventListener("touchstart", (evt) => {
  1020. // 由于移动端不支持mouseout且会触发mouseenter
  1021. // 那么需要移除该监听
  1022. if (!isRemoveMouseEvent) {
  1023. isRemoveMouseEvent = true;
  1024. this.$Qmsg.removeEventListener("mouseenter", enterEvent);
  1025. this.$Qmsg.removeEventListener("mouseleave", leaveEvent);
  1026. }
  1027. enterEvent();
  1028. });
  1029. this.$Qmsg.addEventListener("touchend", leaveEvent);
  1030. this.$Qmsg.addEventListener("touchcancel", leaveEvent);
  1031. }
  1032. }
  1033. /**
  1034. * 对timeout进行检测并转换
  1035. * 当timeout为string时,转换为number
  1036. * timeout必须在规定范围内
  1037. */
  1038. detectionType() {
  1039. if (this.setting.timeout != null &&
  1040. typeof this.setting.timeout === "string") {
  1041. this.setting.timeout = parseInt(this.setting.timeout);
  1042. }
  1043. if (isNaN(this.setting.timeout)) {
  1044. this.setting.timeout = QmsgDefaultConfig.config.timeout;
  1045. }
  1046. if (!(this.setting.timeout != null &&
  1047. parseInt(this.setting.timeout.toString()) >= 0 &&
  1048. parseInt(this.setting.timeout.toString()) <= Number.MAX_VALUE)) {
  1049. this.setting.timeout = QmsgDefaultConfig.config.timeout;
  1050. }
  1051. if (typeof this.setting.zIndex === "function") {
  1052. this.setting.zIndex = this.setting.zIndex();
  1053. }
  1054. if (this.setting.zIndex != null &&
  1055. typeof this.setting.zIndex === "string") {
  1056. this.setting.zIndex = parseInt(this.setting.zIndex);
  1057. }
  1058. if (isNaN(this.setting.zIndex)) {
  1059. this.setting.zIndex =
  1060. typeof QmsgDefaultConfig.config.zIndex === "function"
  1061. ? QmsgDefaultConfig.config.zIndex()
  1062. : QmsgDefaultConfig.config.zIndex;
  1063. }
  1064. }
  1065. /**
  1066. * 设置元素动画状态 开启/关闭
  1067. * @param QmsgMsg
  1068. * @param state
  1069. */
  1070. setState(element, state) {
  1071. if (!state || !QmsgAnimation.$state[state])
  1072. return;
  1073. this.state = state;
  1074. QmsgAnimation.setStyleAnimationName(element, QmsgAnimation.$state[state]);
  1075. }
  1076. /**
  1077. * 设置消息数量统计
  1078. */
  1079. setMsgCount() {
  1080. let countClassName = QmsgUtils.getNameSpacify("count");
  1081. let wrapperClassName = `div.${QmsgUtils.getNameSpacify("data-position", this.setting.position.toLowerCase())} [class^="qmsg-content-"]`;
  1082. let $content = this.$Qmsg.querySelector(wrapperClassName);
  1083. if (!$content) {
  1084. throw new Error("QmsgInst $content is null");
  1085. }
  1086. let $count = $content.querySelector("." + countClassName);
  1087. if (!$count) {
  1088. $count = document.createElement("span");
  1089. $count.classList.add(countClassName);
  1090. $content.appendChild($count);
  1091. }
  1092. // 获取重复显示内容的实例数量
  1093. let repeatNum = this.getRepeatNum();
  1094. QmsgUtils.setSafeHTML($count, repeatNum.toString());
  1095. QmsgAnimation.setStyleAnimationName($count);
  1096. QmsgAnimation.setStyleAnimationName($count, "MessageShake");
  1097. this.resetAutoCloseTimer();
  1098. }
  1099. /**
  1100. * 清除旧的自动关闭定时器
  1101. */
  1102. clearAutoCloseTimer() {
  1103. /* 重置定时器 */
  1104. QmsgUtils.clearTimeout(this.timeId);
  1105. this.timeId = void 0;
  1106. this.startTime = null;
  1107. this.endTime = null;
  1108. }
  1109. /**
  1110. * 开始自动关闭定时器
  1111. */
  1112. startAutoCloseTimer() {
  1113. if (this.setting.autoClose && this.setting.listenEventToPauseAutoClose) {
  1114. this.startTime = Date.now();
  1115. this.endTime = null;
  1116. this.timeId = QmsgUtils.setTimeout(() => {
  1117. this.close();
  1118. }, this.setting.timeout);
  1119. }
  1120. }
  1121. /**
  1122. * 重置自动关闭定时器(会自动清理旧的定时器)
  1123. */
  1124. resetAutoCloseTimer() {
  1125. this.clearAutoCloseTimer();
  1126. this.startAutoCloseTimer();
  1127. }
  1128. /**
  1129. * 关闭Qmsg(会触发动画)
  1130. */
  1131. close() {
  1132. this.setState(this.$Qmsg, "closing");
  1133. if (QmsgAnimation.CAN_ANIMATION) {
  1134. /* 支持动画 */
  1135. QmsgInstStorage.remove(this.uuid);
  1136. }
  1137. else {
  1138. /* 不支持动画 */
  1139. this.destroy();
  1140. }
  1141. let onCloseCallBack = this.setting.onClose;
  1142. if (onCloseCallBack && typeof onCloseCallBack === "function") {
  1143. onCloseCallBack.call(this);
  1144. }
  1145. }
  1146. /**
  1147. * 销毁Qmsg
  1148. */
  1149. destroy() {
  1150. this.endTime = Date.now();
  1151. this.$Qmsg.remove();
  1152. QmsgUtils.clearTimeout(this.timeId);
  1153. QmsgInstStorage.remove(this.uuid);
  1154. this.timeId = void 0;
  1155. }
  1156. /**
  1157. * 获取内容元素
  1158. */
  1159. get $content() {
  1160. let $content = this.$Qmsg.querySelector("div[class^=qmsg-content-] > span");
  1161. if (!$content) {
  1162. throw new Error("QmsgInst $content is null");
  1163. }
  1164. return $content;
  1165. }
  1166. /**
  1167. * 设置内容文本
  1168. */
  1169. setText(text) {
  1170. let $content = this.$content;
  1171. $content.innerText = text;
  1172. this.setting.content = text;
  1173. }
  1174. /**
  1175. * 设置内容超文本
  1176. */
  1177. setHTML(text) {
  1178. let $content = this.$content;
  1179. QmsgUtils.setSafeHTML($content, text);
  1180. this.setting.content = text;
  1181. }
  1182. }
  1183.  
  1184. /**
  1185. * 通过配置信息 来判断是否为同一条消息,并返回消息实例
  1186. * @param config 配置项
  1187. */
  1188. function QmsgInstHandler(config = {}) {
  1189. let optionString = JSON.stringify(config);
  1190. /* 寻找已生成的实例是否存在配置相同的 */
  1191. let findQmsgItemInfo = QmsgInstStorage.insInfoList.find((item) => {
  1192. return item.config === optionString;
  1193. });
  1194. let QmsgInstance = findQmsgItemInfo?.instance;
  1195. if (QmsgInstance == null) {
  1196. /* 不存在,创建个新的 */
  1197. let uuid = QmsgUtils.getUUID();
  1198. let qmsgInstStorageInfo = {
  1199. uuid: uuid,
  1200. config: optionString,
  1201. instance: new QmsgMsg(config, uuid),
  1202. };
  1203. QmsgInstStorage.insInfoList.push(qmsgInstStorageInfo);
  1204. let QmsgListLength = QmsgInstStorage.insInfoList.length;
  1205. let maxNums = qmsgInstStorageInfo.instance.getSetting().maxNums;
  1206. /**
  1207. * 关闭多余的消息
  1208. */
  1209. if (QmsgListLength > maxNums) {
  1210. for (let index = 0; index < QmsgListLength - maxNums; index++) {
  1211. let item = QmsgInstStorage.insInfoList[index];
  1212. item && item.instance.getSetting().autoClose && item.instance.close();
  1213. }
  1214. }
  1215. findQmsgItemInfo = qmsgInstStorageInfo;
  1216. QmsgInstance = qmsgInstStorageInfo.instance;
  1217. }
  1218. else {
  1219. if (!QmsgInstance.getRepeatNum()) {
  1220. QmsgInstance.setRepeatNum(2);
  1221. }
  1222. else {
  1223. if (QmsgInstance.getRepeatNum() >= 99) ;
  1224. else {
  1225. QmsgInstance.setRepeatNumIncreasing();
  1226. }
  1227. }
  1228. QmsgInstance.setMsgCount();
  1229. }
  1230. if (QmsgInstance) {
  1231. QmsgInstance.$Qmsg.setAttribute("data-count", QmsgInstance?.getRepeatNum().toString());
  1232. }
  1233. else {
  1234. throw new Error("QmsgInst is null");
  1235. }
  1236. return QmsgInstance;
  1237. }
  1238.  
  1239. const QmsgEvent = {
  1240. visibilitychange: {
  1241. eventConfig: {
  1242. /**
  1243. * 添加visibilitychange事件监听
  1244. * 当页面切换时,如果切换前的页面存在Qmsg实例且未关闭,切换后,页面活跃度会降低,导致setTimeout/setInterval失效或丢失事件
  1245. * 监听visibilitychange,判断切换回来时,如果当前时间-开始时间大于timeout,则关闭
  1246. * 如果设置了动画,使用close,否则使用destroy
  1247. */
  1248. callback() {
  1249. if (document.visibilityState === "visible") {
  1250. // 回到页面
  1251. for (let index = 0; index < QmsgInstStorage.insInfoList.length; index++) {
  1252. let qmsgInst = QmsgInstStorage.insInfoList[index];
  1253. if (
  1254. // loading类型不被自动关闭
  1255. qmsgInst.instance.setting.type !== "loading" &&
  1256. qmsgInst.instance.endTime == null &&
  1257. qmsgInst.instance.startTime != null &&
  1258. Date.now() - qmsgInst.instance.startTime >=
  1259. qmsgInst.instance.getSetting().timeout) {
  1260. // 超出时间,关闭
  1261. qmsgInst.instance.close();
  1262. }
  1263. }
  1264. }
  1265. },
  1266. option: {
  1267. capture: true,
  1268. },
  1269. },
  1270. addEvent() {
  1271. if ("visibilityState" in document) {
  1272. document.addEventListener("visibilitychange", QmsgEvent.visibilitychange.eventConfig.callback, QmsgEvent.visibilitychange.eventConfig.option);
  1273. }
  1274. else {
  1275. console.error("Qmsg addEvent visibilityState not support");
  1276. }
  1277. },
  1278. removeEvent() {
  1279. document.removeEventListener("visibilitychange", QmsgEvent.visibilitychange.eventConfig.callback, QmsgEvent.visibilitychange.eventConfig.option);
  1280. },
  1281. },
  1282. };
  1283.  
  1284. /* 执行兼容 */
  1285. CompatibleProcessing();
  1286. class Qmsg {
  1287. /** 数据 */
  1288. $data;
  1289. /**
  1290. * 事件工具类
  1291. */
  1292. $eventUtils;
  1293. /**
  1294. * 实例化
  1295. * @param config 配置
  1296. */
  1297. constructor(config) {
  1298. this.$data = {
  1299. version: "2025.6.7",
  1300. config: QmsgDefaultConfig,
  1301. icon: QmsgIcon,
  1302. instanceStorage: QmsgInstStorage,
  1303. };
  1304. this.$eventUtils = QmsgEvent;
  1305. this.$eventUtils.visibilitychange.addEvent();
  1306. this.config(config);
  1307. }
  1308. /**
  1309. * 修改默认配置
  1310. * @param config 配置
  1311. */
  1312. config(config) {
  1313. if (config == null)
  1314. return;
  1315. if (typeof config !== "object")
  1316. return;
  1317. // @ts-ignore
  1318. QmsgDefaultConfig.INS_DEFAULT = null;
  1319. // @ts-ignore
  1320. QmsgDefaultConfig.INS_DEFAULT = config;
  1321. }
  1322. info(content, config) {
  1323. let params = QmsgUtils.mergeArgs(content, config);
  1324. params.type = "info";
  1325. return QmsgInstHandler.call(this, params);
  1326. }
  1327. warning(content, config) {
  1328. let params = QmsgUtils.mergeArgs(content, config);
  1329. params.type = "warning";
  1330. return QmsgInstHandler.call(this, params);
  1331. }
  1332. success(content, config) {
  1333. let params = QmsgUtils.mergeArgs(content, config);
  1334. params.type = "success";
  1335. return QmsgInstHandler.call(this, params);
  1336. }
  1337. error(content, config) {
  1338. let params = QmsgUtils.mergeArgs(content, config);
  1339. params.type = "error";
  1340. return QmsgInstHandler.call(this, params);
  1341. }
  1342. loading(content, config) {
  1343. let params = QmsgUtils.mergeArgs(content, config);
  1344. params.type = "loading";
  1345. params.autoClose = false;
  1346. return QmsgInstHandler.call(this, params);
  1347. }
  1348. /**
  1349. * 根据uuid删除Qmsg实例和元素
  1350. * @param uuid 唯一值
  1351. */
  1352. remove(uuid) {
  1353. QmsgInstStorage.remove(uuid);
  1354. }
  1355. /**
  1356. * 关闭当前Qmsg创建的所有的实例
  1357. */
  1358. closeAll() {
  1359. for (let index = QmsgInstStorage.insInfoList.length - 1; index >= 0; index--) {
  1360. let item = QmsgInstStorage.insInfoList[index];
  1361. item && item.instance && item.instance.close();
  1362. }
  1363. }
  1364. }
  1365. let qmsg = new Qmsg();
  1366.  
  1367. return qmsg;
  1368.  
  1369. }));
  1370. //# sourceMappingURL=index.umd.js.map