User:Irukaza/common.js:修订间差异
跳到导航
跳到搜索
imported>=海豚= 无编辑摘要 |
imported>=海豚= 无编辑摘要 |
||
(未显示同一用户的63个中间版本) | |||
第1行: | 第1行: | ||
$(() => { | |||
const notificationIcon = 'https://img.moegirl.org.cn/common/thumb/f/f6/%E8%93%9D%E8%90%8C%E5%AD%97.png/233px-%E8%93%9D%E8%90%8C%E5%AD%97.png' | |||
const workerUrl = window.mw ? | |||
'/index.php?title=User:東東君/js/notification.js&action=raw&ctype=text/javascript' : | |||
'notification.js' | |||
checkNotificationPermission() | |||
main() | |||
async function main() { | |||
const worker = await registerService() | |||
const configListener = new WorkerDataListener(worker, 'config') | |||
const uiController = initUI({ onSaveConfig }) | |||
configListener.pullData() | |||
configListener.addListener(data => { | |||
uiController.updateConfig(data) | |||
}) | |||
function onSaveConfig(config) { | |||
worker.postMessage({ type: 'saveConfig', data: { config } }) | |||
alert('配置已保存') | |||
} | |||
} | |||
navigator.serviceWorker.addEventListener('message', e => { | |||
const { type, data } = e.data | |||
if (type === 'sendNotification') { | |||
const [title, options] = data | |||
const link = options.data.link | |||
new Notification(title, options).onclick = () => window.open(link, '_blank') | |||
} | |||
}) | |||
function initUI({ | |||
onSaveConfig | |||
}) { | |||
const template = ` | |||
<div class="widget-notification-config" style="display:none;"> | |||
<div class="body"> | |||
<fieldset> | |||
<legend>设置</legend> | |||
<form class="settings" action="javascript:void(0)"> | |||
<label> | |||
<span>开启</span> | |||
<input type="checkbox" name="enable"> | |||
</label> | |||
<label> | |||
<div>排除 条目 :</div> | |||
<textarea placeholder="当其中 的 条目发生变化时不会通知 , 使 用 换行分割" name="excludeList"></textarea> | |||
</label> | |||
<div class="buttonGroup"> | |||
<button class="saveConfig"> 保存</button> | |||
<button class="closeModal"> 关闭</button> | |||
</div> | |||
</form> | |||
</fieldset> | |||
</div> | |||
</div> | |||
` | |||
const cssStyle = ` | |||
<style> | |||
.widget-notification-config { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
background-color: rgba(0, 0, 0, 0.2); | |||
font-size: 14px; | |||
z-index: 100; | |||
} | } | ||
$(" | |||
.widget-notification-config > .body { | |||
background-color: white; | |||
width: 300px; | |||
box-sizing: border-box; | |||
padding: 10px; | |||
border-radius: 5px; | |||
border: 2px #ccc ridge; | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
transform: translate(-50%, -50%); | |||
} | |||
.widget-notification-config > .body fieldset { | |||
margin: 0; | |||
padding-left: 15px; | |||
padding-right: 15px; | |||
} | |||
.widget-notification-config > .body .settings > label { | |||
display: block; | |||
} | |||
.widget-notification-config > .body .settings > label > * { | |||
vertical-align: middle; | |||
} | |||
.widget-notification-config > .body .settings textarea[name="excludeList"] { | |||
width: -webkit-fill-available; | |||
resize: none; | |||
margin-top: 5px; | |||
height: 100px; | |||
border: 1px #666 solid; | |||
} | |||
.widget-notification-config > .body .settings .buttonGroup { | |||
margin: 0 auto; | |||
margin-top: 10px; | |||
display: table; | |||
} | |||
.widget-notification-config > .body .settings .buttonGroup button { | |||
margin: 0 5px; | |||
} | |||
</style> | |||
` | |||
$('body').append(template).append(cssStyle) | |||
const rootEl = $('.widget-notification-config') | |||
const settingsEl = rootEl.find('.settings') | |||
// 注入按钮 | |||
$('#p-cactions ul').append('<li id="widget-notification-showConfig"><a title="实时通知">实时通知</a></li>') | |||
// modal显示事件 | |||
$('#widget-notification-showConfig').on('click', () => rootEl.show()) | |||
// modal关闭事件 | |||
rootEl.find('.closeModal').on('click', () => rootEl.hide()) | |||
// 保存配置 | |||
rootEl.find('.saveConfig').on('click', e => { | |||
const formValues = { | |||
enable: settingsEl.find('[name="enable"]').prop('checked'), | |||
listenNotification: settingsEl.find('[name="listenNotification"]').prop('checked'), | |||
excludeList: settingsEl.find('[name="excludeList"]').val().trim().split('\n') | |||
} | |||
onSaveConfig(formValues) | |||
}) | |||
function updateConfig({ enable, listenNotification, excludeList }) { | |||
settingsEl.find('[name="enable"]').prop('checked', enable) | |||
settingsEl.find('[name="listenNotification"]').prop('checked', listenNotification) | |||
settingsEl.find('[name="excludeList"]').val(excludeList.join('\n')) | |||
} | |||
return { | |||
updateConfig | |||
} | |||
} | |||
function registerService() { | |||
return new Promise(async (resolve, reject) => { | |||
if (Notification === undefined || navigator.serviceWorker === undefined) { | |||
alert('当前浏览器不支持通知插件,请从您的用户页中移除!') | |||
return reject() | |||
} | |||
if(Notification.permission === 'default') { | |||
alert('点击确定关闭这条消息后,将弹出授予通知权限的窗口,届时请点击“允许”,以保证插件的正常运行。') | |||
} | |||
await new Promise(Notification.requestPermission) | |||
const status = Notification.permission | |||
if (status === 'granted') { | |||
if (localStorage.getItem('moegirl-widget-notification-permission-granted') !== 'true') { | |||
localStorage.setItem('moegirl-widget-notification-permission-granted', 'true') | |||
new Notification('欢迎', { | |||
body: '您已经成功开启实时通知功能,欢迎使用!', | |||
icon: notificationIcon | |||
}) | |||
} | |||
} | |||
if (status === 'denied') { | |||
alert('您未能正确授予通知权限,若要继续使用,请在url栏左侧点击小锁标志开启权限,若无法设置,请从你的用户页删去此插件。') | |||
return reject() | |||
} | |||
const serviceWorkerRegistration = await navigator.serviceWorker.register(workerUrl) | |||
await navigator.serviceWorker.ready | |||
resolve(serviceWorkerRegistration.active) | |||
}) | |||
} | |||
function checkNotificationPermission() { | |||
if (localStorage.getItem('moegirl-widget-notification-permission-granted') === 'true' && Notification.permission !== 'granted') { | |||
localStorage.removeItem('moegirl-widget-notification-permission-granted') | |||
} | |||
} | |||
class WorkerDataListener { | |||
broadcastName = '' | |||
worker = null | |||
data = null | |||
#incrementPullId = 0 | |||
get pullEventName() { return 'get-' + this.broadcastName } | |||
get broadcastChannelName() { return 'channel-' + this.broadcastName } | |||
constructor(worker, broadcastName) { | |||
this.broadcastName = broadcastName | |||
this.worker = worker | |||
this.broadcastChannel = new BroadcastChannel(this.broadcastChannelName) | |||
this.addListener(data => this.data = data) | |||
} | |||
addListener(handler) { | |||
const usingHandler = e => { | |||
const { type, data, id } = e.data | |||
type === 'broadcast' && handler(data, id) | |||
} | |||
this.broadcastChannel.addEventListener('message', usingHandler) | |||
return () => this.broadcastChannel.removeEventListener('message', usingHandler) | |||
} | |||
pullData() { | |||
const pullId = this.#incrementPullId++ | |||
this.worker.postMessage({ type: this.pullEventName, data: { id: pullId } }) | |||
return pullId | |||
} | |||
getData() { | |||
return new Promise(resolve => { | |||
const pullId = this.pullData() | |||
const removeListener = this.addListener((data, id) => { | |||
if (id !== pullId) { return } | |||
resolve(data) | |||
removeListener() | |||
}) | |||
}) | |||
} | |||
} | |||
}) |
2021年8月7日 (六) 11:26的版本
$(() => {
const notificationIcon = 'https://img.moegirl.org.cn/common/thumb/f/f6/%E8%93%9D%E8%90%8C%E5%AD%97.png/233px-%E8%93%9D%E8%90%8C%E5%AD%97.png'
const workerUrl = window.mw ?
'/index.php?title=User:東東君/js/notification.js&action=raw&ctype=text/javascript' :
'notification.js'
checkNotificationPermission()
main()
async function main() {
const worker = await registerService()
const configListener = new WorkerDataListener(worker, 'config')
const uiController = initUI({ onSaveConfig })
configListener.pullData()
configListener.addListener(data => {
uiController.updateConfig(data)
})
function onSaveConfig(config) {
worker.postMessage({ type: 'saveConfig', data: { config } })
alert('配置已保存')
}
}
navigator.serviceWorker.addEventListener('message', e => {
const { type, data } = e.data
if (type === 'sendNotification') {
const [title, options] = data
const link = options.data.link
new Notification(title, options).onclick = () => window.open(link, '_blank')
}
})
function initUI({
onSaveConfig
}) {
const template = `
<div class="widget-notification-config" style="display:none;">
<div class="body">
<fieldset>
<legend>设置</legend>
<form class="settings" action="javascript:void(0)">
<label>
<span>开启</span>
<input type="checkbox" name="enable">
</label>
<label>
<div>排除条目:</div>
<textarea placeholder="当其中的条目发生变化时不会通知,使用换行分割" name="excludeList"></textarea>
</label>
<div class="buttonGroup">
<button class="saveConfig">保存</button>
<button class="closeModal">关闭</button>
</div>
</form>
</fieldset>
</div>
</div>
`
const cssStyle = `
<style>
.widget-notification-config {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.2);
font-size: 14px;
z-index: 100;
}
.widget-notification-config > .body {
background-color: white;
width: 300px;
box-sizing: border-box;
padding: 10px;
border-radius: 5px;
border: 2px #ccc ridge;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.widget-notification-config > .body fieldset {
margin: 0;
padding-left: 15px;
padding-right: 15px;
}
.widget-notification-config > .body .settings > label {
display: block;
}
.widget-notification-config > .body .settings > label > * {
vertical-align: middle;
}
.widget-notification-config > .body .settings textarea[name="excludeList"] {
width: -webkit-fill-available;
resize: none;
margin-top: 5px;
height: 100px;
border: 1px #666 solid;
}
.widget-notification-config > .body .settings .buttonGroup {
margin: 0 auto;
margin-top: 10px;
display: table;
}
.widget-notification-config > .body .settings .buttonGroup button {
margin: 0 5px;
}
</style>
`
$('body').append(template).append(cssStyle)
const rootEl = $('.widget-notification-config')
const settingsEl = rootEl.find('.settings')
// 注入按钮
$('#p-cactions ul').append('<li id="widget-notification-showConfig"><a title="实时通知">实时通知</a></li>')
// modal显示事件
$('#widget-notification-showConfig').on('click', () => rootEl.show())
// modal关闭事件
rootEl.find('.closeModal').on('click', () => rootEl.hide())
// 保存配置
rootEl.find('.saveConfig').on('click', e => {
const formValues = {
enable: settingsEl.find('[name="enable"]').prop('checked'),
listenNotification: settingsEl.find('[name="listenNotification"]').prop('checked'),
excludeList: settingsEl.find('[name="excludeList"]').val().trim().split('\n')
}
onSaveConfig(formValues)
})
function updateConfig({ enable, listenNotification, excludeList }) {
settingsEl.find('[name="enable"]').prop('checked', enable)
settingsEl.find('[name="listenNotification"]').prop('checked', listenNotification)
settingsEl.find('[name="excludeList"]').val(excludeList.join('\n'))
}
return {
updateConfig
}
}
function registerService() {
return new Promise(async (resolve, reject) => {
if (Notification === undefined || navigator.serviceWorker === undefined) {
alert('当前浏览器不支持通知插件,请从您的用户页中移除!')
return reject()
}
if(Notification.permission === 'default') {
alert('点击确定关闭这条消息后,将弹出授予通知权限的窗口,届时请点击“允许”,以保证插件的正常运行。')
}
await new Promise(Notification.requestPermission)
const status = Notification.permission
if (status === 'granted') {
if (localStorage.getItem('moegirl-widget-notification-permission-granted') !== 'true') {
localStorage.setItem('moegirl-widget-notification-permission-granted', 'true')
new Notification('欢迎', {
body: '您已经成功开启实时通知功能,欢迎使用!',
icon: notificationIcon
})
}
}
if (status === 'denied') {
alert('您未能正确授予通知权限,若要继续使用,请在url栏左侧点击小锁标志开启权限,若无法设置,请从你的用户页删去此插件。')
return reject()
}
const serviceWorkerRegistration = await navigator.serviceWorker.register(workerUrl)
await navigator.serviceWorker.ready
resolve(serviceWorkerRegistration.active)
})
}
function checkNotificationPermission() {
if (localStorage.getItem('moegirl-widget-notification-permission-granted') === 'true' && Notification.permission !== 'granted') {
localStorage.removeItem('moegirl-widget-notification-permission-granted')
}
}
class WorkerDataListener {
broadcastName = ''
worker = null
data = null
#incrementPullId = 0
get pullEventName() { return 'get-' + this.broadcastName }
get broadcastChannelName() { return 'channel-' + this.broadcastName }
constructor(worker, broadcastName) {
this.broadcastName = broadcastName
this.worker = worker
this.broadcastChannel = new BroadcastChannel(this.broadcastChannelName)
this.addListener(data => this.data = data)
}
addListener(handler) {
const usingHandler = e => {
const { type, data, id } = e.data
type === 'broadcast' && handler(data, id)
}
this.broadcastChannel.addEventListener('message', usingHandler)
return () => this.broadcastChannel.removeEventListener('message', usingHandler)
}
pullData() {
const pullId = this.#incrementPullId++
this.worker.postMessage({ type: this.pullEventName, data: { id: pullId } })
return pullId
}
getData() {
return new Promise(resolve => {
const pullId = this.pullData()
const removeListener = this.addListener((data, id) => {
if (id !== pullId) { return }
resolve(data)
removeListener()
})
})
}
}
})