@wcjiang/notify
Version:
JS achieve the browser title flashing , scrolling, voice prompts , chrome notice.
339 lines (324 loc) • 9.26 kB
JavaScript
/**!
* @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