UNPKG

@wcjiang/notify

Version:

JS achieve the browser title flashing , scrolling, voice prompts , chrome notice.

339 lines (324 loc) 9.26 kB
/**! * @wcjiang/notify v2.1.4 * JS achieve the browser title flashing , scrolling, voice prompts , chrome notice. * * Copyright (c) 2025 kenny wang * https://github.com/jaywcjlove/iNotify.git * * @website: http://jaywcjlove.github.io/iNotify * Licensed under the MIT license */ 'use strict'; // 提醒是否添加chrome通知 if (window.Notification && window.Notification.permission !== 'granted') { window.Notification.requestPermission().then((permission) => { if (permission === 'denied') { console.log("Permission wasn't granted. Allow a retry."); return; } if (permission === 'default') { console.log('The permission request was dismissed.'); return; } }); } let iconURL = ''; const repeatableEffects = ['flash', 'scroll']; const defaultNotification = { title: 'iNotify !', body: 'You have a new message.', openurl: '', }; function jsonArguments(news, olds) { for (const a in olds) { if (news[a]) { olds[a] = news[a]; } } return olds; } function isArray(value) { return Object.prototype.toString.call(value) === '[object Array]'; } function createAudio(url) { const audioElm = document.createElement('audio'); audioElm.autoplay = true; audioElm.muted = true; let source; if (isArray(url) && url.length > 0) { for (let i = 0; i < url.length; i++) { source = document.createElement('source'); source.src = url[i]; source.type = `audio/${getExtension(url[i])}`; audioElm.appendChild(source); } } else { audioElm.src = url; } return audioElm; } function getFavicon(setting) { let ic = document.querySelectorAll('link[rel~=shortcut]')[0]; if (!ic) { ic = changeFavicon('O', setting); } return ic; } function getExtension(fileName) { return fileName.match(/\.([^\\.]+)$/)[1]; } function changeFavicon(num, settings) { const canvas = document.createElement('canvas'); const head = document.getElementsByTagName('head')[0]; const linkTag = document.createElement('link'); let ctx = null; canvas.height = 32; canvas.width = 32; ctx = canvas.getContext('2d'); ctx.fillStyle = settings.backgroundColor; ctx.fillRect(0, 0, 32, 32); ctx.textAlign = 'center'; ctx.font = '22px "helvetica", sans-serif'; ctx.fillStyle = settings.textColor; num && ctx.fillText(num, 16, 24); // 生成到 linkTag.setAttribute('rel', 'shortcut icon'); linkTag.setAttribute('type', 'image/x-icon'); linkTag.setAttribute('id', `new${settings.id}`); linkTag.setAttribute('href', canvas.toDataURL('image/png')); iconURL = canvas.toDataURL('image/png'); return head.appendChild(linkTag); } function Notify(config) { if (config) { this.init(config); } } Notify.prototype = { init(config) { if (!config) { config = {}; } this.interval = config.interval || 100; // 响应时长 this.effect = config.effect || 'flash'; // 效果 this.title = config.title || document.title; // 标题 this.message = config.message || this.title; // 原来的标题 this.onclick = config.onclick || this.onclick; // 点击事件 this.openurl = config.openurl || this.openurl; // 点击事件 this.disableFavicon = config.disableFavicon || false; // 禁用favicon 默认开启 this.updateFavicon = !this.disableFavicon && (config.updateFavicon || { id: 'favicon', textColor: '#fff', backgroundColor: '#2F9A00', }); this.audio = config.audio || ''; this.favicon = !this.disableFavicon && getFavicon(this.updateFavicon); this.cloneFavicon = this.favicon && this.favicon.cloneNode(true); iconURL = config.notification && config.notification.icon ? config.notification.icon : config.icon ? config.icon : this.favicon.href; defaultNotification.icon = iconURL; this.notification = config.notification || defaultNotification; // 初始化生成声音文件节点 if (this.audio && this.audio.file) { this.setURL(this.audio.file); } return this; }, render() { if (this.effect === 'flash') { document.title = this.title === document.title ? this.message : this.title; } else if (this.effect === 'scroll') { const title = this.message || document.title; if (!this.scrollTitle || !this.scrollTitle.slice(1)) { document.title = title; this.scrollTitle = title; } else { this.scrollTitle = this.scrollTitle.slice(1); document.title = this.scrollTitle; } } return this; }, // 设置标题 setTitle(str) { if (str === true) { if (repeatableEffects.indexOf(this.effect) >= 0) { return this.addTimer(); } } else if (str) { this.message = str; this.scrollTitle = ''; this.addTimer(); } else { this.clearTimer(); } return this; }, setURL(url) { if (url) { if (this.audioElm) { this.audioElm.remove(); } this.audioElm = createAudio(url); document.body.appendChild(this.audioElm); } return this; }, loopPlay() { this.setURL(); this.audioElm.loop = true; this.player(); return this; }, stopPlay() { this.audioElm && ((this.audioElm.loop = false), this.audioElm.pause()); return this; }, // 播放声音 player() { if (!this.audio || !this.audio.file) { return; } if (!this.audioElm) { this.audioElm = createAudio(this.audio.file); document.body.appendChild(this.audioElm); } this.audioElm.muted = false; const resp = this.audioElm.play(); if (resp !== undefined) { resp.then(() => { // autoplay starts! }).catch(() => { // show error }); } return this; }, notify(json = {}) { let nt = this.notification; const url = json.openurl ? json.openurl : this.openurl; const onclick = json.onclick ? json.onclick : this.onclick; if (window.Notification) { if (json) { nt = jsonArguments(json, nt); } else { nt = defaultNotification; } const option = {}; option.icon = json.icon ? json.icon : iconURL; option.body = nt.body || json.body; if (json.dir) option.dir = json.dir; const n = new Notification(nt.title || json.title, option); n.onclick = () => { onclick && typeof onclick === 'function' && onclick(n); url && window.open(url); }; n.onshow = () => { json.onshow && typeof json.onshow === 'function' && json.onshow(n); }; n.onclose = () => { json.onclose && typeof json.onclose === 'function' && json.onclose(n); }; n.onerror = () => { json.onerror && typeof json.onerror === 'function' && json.onerror(n); }; this.Notifiy = n; } return this; }, // 是否许可弹框通知 isPermission() { return window.Notification && Notification.permission === 'granted'; }, // 设置时间间隔 setInterval(num) { if (num) { this.interval = num; this.addTimer(); } return this; }, // 设置网页Icon setFavicon(num) { if (!num && num !== 0) { return this.faviconClear(); } const oldicon = document.getElementById(`new${this.updateFavicon.id}`); if (this.favicon) { this.favicon.remove(); } if (oldicon) { oldicon.remove(); } this.updateFavicon.num = num; changeFavicon(num, this.updateFavicon); return this; }, // 设置 Favicon 文字颜色 setFaviconColor(color) { if (color) { this.faviconRemove(); this.updateFavicon.textColor = color; changeFavicon(this.updateFavicon.num, this.updateFavicon); } return this; }, // 设置 Favicon 背景颜色 setFaviconBackgroundColor(color) { if (color) { this.faviconRemove(); this.updateFavicon.backgroundColor = color; changeFavicon(this.updateFavicon.num, this.updateFavicon); } return this; }, faviconRemove() { this.faviconClear(); const oldicon = document.getElementById(`new${this.updateFavicon.id}`); if (this.favicon) { this.favicon.remove(); } if (oldicon) { oldicon.remove(); } }, // 添加计数器 addTimer() { this.clearTimer(); if (repeatableEffects.indexOf(this.effect) >= 0) { this.timer = setInterval(this.render.bind(this), this.interval); } return this; }, close() { if (this.Notifiy) this.Notifiy.close(); }, // 清除Icon faviconClear() { const newicon = document.getElementById(`new${this.updateFavicon.id}`); const head = document.getElementsByTagName('head')[0]; const ficon = document.querySelectorAll('link[rel~=shortcut]'); newicon && newicon.remove(); if (ficon.length > 0) { for (let i = 0; i < ficon.length; i++) { ficon[i].remove(); } } head.appendChild(this.cloneFavicon); iconURL = this.cloneFavicon.href; this.favicon = this.cloneFavicon; return this; }, // 清除计数器 clearTimer() { this.timer && clearInterval(this.timer); document.title = this.title; return this; }, }; module.exports = Notify; //# sourceMappingURL=notify.common.js.map