Qmsg

消息提示

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/455576/1122361/Qmsg.js

  1. ;(function (root, Msg) {
  2. if (typeof exports === 'object' && typeof module !== 'undefined') {
  3. module.exports = Msg
  4. } else if (typeof define === 'function' && define.amd) {
  5. define([], function () {
  6. return Msg(root);
  7. });
  8. } else {
  9. root.Qmsg = Msg(root);
  10. }
  11. })(this, function (global) {
  12. 'use strict';
  13.  
  14. // assign 兼容处理
  15. if (typeof Object.assign != 'function') {
  16. Object.assign = function (target) {
  17. if (target == null) {
  18. throw new TypeError('Cannot convert undefined or null to object');
  19. }
  20.  
  21. target = Object(target);
  22. for (var index = 1; index < arguments.length; index++) {
  23. var source = arguments[index];
  24. if (source != null) {
  25. for (var key in source) {
  26. if (Object.prototype.hasOwnProperty.call(source, key)) {
  27. target[key] = source[key];
  28. }
  29. }
  30. }
  31. }
  32. return target;
  33. }
  34. }
  35.  
  36. // 'classList' 兼容处理,add,remove不支持传入多个cls参数
  37. if (!("classList" in document.documentElement)) {
  38. Object.defineProperty(HTMLElement.prototype, 'classList', {
  39. get: function () {
  40. var self = this;
  41.  
  42. function update(fn) {
  43. return function (value) {
  44. var classes = self.className.split(/\s+/g),
  45. index = classes.indexOf(value);
  46. fn(classes, index, value);
  47. self.className = classes.join(" ");
  48. }
  49. }
  50.  
  51. return {
  52. add: update(function (classes, index, value) {
  53. if (!~index) classes.push(value);
  54. }),
  55.  
  56. remove: update(function (classes, index) {
  57. if (~index) classes.splice(index, 1);
  58. }),
  59.  
  60. toggle: update(function (classes, index, value) {
  61. if (~index)
  62. classes.splice(index, 1);
  63. else
  64. classes.push(value);
  65. }),
  66.  
  67. contains: function (value) {
  68. return !!~self.className.split(/\s+/g).indexOf(value);
  69. },
  70.  
  71. item: function (i) {
  72. return self.className.split(/\s+/g)[i] || null;
  73. }
  74. };
  75. }
  76. });
  77. }
  78.  
  79. /**
  80. * 声明插件名称
  81. */
  82. var PLUGIN_NAME = "qmsg";
  83.  
  84. /**
  85. * 命名空间 用于css和事件
  86. */
  87. var NAMESPACE = global && global.QMSG_GLOBALS && global.QMSG_GLOBALS.NAMESPACE || PLUGIN_NAME;
  88.  
  89. /**
  90. * 状态 & 动画
  91. * 显示中,显示完成,关闭中
  92. */
  93. var STATES = {
  94. opening: 'MessageMoveIn',
  95. done: '',
  96. closing: 'MessageMoveOut'
  97. }
  98.  
  99. /**
  100. * 全局默认配置
  101. * 可在引入js之前通过QMSG_GLOBALS.DEFAULTS进行配置
  102. * position {String} 位置,仅支持'center','right','left',默认'center'
  103. * type {String} 类型,支持'info','warning','success','error','loading'
  104. * showClose {Boolean} 是否显示关闭图标,默认为false不显示
  105. * timeout {Number} 多久后自动关闭,单位ms,默认2500
  106. * autoClose {Boolean} 是否自动关闭,默认true,注意在type为loading的时候自动关闭为false
  107. * content {String} 提示的内容
  108. * onClose {Function} 关闭的回调函数
  109. */
  110. var DEFAULTS = Object.assign({
  111. position: 'center',
  112. type: "info",
  113. showClose: false,
  114. timeout: 2500,
  115. animation: true,
  116. autoClose: true,
  117. content: "",
  118. onClose: null,
  119. maxNums: 5,
  120. html: false
  121. }, global && global.QMSG_GLOBALS && global.QMSG_GLOBALS.DEFAULTS);
  122.  
  123. /**
  124. * 设置icon html代码
  125. */
  126. var ICONS = {
  127. info: '<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>',
  128. warning: '<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>',
  129. error: '<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>',
  130. success: '<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>',
  131. loading: '<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>',
  132. close: '<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>',
  133. }
  134.  
  135. /**
  136. * 是否支持动画属性
  137. * @type {Boolean}
  138. */
  139. var CAN_ANIMATION = (function () {
  140. var style = document.createElement('div').style;
  141. return style.animationName !== undefined ||
  142. style.WebkitAnimationName !== undefined ||
  143. style.MozAnimationName !== undefined ||
  144. style.msAnimationName !== undefined ||
  145. style.OAnimationName !== undefined;
  146. })();
  147.  
  148. /**
  149. * 生成带插件名的名称
  150. * @param {...String}
  151. * @returns {String}
  152. */
  153. function namespacify() {
  154. var res = NAMESPACE;
  155. for (var i = 0; i < arguments.length; ++i) {
  156. res += '-' + arguments[i];
  157. }
  158. return res;
  159. }
  160.  
  161. /**
  162. * 每条消息的构造函数
  163. * @param {Objetc} opts 配置参数,参考DEFAULTS
  164. */
  165. function Msg(opts) {
  166. var oMsg = this;
  167. oMsg.settings = Object.assign({}, DEFAULTS, opts || {});
  168. oMsg.id = Qmsg.instanceCount;
  169. var timeout = oMsg.settings.timeout;
  170. timeout = timeout && parseInt(timeout >= 0) && parseInt(timeout) <= Math.NEGATIVE_INFINITY ? parseInt(timeout) : DEFAULTS.timeout;
  171. oMsg.timeout = timeout;
  172. oMsg.settings.timeout = timeout;
  173. oMsg.timer = null;
  174. var $elem = document.createElement("div");
  175. var $svg = ICONS[oMsg.settings.type || 'info'];
  176. var contentClassName = namespacify("content-" + oMsg.settings.type || 'info');
  177. contentClassName += oMsg.settings.showClose ? ' ' + namespacify('content-with-close') : ''
  178. var content = oMsg.settings.content || '';
  179. var $closeSvg = ICONS['close'];
  180. var $closeIcon = oMsg.settings.showClose ? '<i class="qmsg-icon qmsg-icon-close">' + $closeSvg + '</i>' : '';
  181. var $span = document.createElement("span");
  182. if (oMsg.settings.html) {
  183. $span.innerHTML = content;
  184. } else {
  185. $span.innerText = content;
  186. }
  187. $elem.innerHTML = '<div class="qmsg-content">\
  188. <div class="' + contentClassName + '">\
  189. <i class="qmsg-icon">' + $svg + '</i>' + $span.outerHTML + $closeIcon +
  190. '</div>\
  191. </div>';
  192.  
  193. $elem.classList.add(namespacify('item'));
  194. $elem.style.textAlign = oMsg.settings.position;
  195. var $wrapper = document.querySelector('.' + NAMESPACE);
  196. if (!$wrapper) {
  197. $wrapper = document.createElement("div");
  198. // $wrapper.classList.add(NAMESPACE, namespacify('wrapper'), namespacify('is-initialized'));
  199. $wrapper.classList.add(NAMESPACE);
  200. $wrapper.classList.add(namespacify('wrapper'));
  201. $wrapper.classList.add(namespacify('is-initialized'));
  202. document.body.appendChild($wrapper);
  203. }
  204. $wrapper.appendChild($elem);
  205. oMsg.$wrapper = $wrapper;
  206. oMsg.$elem = $elem;
  207. setState(oMsg, 'opening');
  208. if (oMsg.settings.showClose) { // 关闭按钮绑定点击事件
  209. $elem.querySelector(".qmsg-icon-close").addEventListener('click', function () {
  210. oMsg.close();
  211. }.bind($elem));
  212. }
  213. $elem.addEventListener("animationend", function (e) { // 监听动画完成
  214. var target = e.target, animationName = e.animationName;
  215. if (animationName === STATES['closing']) {
  216. clearInterval(this.timer);
  217. this.destroy();
  218. }
  219. target.style.animationName = '';
  220. }.bind(oMsg));
  221. if (oMsg.settings.autoClose) { // 自动关闭
  222. var intvMs = 10; // 定时器频率
  223. oMsg.timer = setInterval(function () {
  224. this.timeout -= intvMs;
  225. if (this.timeout <= 0) {
  226. clearInterval(this.timer)
  227. this.close();
  228. }
  229. }.bind(oMsg), intvMs);
  230. oMsg.$elem.addEventListener('mouseover', function () {
  231. clearInterval(this.timer)
  232. }.bind(oMsg));
  233. oMsg.$elem.addEventListener('mouseout', function () {
  234. if (this.state !== 'closing') { // 状态为关闭则不重启定时器
  235. this.timer = setInterval(function () {
  236. this.timeout -= intvMs;
  237. if (this.timeout <= 0) {
  238. clearInterval(this.timer);
  239. this.close();
  240. }
  241. }.bind(oMsg), intvMs);
  242. }
  243. }.bind(oMsg));
  244. }
  245. }
  246.  
  247. function setState(inst, state) {
  248. if (!state || !STATES[state]) return;
  249. inst.state = state;
  250. inst.$elem.style.animationName = STATES[state];
  251. }
  252.  
  253. /**
  254. * 直接销毁元素,不会触发关闭回调函数
  255. */
  256. Msg.prototype.destroy = function () {
  257. this.$elem.parentNode && this.$elem.parentNode.removeChild(this.$elem);
  258. clearInterval(this.timer);
  259. Qmsg.remove(this.id);
  260. }
  261. /**
  262. * 关闭,支持动画则会触发动画事件
  263. */
  264. Msg.prototype.close = function () {
  265. setState(this, 'closing');
  266. if (!CAN_ANIMATION) { // 不支持动画
  267. this.destroy();
  268. } else {
  269. Qmsg.remove(this.id);
  270. }
  271. var callback = this.settings.onClose;
  272. if (callback && callback instanceof Function) {
  273. callback.call(this);
  274. }
  275. }
  276.  
  277. /**
  278. * 设置消息数量统计
  279. * @private
  280. */
  281. function setMsgCount(oMsg) {
  282. var countClassName = namespacify('count');
  283. var $content = oMsg.$elem.querySelector('[class^="qmsg-content-"]'),
  284. $count = $content.querySelector('.' + countClassName);
  285. if (!$count) {
  286. $count = document.createElement("span");
  287. $count.classList.add(countClassName);
  288. $content.appendChild($count);
  289. }
  290. $count.innerHTML = oMsg.count;
  291. $count.style.animationName = "";
  292. $count.style.animationName = "MessageShake";
  293. oMsg.timeout = oMsg.settings.timeout || DEFAULTS.timeout;
  294. }
  295.  
  296. /**
  297. * 合并参数为配置信息,用于创建Msg实例
  298. * @param {String} txt 文本内容
  299. * @param {Object} config 配置
  300. * @private
  301. */
  302. function mergeArgs(txt, config) {
  303. var opts = Object.assign({}, DEFAULTS);
  304. if (arguments.length === 0) {
  305. return opts;
  306. }
  307. if (txt instanceof Object) {
  308. return Object.assign(opts, txt);
  309. } else {
  310. opts.content = txt.toString();
  311. }
  312. if (config instanceof Object) {
  313. return Object.assign(opts, config)
  314. }
  315. return opts;
  316. }
  317.  
  318. /**
  319. * 通过配置信息 来判断是否为同一条消息,并返回消息实例
  320. * @param {Object} params 配置项
  321. * @private
  322. */
  323. function judgeReMsg(params) {
  324. params = params || {};
  325. var opt = JSON.stringify(params)
  326. var oInx = -1;
  327. var oMsg;
  328. for (var i in this.oMsgs) {
  329. var oMsgItem = this.oMsgs[i];
  330. if (oMsgItem.config === opt) {
  331. oInx = i;
  332. oMsg = oMsgItem.inst;
  333. break;
  334. }
  335. }
  336. if (oInx < 0) {
  337. this.instanceCount++;
  338. var oItem = {};
  339. oItem.id = this.instanceCount;
  340. oItem.config = opt;
  341. oMsg = new Msg(params);
  342. oMsg.id = this.instanceCount;
  343. oMsg.count = '';
  344. oItem.inst = oMsg;
  345. this.oMsgs[this.instanceCount] = oItem;
  346. var len = this.oMsgs.length;
  347. var maxNums = this.maxNums;
  348. /**
  349. * 关闭多余的消息
  350. */
  351. if (len > maxNums) {
  352. var oIndex = 0;
  353. var oMsgs = this.oMsgs;
  354. for (oIndex; oIndex < len - maxNums; oIndex++) {
  355. oMsgs[oIndex] && oMsgs[oIndex].inst.settings.autoClose && oMsgs[oIndex].inst.close();
  356. }
  357. }
  358. } else {
  359. oMsg.count = !oMsg.count ? 2 : oMsg.count >= 99 ? oMsg.count : oMsg.count + 1;
  360. setMsgCount(oMsg);
  361. }
  362. oMsg.$elem.setAttribute("data-count", oMsg.count);
  363. return oMsg;
  364. }
  365.  
  366.  
  367. var Qmsg = {
  368. version: '0.0.1',
  369. instanceCount: 0,
  370. oMsgs: [],
  371. maxNums: DEFAULTS.maxNums || 5,
  372. config: function (cfg) {
  373. DEFAULTS = cfg && cfg instanceof Object ? Object.assign(DEFAULTS, cfg) : DEFAULTS;
  374. this.maxNums = DEFAULTS.maxNums && DEFAULTS.maxNums > 0 ? parseInt(DEFAULTS.maxNums) : 3;
  375. },
  376. info: function (txt, config) {
  377. var params = mergeArgs(txt, config);
  378. params.type = 'info';
  379. return judgeReMsg.call(this, params);
  380. },
  381. warning: function (txt, config) {
  382. var params = mergeArgs(txt, config);
  383. params.type = 'warning';
  384. return judgeReMsg.call(this, params);
  385. },
  386. success: function (txt, config) {
  387. var params = mergeArgs(txt, config);
  388. params.type = 'success';
  389. return judgeReMsg.call(this, params);
  390. },
  391. error: function (txt, config) {
  392. var params = mergeArgs(txt, config);
  393. params.type = 'error';
  394. return judgeReMsg.call(this, params);
  395. },
  396. loading: function (txt, config) {
  397. var params = mergeArgs(txt, config);
  398. params.type = 'loading';
  399. params.autoClose = false;
  400. return judgeReMsg.call(this, params);
  401. },
  402. remove: function (id) {
  403. this.oMsgs[id] && delete this.oMsgs[id];
  404. },
  405. closeAll: function () {
  406. for (var i in this.oMsgs) {
  407. this.oMsgs[i] && this.oMsgs[i].inst.close();
  408. }
  409. }
  410. }
  411.  
  412. return Qmsg;
  413. });