forked from bilive/bilive_client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.ts
329 lines (329 loc) · 11.6 KB
/
script.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
const options = new Options()
let optionsInfo: optionsInfo
const dDiv = <HTMLDivElement>document.querySelector('#ddd')
const loginDiv = <HTMLDivElement>document.querySelector('#login')
const optionDiv = <HTMLDivElement>document.querySelector('#option')
const configDiv = <HTMLDivElement>document.querySelector('#config')
const userDiv = <HTMLDivElement>document.querySelector('#user')
const logDiv = <HTMLDivElement>document.querySelector('#log')
const returnButton = <HTMLElement>document.querySelector('#logreturn')
const modalDiv = <HTMLDivElement>document.querySelector('.modal')
const template = <HTMLDivElement>document.querySelector('#template')
// 3D效果
let firstDiv: HTMLDivElement = loginDiv
let secondDiv: HTMLDivElement
const dddArray = ['top', 'bottom', 'left', 'right']
let dddString: string
function getRandomIntInclusive(min: number, max: number) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
}
function danimation(toDiv: HTMLDivElement) {
dddString = dddArray[getRandomIntInclusive(0, 3)]
if (firstDiv === logDiv) returnButton.classList.add('d-none')
secondDiv = toDiv
secondDiv.classList.add(`d_${dddString}2`)
secondDiv.classList.remove('d-none')
firstDiv.classList.add(`d_${dddString}1`)
dDiv.className = `ddd_${dddString}`
}
dDiv.addEventListener('animationend', () => {
dDiv.className = ''
firstDiv.classList.remove(`d_${dddString}1`)
firstDiv.classList.add('d-none')
secondDiv.classList.remove(`d_${dddString}2`)
firstDiv = secondDiv
if (firstDiv === logDiv) returnButton.classList.remove('d-none')
})
/**
* 显示登录界面
*
*/
function showLogin() {
const pathInput = <HTMLInputElement>loginDiv.querySelector('#path input')
const protocolInput = <HTMLInputElement>loginDiv.querySelector('#protocol input[type="text"]')
const connectButton = <HTMLElement>loginDiv.querySelector('#connect button')
const connectSpan = <HTMLSpanElement>loginDiv.querySelector('#connect span')
if (location.hash !== '') {
const loginInfo = location.hash.match(/path=(.*)&protocol=(.*)/)
if (loginInfo !== null) {
pathInput.value = loginInfo[1]
protocolInput.value = loginInfo[2]
}
}
connectButton.onclick = async () => {
const protocols = [protocolInput.value]
const connected = await options.connect(pathInput.value, protocols)
if (connected) login()
else connectSpan.innerText = '连接失败'
}
loginDiv.classList.remove('d-none')
}
/**
* 登录成功
*
*/
async function login() {
const infoMSG = await options.getInfo()
optionsInfo = infoMSG.data
// 处理错误信息
options.onerror = (event) => {
modal({ body: event.data })
}
options.onwserror = () => wsClose('连接发生错误')
options.onwsclose = (event) => {
try {
const msg: message = JSON.parse(event.reason)
wsClose('连接已关闭 ' + msg.msg)
} catch (error) {
wsClose('连接已关闭')
}
}
danimation(optionDiv)
await showConfig()
await showUser()
showLog()
}
/**
* 加载全局设置
*
*/
async function showConfig() {
const saveConfigButton = <HTMLElement>document.querySelector('#saveConfig')
const addUserButton = <HTMLElement>document.querySelector('#addUser')
const showLogButton = <HTMLElement>document.querySelector('#showLog')
const configMSG = await options.getConfig()
let config = configMSG.data
const configDF = getConfigTemplate(config)
// 保存全局设置
saveConfigButton.onclick = async () => {
modal()
const configMSG = await options.setConfig(config)
if (configMSG.msg != null) modal({ body: configMSG.msg })
else {
config = configMSG.data
const configDF = getConfigTemplate(config)
configDiv.innerText = ''
configDiv.appendChild(configDF)
modal({ body: '保存成功' })
}
}
// 添加新用户
addUserButton.onclick = async () => {
modal()
const userDataMSG = await options.newUserData()
const uid = userDataMSG.uid
const userData = userDataMSG.data
const userDF = getUserDF(uid, userData)
userDiv.appendChild(userDF)
modal({ body: '添加成功' })
}
// 显示日志
showLogButton.onclick = () => {
danimation(logDiv)
}
configDiv.appendChild(configDF)
}
/**
* 加载Log
*
*/
async function showLog() {
const logMSG = await options.getLog()
const logs = logMSG.data
const logDF = document.createDocumentFragment()
logs.forEach(log => {
const div = document.createElement('div')
div.innerHTML = log.replace(/房间 (\d+) /, '房间 <a href="https://live.bilibili.com/$1" target="_blank" rel="noreferrer">$1</a> ')
logDF.appendChild(div)
})
options.onlog = data => {
const div = document.createElement('div')
div.innerHTML = data.replace(/房间 (\d+) /, '房间 <a href="https://live.bilibili.com/$1" target="_blank" rel="noreferrer">$1</a> ')
logDiv.appendChild(div)
if (logDiv.scrollHeight - logDiv.clientHeight - logDiv.scrollTop < 2 * div.offsetHeight) logDiv.scrollTop = logDiv.scrollHeight
}
returnButton.onclick = () => {
danimation(optionDiv)
}
logDiv.appendChild(logDF)
}
/**
* 加载用户设置
*
*/
async function showUser() {
const userMSG = await options.getAllUID()
const uidArray = userMSG.data
const df = document.createDocumentFragment()
for (const uid of uidArray) {
const userDataMSG = await options.getUserData(uid)
const userData = userDataMSG.data
const userDF = getUserDF(uid, userData)
df.appendChild(userDF)
}
userDiv.appendChild(df)
}
/**
* 新建用户模板
*
* @param {string} uid
* @param {userData} userData
* @returns {DocumentFragment}
*/
function getUserDF(uid: string, userData: userData): DocumentFragment {
const userTemplate = <HTMLTemplateElement>template.querySelector('#userTemplate')
const clone = document.importNode(userTemplate.content, true)
const userDataDiv = <HTMLDivElement>clone.querySelector('.userData')
const userConfigDiv = <HTMLDivElement>clone.querySelector('.userConfig')
const saveUserButton = <HTMLElement>clone.querySelector('.saveUser')
const deleteUserButton = <HTMLElement>clone.querySelector('.deleteUser')
const userConfigDF = getConfigTemplate(userData)
userConfigDiv.appendChild(userConfigDF)
// 保存用户设置
let captcha: string | undefined = undefined
saveUserButton.onclick = async () => {
modal()
const userDataMSG = await options.setUserData(uid, userData, captcha)
captcha = undefined
if (userDataMSG.msg == null) {
modal({ body: '保存成功' })
userData = userDataMSG.data
const userConfigDF = getConfigTemplate(userData)
userConfigDiv.innerText = ''
userConfigDiv.appendChild(userConfigDF)
}
else if (userDataMSG.msg === 'captcha' && userDataMSG.captcha != null) {
const captchaTemplate = <HTMLTemplateElement>template.querySelector('#captchaTemplate')
const clone = document.importNode(captchaTemplate.content, true)
const captchaImg = <HTMLImageElement>clone.querySelector('img')
const captchaInput = <HTMLInputElement>clone.querySelector('input')
captchaImg.src = userDataMSG.captcha
modal({
body: clone,
showOK: true,
onOK: () => {
captcha = captchaInput.value
saveUserButton.click()
}
})
}
else modal({ body: userDataMSG.msg })
}
// 删除用户设置
deleteUserButton.onclick = async () => {
modal()
const userDataMSG = await options.delUserData(uid)
if (userDataMSG.msg != null) modal({ body: userDataMSG.msg })
else {
modal({ body: '删除成功' })
userDataDiv.remove()
}
}
return clone
}
/**
* 设置模板
*
* @param {(config | userData)} config
* @returns {DocumentFragment}
*/
function getConfigTemplate(config: config | userData): DocumentFragment {
const df = document.createDocumentFragment()
for (const key in config) {
const info = optionsInfo[key]
if (info == null) continue
const configValue = config[key]
let configTemplate: HTMLTemplateElement
if (info.type === 'boolean') configTemplate = <HTMLTemplateElement>template.querySelector('#configCheckboxTemplate')
else configTemplate = <HTMLTemplateElement>template.querySelector('#configTextTemplate')
const clone = document.importNode(configTemplate.content, true)
const descriptionDiv = <HTMLDivElement>clone.querySelector('._description')
const inputInput = <HTMLInputElement>clone.querySelector('.form-control')
const checkboxInput = <HTMLInputElement>clone.querySelector('.form-check-input')
switch (info.type) {
case 'number':
inputInput.value = (<number>configValue).toString()
inputInput.oninput = () => config[key] = parseInt(inputInput.value)
break
case 'numberArray':
inputInput.value = (<number[]>configValue).join(',')
inputInput.oninput = () => config[key] = inputInput.value.split(',').map(value => parseInt(value))
break
case 'string':
inputInput.value = <string>configValue
inputInput.oninput = () => config[key] = inputInput.value
break
case 'stringArray':
inputInput.value = (<string[]>configValue).join(',')
inputInput.oninput = () => config[key] = inputInput.value.split(',')
break
case 'boolean':
checkboxInput.checked = <boolean>configValue
checkboxInput.onchange = () => config[key] = checkboxInput.checked
break
default:
break
}
descriptionDiv.innerText = info.description
descriptionDiv.title = info.tip
$(descriptionDiv).tooltip()
df.appendChild(clone)
}
return df
}
/**
* 处理连接中断
*
* @param {string} data
*/
function wsClose(data: string) {
const connectSpan = <HTMLSpanElement>loginDiv.querySelector('#connect span')
configDiv.innerText = ''
logDiv.innerText = ''
userDiv.innerText = ''
connectSpan.innerText = data
danimation(loginDiv)
}
/**
* 弹窗提示
* 无参数时只显示遮罩
*
* @param {modalOPtions} [options]
*/
function modal(options?: modalOPtions) {
if (options != null) {
const modalDialogDiv = <HTMLDivElement>modalDiv.querySelector('.modal-dialog')
const modalTemplate = <HTMLTemplateElement>template.querySelector('#modalContentTemplate')
const clone = document.importNode(modalTemplate.content, true)
const headerTitle = <HTMLHeadingElement>clone.querySelector('.modal-header .modal-title')
const headerClose = <HTMLElement>clone.querySelector('.modal-header .close')
const modalBody = <HTMLDivElement>clone.querySelector('.modal-body')
const footerClose = <HTMLElement>clone.querySelector('.modal-footer .btn-secondary')
const footerOK = <HTMLElement>clone.querySelector('.modal-footer .btn-primary')
headerClose.onclick = footerClose.onclick = () => {
$(modalDiv).one('hidden.bs.modal', () => {
modalDialogDiv.innerText = ''
if (typeof options.onClose === 'function') options.onClose(options.body)
})
$(modalDiv).modal('hide')
}
footerOK.onclick = () => {
$(modalDiv).one('hidden.bs.modal', () => {
modalDialogDiv.innerText = ''
if (typeof options.onOK === 'function') options.onOK(options.body)
})
$(modalDiv).modal('hide')
}
if (options.body instanceof DocumentFragment) modalBody.appendChild(options.body)
else modalBody.innerText = options.body
if (options.title != null) headerTitle.innerText = options.title
if (options.close != null) footerClose.innerText = options.close
if (options.ok != null) footerOK.innerText = options.ok
if (options.showOK) footerOK.classList.remove('d-none')
modalDialogDiv.appendChild(clone)
}
$(modalDiv).modal({ backdrop: 'static', keyboard: false })
}
showLogin()