mirror of
https://github.com/wbbaddons/Tims-Chat.git
synced 2024-12-20 21:20:08 +00:00
Run prettier on all files
This commit is contained in:
parent
cfe91be22d
commit
3a88e73ee1
@ -11,54 +11,85 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Chat/console'
|
define([
|
||||||
, 'Bastelstu.be/bottle'
|
'./Chat/console',
|
||||||
, 'Bastelstu.be/_Push'
|
'Bastelstu.be/bottle',
|
||||||
, 'WoltLabSuite/Core/Core'
|
'Bastelstu.be/_Push',
|
||||||
, 'WoltLabSuite/Core/Language'
|
'WoltLabSuite/Core/Core',
|
||||||
, 'WoltLabSuite/Core/Timer/Repeating'
|
'WoltLabSuite/Core/Language',
|
||||||
, 'WoltLabSuite/Core/User'
|
'WoltLabSuite/Core/Timer/Repeating',
|
||||||
, './Chat/Autocompleter'
|
'WoltLabSuite/Core/User',
|
||||||
, './Chat/CommandHandler'
|
'./Chat/Autocompleter',
|
||||||
, './Chat/DataStructure/Throttle'
|
'./Chat/CommandHandler',
|
||||||
, './Chat/Message'
|
'./Chat/DataStructure/Throttle',
|
||||||
, './Chat/Messenger'
|
'./Chat/Message',
|
||||||
, './Chat/ParseError'
|
'./Chat/Messenger',
|
||||||
, './Chat/ProfileStore'
|
'./Chat/ParseError',
|
||||||
, './Chat/Room'
|
'./Chat/ProfileStore',
|
||||||
, './Chat/Template'
|
'./Chat/Room',
|
||||||
, './Chat/Ui/Attachment/Upload'
|
'./Chat/Template',
|
||||||
, './Chat/Ui/AutoAway'
|
'./Chat/Ui/Attachment/Upload',
|
||||||
, './Chat/Ui/Chat'
|
'./Chat/Ui/AutoAway',
|
||||||
, './Chat/Ui/ConnectionWarning'
|
'./Chat/Ui/Chat',
|
||||||
, './Chat/Ui/ErrorDialog'
|
'./Chat/Ui/ConnectionWarning',
|
||||||
, './Chat/Ui/Input'
|
'./Chat/Ui/ErrorDialog',
|
||||||
, './Chat/Ui/Input/Autocompleter'
|
'./Chat/Ui/Input',
|
||||||
, './Chat/Ui/MessageStream'
|
'./Chat/Ui/Input/Autocompleter',
|
||||||
, './Chat/Ui/MessageActions/Delete'
|
'./Chat/Ui/MessageStream',
|
||||||
, './Chat/Ui/Mobile'
|
'./Chat/Ui/MessageActions/Delete',
|
||||||
, './Chat/Ui/Notification'
|
'./Chat/Ui/Mobile',
|
||||||
, './Chat/Ui/ReadMarker'
|
'./Chat/Ui/Notification',
|
||||||
, './Chat/Ui/Settings'
|
'./Chat/Ui/ReadMarker',
|
||||||
, './Chat/Ui/Topic'
|
'./Chat/Ui/Settings',
|
||||||
, './Chat/Ui/UserActionDropdownHandler'
|
'./Chat/Ui/Topic',
|
||||||
, './Chat/Ui/UserList'
|
'./Chat/Ui/UserActionDropdownHandler',
|
||||||
], function (console, Bottle, Push, Core, Language, RepeatingTimer, CoreUser, Autocompleter,
|
'./Chat/Ui/UserList',
|
||||||
CommandHandler, Throttle, Message, Messenger, ParseError, ProfileStore, Room, Template, UiAttachmentUpload, UiAutoAway, Ui,
|
], function (
|
||||||
UiConnectionWarning, ErrorDialog, UiInput, UiInputAutocompleter, UiMessageStream, UiMessageActionDelete, UiMobile, UiNotification,
|
console,
|
||||||
UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) {
|
Bottle,
|
||||||
"use strict";
|
Push,
|
||||||
|
Core,
|
||||||
|
Language,
|
||||||
|
RepeatingTimer,
|
||||||
|
CoreUser,
|
||||||
|
Autocompleter,
|
||||||
|
CommandHandler,
|
||||||
|
Throttle,
|
||||||
|
Message,
|
||||||
|
Messenger,
|
||||||
|
ParseError,
|
||||||
|
ProfileStore,
|
||||||
|
Room,
|
||||||
|
Template,
|
||||||
|
UiAttachmentUpload,
|
||||||
|
UiAutoAway,
|
||||||
|
Ui,
|
||||||
|
UiConnectionWarning,
|
||||||
|
ErrorDialog,
|
||||||
|
UiInput,
|
||||||
|
UiInputAutocompleter,
|
||||||
|
UiMessageStream,
|
||||||
|
UiMessageActionDelete,
|
||||||
|
UiMobile,
|
||||||
|
UiNotification,
|
||||||
|
UiReadMarker,
|
||||||
|
UiSettings,
|
||||||
|
UiTopic,
|
||||||
|
UiUserActionDropdownHandler,
|
||||||
|
UiUserList
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Chat {
|
class Chat {
|
||||||
constructor(roomID, config) {
|
constructor(roomID, config) {
|
||||||
console.debug('Chat.constructor', 'Constructing …')
|
console.debug('Chat.constructor', 'Constructing …')
|
||||||
|
|
||||||
this.config = config
|
this.config = config
|
||||||
|
|
||||||
this.sessionID = Core.getUuid()
|
this.sessionID = Core.getUuid()
|
||||||
|
|
||||||
// Setup Bottle containers
|
// Setup Bottle containers
|
||||||
this.bottle = new Bottle()
|
this.bottle = new Bottle()
|
||||||
this.bottle.value('bottle', this.bottle)
|
this.bottle.value('bottle', this.bottle)
|
||||||
this.bottle.value('config', config)
|
this.bottle.value('config', config)
|
||||||
this.bottle.constant('sessionID', this.sessionID)
|
this.bottle.constant('sessionID', this.sessionID)
|
||||||
@ -94,79 +125,105 @@ define([ './Chat/console'
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Register Templates
|
// Register Templates
|
||||||
const selector = [ '[type="x-text/template"]'
|
const selector = [
|
||||||
, '[data-application="be.bastelstu.chat"]'
|
'[type="x-text/template"]',
|
||||||
, '[data-template-name]'
|
'[data-application="be.bastelstu.chat"]',
|
||||||
].join('')
|
'[data-template-name]',
|
||||||
|
].join('')
|
||||||
|
|
||||||
const templates = elBySelAll(selector)
|
const templates = elBySelAll(selector)
|
||||||
|
|
||||||
Array.prototype.forEach.call(templates, (function (template) {
|
Array.prototype.forEach.call(
|
||||||
this.bottle.factory(`Template.${template.dataset.templateName}`, function (container) {
|
templates,
|
||||||
const includeNames = (template.dataset.templateIncludes || '').split(/ /).filter(item => item !== "")
|
function (template) {
|
||||||
const includes = { }
|
this.bottle.factory(
|
||||||
includeNames.forEach(item => includes[item] = container[item])
|
`Template.${template.dataset.templateName}`,
|
||||||
|
function (container) {
|
||||||
|
const includeNames = (template.dataset.templateIncludes || '')
|
||||||
|
.split(/ /)
|
||||||
|
.filter((item) => item !== '')
|
||||||
|
const includes = {}
|
||||||
|
includeNames.forEach((item) => (includes[item] = container[item]))
|
||||||
|
|
||||||
return new Template(template.textContent, includes)
|
return new Template(template.textContent, includes)
|
||||||
})
|
}
|
||||||
}).bind(this))
|
)
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
|
||||||
// Register MessageTypes
|
// Register MessageTypes
|
||||||
Object.entries(this.config.messageTypes)
|
Object.entries(this.config.messageTypes).forEach(
|
||||||
.forEach(([ objectType, messageType ]) => {
|
([objectType, messageType]) => {
|
||||||
const MessageType = require(messageType.module)
|
const MessageType = require(messageType.module)
|
||||||
|
|
||||||
this.bottle.factory(`MessageType.${objectType.replace(/\./g, '-')}`, _ => {
|
this.bottle.factory(
|
||||||
const deps = this.bottle.digest(MessageType.DEPENDENCIES || [])
|
`MessageType.${objectType.replace(/\./g, '-')}`,
|
||||||
|
(_) => {
|
||||||
|
const deps = this.bottle.digest(MessageType.DEPENDENCIES || [])
|
||||||
|
|
||||||
return new MessageType(...deps, objectType)
|
return new MessageType(...deps, objectType)
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Register Commands
|
// Register Commands
|
||||||
Object.values(this.config.commands).forEach(command => {
|
Object.values(this.config.commands).forEach((command) => {
|
||||||
const Command = require(command.module)
|
const Command = require(command.module)
|
||||||
|
|
||||||
this.bottle.factory(`Command.${command.package.replace(/\./g, '-')}:${command.identifier}`, _ => {
|
this.bottle.factory(
|
||||||
const deps = this.bottle.digest(Command.DEPENDENCIES || [])
|
`Command.${command.package.replace(/\./g, '-')}:${
|
||||||
|
command.identifier
|
||||||
|
}`,
|
||||||
|
(_) => {
|
||||||
|
const deps = this.bottle.digest(Command.DEPENDENCIES || [])
|
||||||
|
|
||||||
return new Command(...deps, command)
|
return new Command(...deps, command)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
this.bottle.constant('Trigger', new Map(Object.entries(this.config.triggers).map(([ trigger, commandID ]) => {
|
this.bottle.constant(
|
||||||
const command = this.config.commands[commandID]
|
'Trigger',
|
||||||
const key = [ command.package, command.identifier ]
|
new Map(
|
||||||
return [ trigger, key ]
|
Object.entries(this.config.triggers).map(([trigger, commandID]) => {
|
||||||
})))
|
const command = this.config.commands[commandID]
|
||||||
|
const key = [command.package, command.identifier]
|
||||||
|
return [trigger, key]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Register Settings
|
// Register Settings
|
||||||
Array.from(elBySelAll('#chatQuickSettingsNavigation .button[data-module]')).forEach(item => {
|
Array.from(
|
||||||
|
elBySelAll('#chatQuickSettingsNavigation .button[data-module]')
|
||||||
|
).forEach((item) => {
|
||||||
const Button = require(item.dataset.module)
|
const Button = require(item.dataset.module)
|
||||||
|
|
||||||
this.bottle.instanceFactory(`UiSettingsButton.${item.dataset.module.replace(/\./g, '-')}`, (_, element) => {
|
this.bottle.instanceFactory(
|
||||||
const deps = this.bottle.digest(Button.DEPENDENCIES || [])
|
`UiSettingsButton.${item.dataset.module.replace(/\./g, '-')}`,
|
||||||
return new Button(element, ...deps)
|
(_, element) => {
|
||||||
})
|
const deps = this.bottle.digest(Button.DEPENDENCIES || [])
|
||||||
|
return new Button(element, ...deps)
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.knows = { from: undefined
|
this.knows = { from: undefined, to: undefined }
|
||||||
, to: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processMessagesThrottled = Throttle(this.processMessages.bind(this))
|
this.processMessagesThrottled = Throttle(this.processMessages.bind(this))
|
||||||
this.queuedMessages = [ ]
|
this.queuedMessages = []
|
||||||
this.messageSinks = new Set()
|
this.messageSinks = new Set()
|
||||||
|
|
||||||
this.pullTimer = undefined
|
this.pullTimer = undefined
|
||||||
this.pullUserListTimer = undefined
|
this.pullUserListTimer = undefined
|
||||||
this.pushConnected = false
|
this.pushConnected = false
|
||||||
|
|
||||||
this.firstFailure = null
|
this.firstFailure = null
|
||||||
}
|
}
|
||||||
|
|
||||||
service(name, _constructor, args = [ ]) {
|
service(name, _constructor, args = []) {
|
||||||
this.bottle.factory(name, _ => {
|
this.bottle.factory(name, (_) => {
|
||||||
const deps = this.bottle.digest(_constructor.DEPENDENCIES || [ ])
|
const deps = this.bottle.digest(_constructor.DEPENDENCIES || [])
|
||||||
|
|
||||||
return new _constructor(...deps, ...args)
|
return new _constructor(...deps, ...args)
|
||||||
})
|
})
|
||||||
@ -187,42 +244,58 @@ define([ './Chat/console'
|
|||||||
await this.bottle.container.Room.join()
|
await this.bottle.container.Room.join()
|
||||||
|
|
||||||
// Bind unload event to leave the Chat
|
// Bind unload event to leave the Chat
|
||||||
window.addEventListener('unload', this.bottle.container.Room.leave.bind(this.bottle.container.Room, true))
|
window.addEventListener(
|
||||||
document.addEventListener('visibilitychange', _ => {
|
'unload',
|
||||||
|
this.bottle.container.Room.leave.bind(this.bottle.container.Room, true)
|
||||||
|
)
|
||||||
|
document.addEventListener('visibilitychange', (_) => {
|
||||||
this.processMessagesThrottled.setDelay(document.hidden ? 10000 : 125)
|
this.processMessagesThrottled.setDelay(document.hidden ? 10000 : 125)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.pullTimer = new RepeatingTimer(Throttle(this.pullMessages.bind(this)), this.config.reloadTime * 1e3)
|
this.pullTimer = new RepeatingTimer(
|
||||||
|
Throttle(this.pullMessages.bind(this)),
|
||||||
|
this.config.reloadTime * 1e3
|
||||||
|
)
|
||||||
|
|
||||||
Push.onConnect(_ => {
|
Push.onConnect((_) => {
|
||||||
console.debug('Chat.bootstrap', 'Push connected')
|
console.debug('Chat.bootstrap', 'Push connected')
|
||||||
this.pushConnected = true
|
this.pushConnected = true
|
||||||
this.pullTimer.setDelta(30e3)
|
this.pullTimer.setDelta(30e3)
|
||||||
|
}).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
})
|
})
|
||||||
.catch(error => { console.debug(error) })
|
|
||||||
|
|
||||||
Push.onDisconnect(_ => {
|
Push.onDisconnect((_) => {
|
||||||
console.debug('Chat.bootstrap', 'Push disconnected')
|
console.debug('Chat.bootstrap', 'Push disconnected')
|
||||||
this.pushConnected = false
|
this.pushConnected = false
|
||||||
this.pullTimer.setDelta(this.config.reloadTime * 1e3)
|
this.pullTimer.setDelta(this.config.reloadTime * 1e3)
|
||||||
|
}).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
})
|
})
|
||||||
.catch(error => { console.debug(error) })
|
|
||||||
|
|
||||||
Push.onMessage('be.bastelstu.chat.message', this.pullMessages.bind(this))
|
Push.onMessage(
|
||||||
.catch(error => { console.debug(error) })
|
'be.bastelstu.chat.message',
|
||||||
|
this.pullMessages.bind(this)
|
||||||
|
).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
|
})
|
||||||
|
|
||||||
// Fetch user list every 60 seconds
|
// Fetch user list every 60 seconds
|
||||||
// This acts as a safety net: It should be kept current by messages whenever possible.
|
// This acts as a safety net: It should be kept current by messages whenever possible.
|
||||||
this.pullUserListTimer = new RepeatingTimer(this.updateUsers.bind(this), 60e3)
|
this.pullUserListTimer = new RepeatingTimer(
|
||||||
|
this.updateUsers.bind(this),
|
||||||
|
60e3
|
||||||
|
)
|
||||||
|
|
||||||
this.registerMessageSink(this.bottle.container.UiMessageStream)
|
this.registerMessageSink(this.bottle.container.UiMessageStream)
|
||||||
this.registerMessageSink(this.bottle.container.UiNotification)
|
this.registerMessageSink(this.bottle.container.UiNotification)
|
||||||
this.registerMessageSink(this.bottle.container.UiAutoAway)
|
this.registerMessageSink(this.bottle.container.UiAutoAway)
|
||||||
|
|
||||||
await Promise.all([ this.pullMessages()
|
await Promise.all([
|
||||||
, this.updateUsers()
|
this.pullMessages(),
|
||||||
, this.bottle.container.ProfileStore.ensureUsersByIDs([ CoreUser.userId ])
|
this.updateUsers(),
|
||||||
])
|
this.bottle.container.ProfileStore.ensureUsersByIDs([CoreUser.userId]),
|
||||||
|
])
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@ -240,7 +313,11 @@ define([ './Chat/console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
hcf(err = undefined) {
|
hcf(err = undefined) {
|
||||||
console.debug('Chat.hcf', 'Gotcha! FIRE was caught! FIRE’s data was newly added to the POKéDEX.', err)
|
console.debug(
|
||||||
|
'Chat.hcf',
|
||||||
|
'Gotcha! FIRE was caught! FIRE’s data was newly added to the POKéDEX.',
|
||||||
|
err
|
||||||
|
)
|
||||||
|
|
||||||
this.pullTimer.stop()
|
this.pullTimer.stop()
|
||||||
this.pullUserListTimer.stop()
|
this.pullUserListTimer.stop()
|
||||||
@ -259,47 +336,64 @@ define([ './Chat/console'
|
|||||||
|
|
||||||
this.markAsBack()
|
this.markAsBack()
|
||||||
|
|
||||||
let [ trigger, parameterString ] = this.bottle.container.CommandHandler.splitCommand(value)
|
let [
|
||||||
|
trigger,
|
||||||
|
parameterString,
|
||||||
|
] = this.bottle.container.CommandHandler.splitCommand(value)
|
||||||
let command = null
|
let command = null
|
||||||
if (trigger === null) {
|
if (trigger === null) {
|
||||||
command = this.bottle.container.CommandHandler.getCommandByIdentifier('be.bastelstu.chat', 'plain')
|
command = this.bottle.container.CommandHandler.getCommandByIdentifier(
|
||||||
}
|
'be.bastelstu.chat',
|
||||||
else {
|
'plain'
|
||||||
command = this.bottle.container.CommandHandler.getCommandByTrigger(trigger)
|
)
|
||||||
|
} else {
|
||||||
|
command = this.bottle.container.CommandHandler.getCommandByTrigger(
|
||||||
|
trigger
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === null) {
|
if (command === null) {
|
||||||
this.ui.input.inputError(Language.get('chat.error.triggerNotFound', { trigger }))
|
this.ui.input.inputError(
|
||||||
|
Language.get('chat.error.triggerNotFound', { trigger })
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let parameters
|
let parameters
|
||||||
try {
|
try {
|
||||||
parameters = this.bottle.container.CommandHandler.applyCommand(command, parameterString)
|
parameters = this.bottle.container.CommandHandler.applyCommand(
|
||||||
}
|
command,
|
||||||
catch (e) {
|
parameterString
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
if (e instanceof ParseError) {
|
if (e instanceof ParseError) {
|
||||||
e = new Error(Language.get('chat.error.invalidParameters', { data: e.data }))
|
e = new Error(
|
||||||
|
Language.get('chat.error.invalidParameters', { data: e.data })
|
||||||
|
)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = { commandID: command.id
|
const payload = { commandID: command.id, parameters }
|
||||||
, parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.bottle.container.Messenger.push(payload)
|
await this.bottle.container.Messenger.push(payload)
|
||||||
this.ui.input.hideInputError()
|
this.ui.input.hideInputError()
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
let seriousError = true
|
let seriousError = true
|
||||||
if (error.returnValues && error.returnValues.fieldName === 'message' && (error.returnValues.realErrorMessage || error.returnValues.errorType)) {
|
if (
|
||||||
this.ui.input.inputError(error.returnValues.realErrorMessage || error.returnValues.errorType)
|
error.returnValues &&
|
||||||
|
error.returnValues.fieldName === 'message' &&
|
||||||
|
(error.returnValues.realErrorMessage ||
|
||||||
|
error.returnValues.errorType)
|
||||||
|
) {
|
||||||
|
this.ui.input.inputError(
|
||||||
|
error.returnValues.realErrorMessage ||
|
||||||
|
error.returnValues.errorType
|
||||||
|
)
|
||||||
seriousError = false
|
seriousError = false
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.ui.input.inputError(error.message)
|
this.ui.input.inputError(error.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +408,7 @@ define([ './Chat/console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.debug('Chat.onSubmit', `Done`)
|
console.debug('Chat.onSubmit', `Done`)
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
this.ui.input.inputError(e.message)
|
this.ui.input.inputError(e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,16 +418,23 @@ define([ './Chat/console'
|
|||||||
if (this.bottle.container.ProfileStore.getSelf().away == null) return
|
if (this.bottle.container.ProfileStore.getSelf().away == null) return
|
||||||
console.debug('Chat.markAsBack', `Marking as back`)
|
console.debug('Chat.markAsBack', `Marking as back`)
|
||||||
|
|
||||||
const command = this.bottle.container.CommandHandler.getCommandByIdentifier('be.bastelstu.chat', 'back')
|
const command = this.bottle.container.CommandHandler.getCommandByIdentifier(
|
||||||
return this.bottle.container.Messenger.push({ commandID: command.id, parameters: { } })
|
'be.bastelstu.chat',
|
||||||
}
|
'back'
|
||||||
catch (err) {
|
)
|
||||||
|
return this.bottle.container.Messenger.push({
|
||||||
|
commandID: command.id,
|
||||||
|
parameters: {},
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
console.error('Chat.markAsBack', err)
|
console.error('Chat.markAsBack', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onSendAttachment(event) {
|
async onSendAttachment(event) {
|
||||||
return this.bottle.container.Messenger.pushAttachment(event.detail.tmpHash)
|
return this.bottle.container.Messenger.pushAttachment(
|
||||||
|
event.detail.tmpHash
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onAutocomplete(event) {
|
onAutocomplete(event) {
|
||||||
@ -354,18 +454,23 @@ define([ './Chat/console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
async pullMessages() {
|
async pullMessages() {
|
||||||
console.debug('Chat.pullMessages', `Pulling new messages, starting at ${this.knows.to ? this.knows.to + 1 : ''}`)
|
console.debug(
|
||||||
|
'Chat.pullMessages',
|
||||||
|
`Pulling new messages, starting at ${
|
||||||
|
this.knows.to ? this.knows.to + 1 : ''
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
let payload
|
let payload
|
||||||
try {
|
try {
|
||||||
if (this.knows.to === undefined) {
|
if (this.knows.to === undefined) {
|
||||||
payload = await this.bottle.container.Messenger.pull()
|
payload = await this.bottle.container.Messenger.pull()
|
||||||
|
} else {
|
||||||
|
payload = await this.bottle.container.Messenger.pull(
|
||||||
|
this.knows.to + 1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else {
|
} catch (e) {
|
||||||
payload = await this.bottle.container.Messenger.pull(this.knows.to + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
this.handleError(e)
|
this.handleError(e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -386,12 +491,17 @@ define([ './Chat/console'
|
|||||||
|
|
||||||
if (this.knows.from !== undefined && this.knows.to !== undefined) {
|
if (this.knows.from !== undefined && this.knows.to !== undefined) {
|
||||||
messages = messages.filter((message) => {
|
messages = messages.filter((message) => {
|
||||||
return !(this.knows.from <= message.messageID && message.messageID <= this.knows.to)
|
return !(
|
||||||
|
this.knows.from <= message.messageID &&
|
||||||
|
message.messageID <= this.knows.to
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.knows.from === undefined || payload.from < this.knows.from) this.knows.from = payload.from
|
if (this.knows.from === undefined || payload.from < this.knows.from)
|
||||||
if (this.knows.to === undefined || payload.to > this.knows.to) this.knows.to = payload.to
|
this.knows.from = payload.from
|
||||||
|
if (this.knows.to === undefined || payload.to > this.knows.to)
|
||||||
|
this.knows.to = payload.to
|
||||||
|
|
||||||
this.queuedMessages.push(messages)
|
this.queuedMessages.push(messages)
|
||||||
const end = (performance ? performance : Date).now()
|
const end = (performance ? performance : Date).now()
|
||||||
@ -402,14 +512,17 @@ define([ './Chat/console'
|
|||||||
|
|
||||||
handleError(error) {
|
handleError(error) {
|
||||||
if (this.firstFailure === null) {
|
if (this.firstFailure === null) {
|
||||||
console.error('Chat.handleError', `Request failed, 30 seconds until shutdown`)
|
console.error(
|
||||||
|
'Chat.handleError',
|
||||||
|
`Request failed, 30 seconds until shutdown`
|
||||||
|
)
|
||||||
this.firstFailure = Date.now()
|
this.firstFailure = Date.now()
|
||||||
this.ui.connectionWarning.show()
|
this.ui.connectionWarning.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debugException(error)
|
console.debugException(error)
|
||||||
|
|
||||||
if ((Date.now() - this.firstFailure) >= 30e3) {
|
if (Date.now() - this.firstFailure >= 30e3) {
|
||||||
console.error('Chat.handleError', ' Failures for 30 seconds, aborting')
|
console.error('Chat.handleError', ' Failures for 30 seconds, aborting')
|
||||||
|
|
||||||
this.hcf(error)
|
this.hcf(error)
|
||||||
@ -419,16 +532,18 @@ define([ './Chat/console'
|
|||||||
async processMessages() {
|
async processMessages() {
|
||||||
console.debug('Chat.processMessages', 'Processing messages')
|
console.debug('Chat.processMessages', 'Processing messages')
|
||||||
const start = (performance ? performance : Date).now()
|
const start = (performance ? performance : Date).now()
|
||||||
const messages = [ ].concat(...this.queuedMessages)
|
const messages = [].concat(...this.queuedMessages)
|
||||||
this.queuedMessages = []
|
this.queuedMessages = []
|
||||||
|
|
||||||
if (messages.length === 0) return
|
if (messages.length === 0) return
|
||||||
|
|
||||||
await Promise.all(messages.map(async (message) => {
|
await Promise.all(
|
||||||
this.bottle.container.ProfileStore.pushLastActivity(message.userID)
|
messages.map(async (message) => {
|
||||||
|
this.bottle.container.ProfileStore.pushLastActivity(message.userID)
|
||||||
|
|
||||||
return message.getMessageType().preProcess(message)
|
return message.getMessageType().preProcess(message)
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const updateUserList = messages.some((message) => {
|
const updateUserList = messages.some((message) => {
|
||||||
return message.getMessageType().shouldUpdateUserList(message)
|
return message.getMessageType().shouldUpdateUserList(message)
|
||||||
@ -438,13 +553,19 @@ define([ './Chat/console'
|
|||||||
this.updateUsers()
|
this.updateUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.bottle.container.ProfileStore.ensureUsersByIDs([ ].concat(...messages.map(message => message.getMessageType().getReferencedUsers(message))))
|
await this.bottle.container.ProfileStore.ensureUsersByIDs(
|
||||||
|
[].concat(
|
||||||
|
...messages.map((message) =>
|
||||||
|
message.getMessageType().getReferencedUsers(message)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
messages.forEach((message) => {
|
messages.forEach((message) => {
|
||||||
message.getMessageType().preRender(message)
|
message.getMessageType().preRender(message)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.messageSinks.forEach(sink => sink.ingest(messages))
|
this.messageSinks.forEach((sink) => sink.ingest(messages))
|
||||||
const end = (performance ? performance : Date).now()
|
const end = (performance ? performance : Date).now()
|
||||||
console.debug('Chat.processMessages', `took ${(end - start) / 1000}s`)
|
console.debug('Chat.processMessages', `took ${(end - start) / 1000}s`)
|
||||||
}
|
}
|
||||||
@ -453,10 +574,12 @@ define([ './Chat/console'
|
|||||||
console.debug('Chat.updateUsers')
|
console.debug('Chat.updateUsers')
|
||||||
|
|
||||||
const users = await this.bottle.container.Room.getUsers()
|
const users = await this.bottle.container.Room.getUsers()
|
||||||
await this.bottle.container.ProfileStore.ensureUsersByIDs(users.map(user => user.userID))
|
await this.bottle.container.ProfileStore.ensureUsersByIDs(
|
||||||
|
users.map((user) => user.userID)
|
||||||
|
)
|
||||||
this.ui.userList.render(users)
|
this.ui.userList.render(users)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Chat
|
return Chat
|
||||||
});
|
})
|
||||||
|
@ -11,35 +11,42 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './CommandHandler'
|
define(['./CommandHandler', './Parser'], function (CommandHandler, Parser) {
|
||||||
, './Parser'
|
'use strict'
|
||||||
], function (CommandHandler, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'CommandHandler' ]
|
const DEPENDENCIES = ['CommandHandler']
|
||||||
class Autocompleter {
|
class Autocompleter {
|
||||||
constructor(commandHandler) {
|
constructor(commandHandler) {
|
||||||
if (!(commandHandler instanceof CommandHandler)) throw new TypeError('You must pass a CommandHandler to the Autocompleter')
|
if (!(commandHandler instanceof CommandHandler))
|
||||||
|
throw new TypeError(
|
||||||
|
'You must pass a CommandHandler to the Autocompleter'
|
||||||
|
)
|
||||||
|
|
||||||
this.commandHandler = commandHandler
|
this.commandHandler = commandHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(text) {
|
*autocomplete(text) {
|
||||||
if (text === '/') {
|
if (text === '/') {
|
||||||
yield * this.autocompleteCommandTrigger(text, '')
|
yield* this.autocompleteCommandTrigger(text, '')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ trigger, parameterString ] = this.commandHandler.splitCommand(text)
|
const [trigger, parameterString] = this.commandHandler.splitCommand(text)
|
||||||
|
|
||||||
let command
|
let command
|
||||||
if (trigger === null) {
|
if (trigger === null) {
|
||||||
command = this.commandHandler.getCommandByIdentifier('be.bastelstu.chat', 'plain')
|
command = this.commandHandler.getCommandByIdentifier(
|
||||||
}
|
'be.bastelstu.chat',
|
||||||
else {
|
'plain'
|
||||||
const triggerDone = Parser.Slash.thenRight(Parser.AlnumTrigger.or(Parser.SymbolicTrigger).thenLeft(Parser.Whitespace)).parse(Parser.Streams.ofString(text))
|
)
|
||||||
|
} else {
|
||||||
|
const triggerDone = Parser.Slash.thenRight(
|
||||||
|
Parser.AlnumTrigger.or(Parser.SymbolicTrigger).thenLeft(
|
||||||
|
Parser.Whitespace
|
||||||
|
)
|
||||||
|
).parse(Parser.Streams.ofString(text))
|
||||||
if (!triggerDone.isAccepted()) {
|
if (!triggerDone.isAccepted()) {
|
||||||
yield * this.autocompleteCommandTrigger(text, trigger)
|
yield* this.autocompleteCommandTrigger(text, trigger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,13 +63,12 @@ define([ './CommandHandler'
|
|||||||
for (const item of values) {
|
for (const item of values) {
|
||||||
yield `/${trigger} ${item}`
|
yield `/${trigger} ${item}`
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
yield* values
|
||||||
yield * values
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocompleteCommandTrigger(text, prefix) {
|
*autocompleteCommandTrigger(text, prefix) {
|
||||||
const triggers = Array.from(this.commandHandler.getTriggers())
|
const triggers = Array.from(this.commandHandler.getTriggers())
|
||||||
|
|
||||||
triggers.sort()
|
triggers.sort()
|
||||||
@ -70,7 +76,8 @@ define([ './CommandHandler'
|
|||||||
for (const trigger of triggers) {
|
for (const trigger of triggers) {
|
||||||
if (trigger === '') continue
|
if (trigger === '') continue
|
||||||
if (!trigger.startsWith(prefix)) continue
|
if (!trigger.startsWith(prefix)) continue
|
||||||
if (!this.commandHandler.getCommandByTrigger(trigger).isAvailable) continue
|
if (!this.commandHandler.getCommandByTrigger(trigger).isAvailable)
|
||||||
|
continue
|
||||||
|
|
||||||
yield `/${trigger} `
|
yield `/${trigger} `
|
||||||
}
|
}
|
||||||
@ -79,4 +86,4 @@ define([ './CommandHandler'
|
|||||||
Autocompleter.DEPENDENCIES = DEPENDENCIES
|
Autocompleter.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Autocompleter
|
return Autocompleter
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './console'
|
define([
|
||||||
, 'Bastelstu.be/_Push'
|
'./console',
|
||||||
, 'WoltLabSuite/Core/Dom/Util'
|
'Bastelstu.be/_Push',
|
||||||
, 'WoltLabSuite/Core/Timer/Repeating'
|
'WoltLabSuite/Core/Dom/Util',
|
||||||
, 'Bastelstu.be/PromiseWrap/Ajax'
|
'WoltLabSuite/Core/Timer/Repeating',
|
||||||
], function (console, Push, DomUtil, RepeatingTimer, Ajax) {
|
'Bastelstu.be/PromiseWrap/Ajax',
|
||||||
"use strict";
|
], function (console, Push, DomUtil, RepeatingTimer, Ajax) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
let timer = undefined
|
let timer = undefined
|
||||||
const mapping = new Map()
|
const mapping = new Map()
|
||||||
@ -29,26 +30,44 @@ define([ './console'
|
|||||||
mapping.set(container, this)
|
mapping.set(container, this)
|
||||||
|
|
||||||
if (timer == null) {
|
if (timer == null) {
|
||||||
timer = new RepeatingTimer(BoxRoomList.updateBoxes.bind(BoxRoomList), 60e3)
|
timer = new RepeatingTimer(
|
||||||
|
BoxRoomList.updateBoxes.bind(BoxRoomList),
|
||||||
|
60e3
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Push.onConnect(timer.setDelta.bind(timer, 300e3)).catch(error => { console.debug(error) })
|
Push.onConnect(timer.setDelta.bind(timer, 300e3)).catch((error) => {
|
||||||
Push.onDisconnect(timer.setDelta.bind(timer, 60e3)).catch(error => { console.debug(error) })
|
console.debug(error)
|
||||||
Push.onMessage('be.bastelstu.chat.join', BoxRoomList.updateBoxes.bind(BoxRoomList)).catch(error => { console.debug(error) })
|
})
|
||||||
Push.onMessage('be.bastelstu.chat.leave', BoxRoomList.updateBoxes.bind(BoxRoomList)).catch(error => { console.debug(error) })
|
Push.onDisconnect(timer.setDelta.bind(timer, 60e3)).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
|
})
|
||||||
|
Push.onMessage(
|
||||||
|
'be.bastelstu.chat.join',
|
||||||
|
BoxRoomList.updateBoxes.bind(BoxRoomList)
|
||||||
|
).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
|
})
|
||||||
|
Push.onMessage(
|
||||||
|
'be.bastelstu.chat.leave',
|
||||||
|
BoxRoomList.updateBoxes.bind(BoxRoomList)
|
||||||
|
).catch((error) => {
|
||||||
|
console.debug(error)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateBoxes() {
|
static updateBoxes() {
|
||||||
mapping.forEach(object => {
|
mapping.forEach((object) => {
|
||||||
object.update()
|
object.update()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async update() {
|
async update() {
|
||||||
const payload = { className: 'chat\\data\\room\\RoomAction'
|
const payload = {
|
||||||
, actionName: 'getBoxRoomList'
|
className: 'chat\\data\\room\\RoomAction',
|
||||||
, parameters: { }
|
actionName: 'getBoxRoomList',
|
||||||
}
|
parameters: {},
|
||||||
|
}
|
||||||
|
|
||||||
payload.parameters.activeRoomID = this.container.dataset.activeRoomId
|
payload.parameters.activeRoomID = this.container.dataset.activeRoomId
|
||||||
payload.parameters.boxID = this.container.dataset.boxId
|
payload.parameters.boxID = this.container.dataset.boxId
|
||||||
@ -59,9 +78,12 @@ define([ './console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
replace(data) {
|
replace(data) {
|
||||||
if (data.returnValues.template == null) throw new Error('template could not be found in returnValues')
|
if (data.returnValues.template == null)
|
||||||
|
throw new Error('template could not be found in returnValues')
|
||||||
|
|
||||||
const fragment = DomUtil.createFragmentFromHtml(data.returnValues.template)
|
const fragment = DomUtil.createFragmentFromHtml(
|
||||||
|
data.returnValues.template
|
||||||
|
)
|
||||||
const oldRoomList = this.container.querySelector('.chatBoxRoomList')
|
const oldRoomList = this.container.querySelector('.chatBoxRoomList')
|
||||||
const newRoomList = fragment.querySelector('.chatBoxRoomList')
|
const newRoomList = fragment.querySelector('.chatBoxRoomList')
|
||||||
|
|
||||||
@ -69,7 +91,9 @@ define([ './console'
|
|||||||
throw new Error('.chatBoxRoomList could not be found in container')
|
throw new Error('.chatBoxRoomList could not be found in container')
|
||||||
}
|
}
|
||||||
if (newRoomList == null) {
|
if (newRoomList == null) {
|
||||||
throw new Error('.chatBoxRoomList could not be found in returned template')
|
throw new Error(
|
||||||
|
'.chatBoxRoomList could not be found in returned template'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldRoomList.dataset.hash !== newRoomList.dataset.hash) {
|
if (oldRoomList.dataset.hash !== newRoomList.dataset.hash) {
|
||||||
@ -78,11 +102,9 @@ define([ './console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return { silent: true, ignoreError: true }
|
||||||
, ignoreError: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return BoxRoomList
|
return BoxRoomList
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Parser' ], function (Parser) {
|
define(['./Parser'], function (Parser) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const data = Symbol('data')
|
const data = Symbol('data')
|
||||||
|
|
||||||
@ -28,9 +28,7 @@ define([ './Parser' ], function (Parser) {
|
|||||||
return Parser.Rest
|
return Parser.Rest
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this[data].commandID
|
return this[data].commandID
|
||||||
@ -54,4 +52,4 @@ define([ './Parser' ], function (Parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Command
|
return Command
|
||||||
});
|
})
|
||||||
|
@ -11,16 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Away extends Command {
|
class Away extends Command {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
return Parser.Rest.map(reason => ({ reason }))
|
return Parser.Rest.map((reason) => ({ reason }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Away
|
return Away
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Back extends Command {
|
class Back extends Command {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
@ -23,4 +21,4 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Back
|
return Back
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './_Suspension' ], function (Suspension) {
|
define(['./_Suspension'], function (Suspension) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Ban extends Suspension {
|
class Ban extends Suspension {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ban
|
return Ban
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain' ], function (Plain) {
|
define(['./Plain'], function (Plain) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Broadcast extends Plain {
|
class Broadcast extends Plain {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Broadcast
|
return Broadcast
|
||||||
});
|
})
|
||||||
|
@ -11,21 +11,22 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Color extends Command {
|
class Color extends Command {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
// Either match a color in hexadecimal RGB notation or a color name (just letters)
|
// Either match a color in hexadecimal RGB notation or a color name (just letters)
|
||||||
const color = Parser.F.try(Parser.RGBHex.map(color => ({ type: 'hex', value: color })))
|
const color = Parser.F.try(
|
||||||
.or(new Parser.X().word().map(word => ({ type: 'word', value: word })))
|
Parser.RGBHex.map((color) => ({ type: 'hex', value: color }))
|
||||||
|
).or(new Parser.X().word().map((word) => ({ type: 'word', value: word })))
|
||||||
|
|
||||||
// Either match a single color or two colors separated by a space
|
// Either match a single color or two colors separated by a space
|
||||||
return Parser.F.try(color.then(Parser.C.char(' ').thenRight(color))).or(color.map(item => [ item ]))
|
return Parser.F.try(color.then(Parser.C.char(' ').thenRight(color))).or(
|
||||||
|
color.map((item) => [item])
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Color
|
return Color
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore' ]
|
const DEPENDENCIES = ['ProfileStore']
|
||||||
class Info extends Command {
|
class Info extends Command {
|
||||||
constructor(profileStore, id) {
|
constructor(profileStore, id) {
|
||||||
super(id)
|
super(id)
|
||||||
@ -24,10 +22,10 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
return Parser.Username.map(username => ({ username }))
|
return Parser.Username.map((username) => ({ username }))
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
for (const userID of this.profileStore.getLastActivity()) {
|
for (const userID of this.profileStore.getLastActivity()) {
|
||||||
const user = this.profileStore.get(userID)
|
const user = this.profileStore.get(userID)
|
||||||
if (!user.username.startsWith(parameterString)) continue
|
if (!user.username.startsWith(parameterString)) continue
|
||||||
@ -39,4 +37,4 @@ define([ '../Command'
|
|||||||
Info.DEPENDENCIES = DEPENDENCIES
|
Info.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Info
|
return Info
|
||||||
});
|
})
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain', '../Parser' ], function (Plain, Parser) {
|
define(['./Plain', '../Parser'], function (Plain, Parser) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Me extends Plain {
|
class Me extends Plain {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
return Parser.Rest1.map(text => ({ text }))
|
return Parser.Rest1.map((text) => ({ text }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Me
|
return Me
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './_Suspension' ], function (Suspension) {
|
define(['./_Suspension'], function (Suspension) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Mute extends Suspension {
|
class Mute extends Suspension {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Mute
|
return Mute
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser', 'WoltLabSuite/Core/StringUtil'], function (
|
||||||
, '../Parser'
|
Command,
|
||||||
, 'WoltLabSuite/Core/StringUtil'
|
Parser,
|
||||||
], function (Command, Parser, StringUtil) {
|
StringUtil
|
||||||
"use strict";
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore' ]
|
const DEPENDENCIES = ['ProfileStore']
|
||||||
class Plain extends Command {
|
class Plain extends Command {
|
||||||
constructor(profileStore, id) {
|
constructor(profileStore, id) {
|
||||||
super(id)
|
super(id)
|
||||||
@ -25,12 +26,12 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
return Parser.Rest1
|
return Parser.Rest1.map(
|
||||||
.map(StringUtil.escapeHTML.bind(StringUtil))
|
StringUtil.escapeHTML.bind(StringUtil)
|
||||||
.map(text => ({ text }))
|
).map((text) => ({ text }))
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
const parts = parameterString.split(/ /)
|
const parts = parameterString.split(/ /)
|
||||||
const lastWord = parts.pop().toLowerCase()
|
const lastWord = parts.pop().toLowerCase()
|
||||||
|
|
||||||
@ -41,13 +42,21 @@ define([ '../Command'
|
|||||||
for (const userID of this.profileStore.getLastActivity()) {
|
for (const userID of this.profileStore.getLastActivity()) {
|
||||||
const user = this.profileStore.get(userID)
|
const user = this.profileStore.get(userID)
|
||||||
const username = user.username.toLowerCase()
|
const username = user.username.toLowerCase()
|
||||||
if (!username.startsWith(parameterString) && !username.startsWith(lastWord.replace(/^@/, ''))) continue
|
if (
|
||||||
|
!username.startsWith(parameterString) &&
|
||||||
|
!username.startsWith(lastWord.replace(/^@/, ''))
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
yield `${parts.concat([ lastWord.startsWith('@') ? `@${user.username}` : user.username ]).join(' ')} `
|
yield `${parts
|
||||||
|
.concat([
|
||||||
|
lastWord.startsWith('@') ? `@${user.username}` : user.username,
|
||||||
|
])
|
||||||
|
.join(' ')} `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Plain.DEPENDENCIES = DEPENDENCIES
|
Plain.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Plain
|
return Plain
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain' ], function (Plain) {
|
define(['./Plain'], function (Plain) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Team extends Plain {
|
class Team extends Plain {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Team
|
return Team
|
||||||
});
|
})
|
||||||
|
@ -11,39 +11,44 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Temproom extends Command {
|
class Temproom extends Command {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
const Create = Parser.C.string('create').thenReturns({ type: 'create' })
|
const Create = Parser.C.string('create').thenReturns({ type: 'create' })
|
||||||
const Invite = Parser.C.string('invite').thenLeft(Parser.Whitespace.rep()).thenRight(Parser.Username).map((username) => {
|
const Invite = Parser.C.string('invite')
|
||||||
return { type: 'invite'
|
.thenLeft(Parser.Whitespace.rep())
|
||||||
, username
|
.thenRight(Parser.Username)
|
||||||
}
|
.map((username) => {
|
||||||
})
|
return { type: 'invite', username }
|
||||||
|
})
|
||||||
const Delete = Parser.C.string('delete').thenReturns({ type: 'delete' })
|
const Delete = Parser.C.string('delete').thenReturns({ type: 'delete' })
|
||||||
|
|
||||||
return Create.or(Invite).or(Delete)
|
return Create.or(Invite).or(Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
const Create = Parser.C.string('create')
|
const Create = Parser.C.string('create')
|
||||||
const Invite = Parser.C.string('invite')
|
const Invite = Parser.C.string('invite')
|
||||||
const Delete = Parser.C.string('delete')
|
const Delete = Parser.C.string('delete')
|
||||||
|
|
||||||
const subcommandDone = Create.or(Invite).or(Delete).thenLeft(Parser.Whitespace)
|
const subcommandDone = Create.or(Invite)
|
||||||
|
.or(Delete)
|
||||||
|
.thenLeft(Parser.Whitespace)
|
||||||
|
|
||||||
const subcommandCheck = subcommandDone.parse(Parser.Streams.ofString(parameterString))
|
const subcommandCheck = subcommandDone.parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
if (subcommandCheck.isAccepted()) {
|
if (subcommandCheck.isAccepted()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
yield * [ 'create', 'invite ', 'delete' ].filter(item => item.startsWith(parameterString))
|
yield* ['create', 'invite ', 'delete'].filter((item) =>
|
||||||
|
item.startsWith(parameterString)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Temproom
|
return Temproom
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './_Unsuspension' ], function (Unsuspension) {
|
define(['./_Unsuspension'], function (Unsuspension) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Unban extends Unsuspension {
|
class Unban extends Unsuspension {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Unban
|
return Unban
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './_Unsuspension' ], function (Unsuspension) {
|
define(['./_Unsuspension'], function (Unsuspension) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Unmute extends Unsuspension {
|
class Unmute extends Unsuspension {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Unmute
|
return Unmute
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Where extends Command {
|
class Where extends Command {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
@ -23,4 +21,4 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Where
|
return Where
|
||||||
});
|
})
|
||||||
|
@ -11,25 +11,27 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Parser'
|
define(['../Parser', './Plain'], function (Parser, Plain) {
|
||||||
, './Plain'
|
'use strict'
|
||||||
], function (Parser, Plain) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class Whisper extends Plain {
|
class Whisper extends Plain {
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
return Parser.Username.thenLeft(Parser.Whitespace.rep()).then(super.getParameterParser()).map(([ username, object ]) => {
|
return Parser.Username.thenLeft(Parser.Whitespace.rep())
|
||||||
object.username = username
|
.then(super.getParameterParser())
|
||||||
|
.map(([username, object]) => {
|
||||||
|
object.username = username
|
||||||
|
|
||||||
return object
|
return object
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
const usernameDone = Parser.Username.thenLeft(Parser.Whitespace).parse(Parser.Streams.ofString(parameterString))
|
const usernameDone = Parser.Username.thenLeft(Parser.Whitespace).parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
|
|
||||||
if (usernameDone.isAccepted()) {
|
if (usernameDone.isAccepted()) {
|
||||||
yield * super.autocomplete(parameterString)
|
yield* super.autocomplete(parameterString)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,4 +45,4 @@ define([ '../Parser'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Whisper
|
return Whisper
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore' ]
|
const DEPENDENCIES = ['ProfileStore']
|
||||||
class Suspension extends Command {
|
class Suspension extends Command {
|
||||||
constructor(profileStore, id) {
|
constructor(profileStore, id) {
|
||||||
super(id)
|
super(id)
|
||||||
@ -24,51 +22,68 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
const Globally = Parser.C.string('global').thenLeft(Parser.C.string('ly').opt())
|
const Globally = Parser.C.string('global').thenLeft(
|
||||||
|
Parser.C.string('ly').opt()
|
||||||
|
)
|
||||||
const Forever = Parser.C.string('forever').thenReturns(null)
|
const Forever = Parser.C.string('forever').thenReturns(null)
|
||||||
const Timespan = Parser.N.digits.then(Parser.C.charIn('dhm')).map(function ([ span, unit ]) {
|
const Timespan = Parser.N.digits
|
||||||
switch (unit) {
|
.then(Parser.C.charIn('dhm'))
|
||||||
case 'd':
|
.map(function ([span, unit]) {
|
||||||
return span * 86400;
|
switch (unit) {
|
||||||
case 'h':
|
case 'd':
|
||||||
return span * 3600;
|
return span * 86400
|
||||||
case 'm':
|
case 'h':
|
||||||
return span * 60;
|
return span * 3600
|
||||||
}
|
case 'm':
|
||||||
throw new Error('Unreachable')
|
return span * 60
|
||||||
})
|
}
|
||||||
.rep()
|
throw new Error('Unreachable')
|
||||||
.map(parts => parts.array().reduce((carry, item) => carry + item, 0))
|
})
|
||||||
.map(offset => Math.floor(Date.now() / 1000) + offset)
|
.rep()
|
||||||
|
.map((parts) => parts.array().reduce((carry, item) => carry + item, 0))
|
||||||
|
.map((offset) => Math.floor(Date.now() / 1000) + offset)
|
||||||
|
|
||||||
const Duration = Forever.or(Timespan).or(Parser.ISODate.map(item => Math.floor(item.valueOf() / 1000)))
|
const Duration = Forever.or(Timespan).or(
|
||||||
|
Parser.ISODate.map((item) => Math.floor(item.valueOf() / 1000))
|
||||||
|
)
|
||||||
|
|
||||||
return Parser.Username.thenLeft(Parser.Whitespace.rep())
|
return Parser.Username.thenLeft(Parser.Whitespace.rep())
|
||||||
.then(Globally.thenLeft(Parser.Whitespace.rep()).thenReturns(true).or(Parser.F.returns(false)))
|
.then(
|
||||||
.then(Duration)
|
Globally.thenLeft(Parser.Whitespace.rep())
|
||||||
.then(Parser.Whitespace.rep().thenRight(Parser.Rest1).or(Parser.F.eos.thenReturns(null)))
|
.thenReturns(true)
|
||||||
.map(([ username, globally, duration, reason ]) => {
|
.or(Parser.F.returns(false))
|
||||||
return { username
|
)
|
||||||
, globally
|
.then(Duration)
|
||||||
, duration
|
.then(
|
||||||
, reason
|
Parser.Whitespace.rep()
|
||||||
}
|
.thenRight(Parser.Rest1)
|
||||||
})
|
.or(Parser.F.eos.thenReturns(null))
|
||||||
|
)
|
||||||
|
.map(([username, globally, duration, reason]) => {
|
||||||
|
return { username, globally, duration, reason }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
const usernameDone = Parser.Username.thenLeft(Parser.Whitespace.rep()).map(username => `"${username.replace(/"/g, '""')}"`)
|
const usernameDone = Parser.Username.thenLeft(
|
||||||
const globallyDone = usernameDone.then(Parser.C.string('global').thenLeft(Parser.C.string('ly').opt())).thenLeft(Parser.Whitespace.rep())
|
Parser.Whitespace.rep()
|
||||||
|
).map((username) => `"${username.replace(/"/g, '""')}"`)
|
||||||
|
const globallyDone = usernameDone
|
||||||
|
.then(Parser.C.string('global').thenLeft(Parser.C.string('ly').opt()))
|
||||||
|
.thenLeft(Parser.Whitespace.rep())
|
||||||
|
|
||||||
const usernameCheck = usernameDone.parse(Parser.Streams.ofString(parameterString))
|
const usernameCheck = usernameDone.parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
if (usernameCheck.isAccepted()) {
|
if (usernameCheck.isAccepted()) {
|
||||||
const globallyCheck = globallyDone.parse(Parser.Streams.ofString(parameterString))
|
const globallyCheck = globallyDone.parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
let prefix, rest
|
let prefix, rest
|
||||||
if (globallyCheck.isAccepted()) {
|
if (globallyCheck.isAccepted()) {
|
||||||
prefix = parameterString.substring(0, globallyCheck.offset)
|
prefix = parameterString.substring(0, globallyCheck.offset)
|
||||||
rest = parameterString.substring(globallyCheck.offset)
|
rest = parameterString.substring(globallyCheck.offset)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
prefix = parameterString.substring(0, usernameCheck.offset)
|
prefix = parameterString.substring(0, usernameCheck.offset)
|
||||||
rest = parameterString.substring(usernameCheck.offset)
|
rest = parameterString.substring(usernameCheck.offset)
|
||||||
}
|
}
|
||||||
@ -101,4 +116,4 @@ define([ '../Command'
|
|||||||
Suspension.DEPENDENCIES = DEPENDENCIES
|
Suspension.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Suspension
|
return Suspension
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Command'
|
define(['../Command', '../Parser'], function (Command, Parser) {
|
||||||
, '../Parser'
|
'use strict'
|
||||||
], function (Command, Parser) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore' ]
|
const DEPENDENCIES = ['ProfileStore']
|
||||||
class Unsuspension extends Command {
|
class Unsuspension extends Command {
|
||||||
constructor(profileStore, id) {
|
constructor(profileStore, id) {
|
||||||
super(id)
|
super(id)
|
||||||
@ -24,30 +22,39 @@ define([ '../Command'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getParameterParser() {
|
getParameterParser() {
|
||||||
const Globally = Parser.C.string('global').thenLeft(Parser.C.string('ly').opt())
|
const Globally = Parser.C.string('global').thenLeft(
|
||||||
|
Parser.C.string('ly').opt()
|
||||||
|
)
|
||||||
|
|
||||||
return Parser.Username
|
return Parser.Username.then(
|
||||||
.then(Parser.Whitespace.rep().thenRight(Globally.thenReturns(true)).or(Parser.F.returns(false)))
|
Parser.Whitespace.rep()
|
||||||
.map(([ username, globally ]) => {
|
.thenRight(Globally.thenReturns(true))
|
||||||
return { username
|
.or(Parser.F.returns(false))
|
||||||
, globally
|
).map(([username, globally]) => {
|
||||||
}
|
return { username, globally }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
* autocomplete(parameterString) {
|
*autocomplete(parameterString) {
|
||||||
const usernameDone = Parser.Username.thenLeft(Parser.Whitespace.rep()).map(username => `"${username.replace(/"/g, '""')}"`)
|
const usernameDone = Parser.Username.thenLeft(
|
||||||
const globallyDone = usernameDone.then(Parser.C.string('global').thenLeft(Parser.C.string('ly').opt())).thenLeft(Parser.Whitespace.rep())
|
Parser.Whitespace.rep()
|
||||||
|
).map((username) => `"${username.replace(/"/g, '""')}"`)
|
||||||
|
const globallyDone = usernameDone
|
||||||
|
.then(Parser.C.string('global').thenLeft(Parser.C.string('ly').opt()))
|
||||||
|
.thenLeft(Parser.Whitespace.rep())
|
||||||
|
|
||||||
const usernameCheck = usernameDone.parse(Parser.Streams.ofString(parameterString))
|
const usernameCheck = usernameDone.parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
if (usernameCheck.isAccepted()) {
|
if (usernameCheck.isAccepted()) {
|
||||||
const globallyCheck = globallyDone.parse(Parser.Streams.ofString(parameterString))
|
const globallyCheck = globallyDone.parse(
|
||||||
|
Parser.Streams.ofString(parameterString)
|
||||||
|
)
|
||||||
let prefix, rest
|
let prefix, rest
|
||||||
if (globallyCheck.isAccepted()) {
|
if (globallyCheck.isAccepted()) {
|
||||||
prefix = parameterString.substring(0, globallyCheck.offset)
|
prefix = parameterString.substring(0, globallyCheck.offset)
|
||||||
rest = parameterString.substring(globallyCheck.offset)
|
rest = parameterString.substring(globallyCheck.offset)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
prefix = parameterString.substring(0, usernameCheck.offset)
|
prefix = parameterString.substring(0, usernameCheck.offset)
|
||||||
rest = parameterString.substring(usernameCheck.offset)
|
rest = parameterString.substring(usernameCheck.offset)
|
||||||
}
|
}
|
||||||
@ -68,4 +75,4 @@ define([ '../Command'
|
|||||||
Unsuspension.DEPENDENCIES = DEPENDENCIES
|
Unsuspension.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Unsuspension
|
return Unsuspension
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Parser'
|
define(['./Parser', './ParseError'], function (Parser, ParseError) {
|
||||||
, './ParseError'
|
'use strict'
|
||||||
], function (Parser, ParseError) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'Trigger', 'Command' ]
|
const DEPENDENCIES = ['Trigger', 'Command']
|
||||||
class CommandHandler {
|
class CommandHandler {
|
||||||
constructor(triggers, commands) {
|
constructor(triggers, commands) {
|
||||||
this.triggers = triggers
|
this.triggers = triggers
|
||||||
@ -28,19 +26,19 @@ define([ './Parser'
|
|||||||
|
|
||||||
if (result.isAccepted()) {
|
if (result.isAccepted()) {
|
||||||
return result.value
|
return result.value
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throw new ParseError('Empty trigger')
|
throw new ParseError('Empty trigger')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCommand(command, parameterString) {
|
applyCommand(command, parameterString) {
|
||||||
const result = command.getParameterParser().parse(Parser.Streams.ofString(parameterString))
|
const result = command
|
||||||
|
.getParameterParser()
|
||||||
|
.parse(Parser.Streams.ofString(parameterString))
|
||||||
|
|
||||||
if (result.isAccepted()) {
|
if (result.isAccepted()) {
|
||||||
return result.value
|
return result.value
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
throw new ParseError('Could not parse', { result, parameterString })
|
throw new ParseError('Could not parse', { result, parameterString })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,4 +62,4 @@ define([ './Parser'
|
|||||||
CommandHandler.DEPENDENCIES = DEPENDENCIES
|
CommandHandler.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return CommandHandler
|
return CommandHandler
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,13 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const listeners = new WeakMap()
|
const listeners = new WeakMap()
|
||||||
const EventEmitter = function (target) {
|
const EventEmitter = function (target) {
|
||||||
Object.assign(target, {
|
Object.assign(target, {
|
||||||
on(type, listener, options = { }) {
|
on(type, listener, options = {}) {
|
||||||
if (!listeners.has(this)) {
|
if (!listeners.has(this)) {
|
||||||
listeners.set(this, new Map())
|
listeners.set(this, new Map())
|
||||||
}
|
}
|
||||||
@ -33,22 +33,24 @@ define([ ], function () {
|
|||||||
listeners.get(this).get(type).delete(listener)
|
listeners.get(this).get(type).delete(listener)
|
||||||
},
|
},
|
||||||
|
|
||||||
emit(type, detail = { }) {
|
emit(type, detail = {}) {
|
||||||
if (!listeners.has(this)) return
|
if (!listeners.has(this)) return
|
||||||
if (!listeners.get(this).has(type)) return
|
if (!listeners.get(this).has(type)) return
|
||||||
|
|
||||||
const set = listeners.get(this).get(type)
|
const set = listeners.get(this).get(type)
|
||||||
|
|
||||||
set.forEach((function ({ listener, options }) {
|
set.forEach(
|
||||||
if (options.once) {
|
function ({ listener, options }) {
|
||||||
set.delete(listener)
|
if (options.once) {
|
||||||
}
|
set.delete(listener)
|
||||||
|
}
|
||||||
|
|
||||||
listener({ target: this, detail })
|
listener({ target: this, detail })
|
||||||
}).bind(this))
|
}.bind(this)
|
||||||
}
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return EventEmitter
|
return EventEmitter
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const s = Symbol('s')
|
const s = Symbol('s')
|
||||||
const start = Symbol('start')
|
const start = Symbol('start')
|
||||||
@ -24,7 +24,7 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(value) {
|
add(value) {
|
||||||
if (this[start] && this[start].value === value) {
|
if (this[start] && this[start].value === value) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,14 +45,13 @@ define([ ], function () {
|
|||||||
this[s].set(value, obj)
|
this[s].set(value, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
* [Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
let current = this[start]
|
let current = this[start]
|
||||||
do {
|
do {
|
||||||
yield current.value
|
yield current.value
|
||||||
}
|
} while ((current = current.next))
|
||||||
while ((current = current.next))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return LRU
|
return LRU
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
@ -93,39 +93,52 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
search(value) {
|
search(value) {
|
||||||
if (value === this.value) return [ 'IS', this ]
|
if (value === this.value) return ['IS', this]
|
||||||
if (value < this.value) {
|
if (value < this.value) {
|
||||||
if (this.left !== undefined) return this.left.search(value)
|
if (this.left !== undefined) return this.left.search(value)
|
||||||
return [ 'LEFT', this ]
|
return ['LEFT', this]
|
||||||
}
|
}
|
||||||
if (value > this.value) {
|
if (value > this.value) {
|
||||||
if (this.right !== undefined) return this.right.search(value)
|
if (this.right !== undefined) return this.right.search(value)
|
||||||
return [ 'RIGHT', this ]
|
return ['RIGHT', this]
|
||||||
}
|
}
|
||||||
throw new Error('Unreachable')
|
throw new Error('Unreachable')
|
||||||
}
|
}
|
||||||
|
|
||||||
print(depth = 0) {
|
print(depth = 0) {
|
||||||
console.log(" ".repeat(depth) + `${this.value}: ${this.color} (Parent: ${this.parent ? this.parent.value : '-'})`)
|
console.log(
|
||||||
|
' '.repeat(depth) +
|
||||||
|
`${this.value}: ${this.color} (Parent: ${
|
||||||
|
this.parent ? this.parent.value : '-'
|
||||||
|
})`
|
||||||
|
)
|
||||||
if (this.left) this.left.print(depth + 1)
|
if (this.left) this.left.print(depth + 1)
|
||||||
else console.log(" ".repeat(depth + 1) + '-')
|
else console.log(' '.repeat(depth + 1) + '-')
|
||||||
if (this.right) this.right.print(depth + 1)
|
if (this.right) this.right.print(depth + 1)
|
||||||
else console.log(" ".repeat(depth + 1) + '-')
|
else console.log(' '.repeat(depth + 1) + '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
if (this.left && this.left.value >= this.value) throw new Error('Invalid' + this.value);
|
if (this.left && this.left.value >= this.value)
|
||||||
if (this.right && this.right.value <= this.value) throw new Error('Invalid' + this.value);
|
throw new Error('Invalid' + this.value)
|
||||||
if (this.color === 'RED' && ((this.left && this.left.color !== 'BLACK') || (this.right && this.right.color !== 'BLACK'))) throw new Error('Invalid' + this.value);
|
if (this.right && this.right.value <= this.value)
|
||||||
|
throw new Error('Invalid' + this.value)
|
||||||
|
if (
|
||||||
|
this.color === 'RED' &&
|
||||||
|
((this.left && this.left.color !== 'BLACK') ||
|
||||||
|
(this.right && this.right.color !== 'BLACK'))
|
||||||
|
)
|
||||||
|
throw new Error('Invalid' + this.value)
|
||||||
|
|
||||||
let leftBlacks = 1, rightBlacks = 1
|
let leftBlacks = 1,
|
||||||
|
rightBlacks = 1
|
||||||
if (this.left) {
|
if (this.left) {
|
||||||
leftBlacks = this.left.check()
|
leftBlacks = this.left.check()
|
||||||
}
|
}
|
||||||
if (this.right) {
|
if (this.right) {
|
||||||
rightBlacks = this.right.check()
|
rightBlacks = this.right.check()
|
||||||
}
|
}
|
||||||
if (leftBlacks !== rightBlacks) throw new Error('Invalid' + this.value);
|
if (leftBlacks !== rightBlacks) throw new Error('Invalid' + this.value)
|
||||||
|
|
||||||
if (this.color === 'BLACK') return leftBlacks + 1
|
if (this.color === 'BLACK') return leftBlacks + 1
|
||||||
return leftBlacks
|
return leftBlacks
|
||||||
@ -133,4 +146,4 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Node
|
return Node
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Node' ], function (Node) {
|
define(['./Node'], function (Node) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Tree {
|
class Tree {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -29,22 +29,22 @@ define([ './Node' ], function (Node) {
|
|||||||
if (this.root === undefined) {
|
if (this.root === undefined) {
|
||||||
this.root = node
|
this.root = node
|
||||||
this.fix(node)
|
this.fix(node)
|
||||||
return [ 'RIGHT', undefined ]
|
return ['RIGHT', undefined]
|
||||||
}
|
}
|
||||||
|
|
||||||
const search = this.search(value)
|
const search = this.search(value)
|
||||||
const [ side, parent ] = search
|
const [side, parent] = search
|
||||||
|
|
||||||
if (side === 'IS') return [ side, parent.value ]
|
if (side === 'IS') return [side, parent.value]
|
||||||
if (side === 'LEFT') {
|
if (side === 'LEFT') {
|
||||||
parent.left = node
|
parent.left = node
|
||||||
this.fix(node)
|
this.fix(node)
|
||||||
return [ side, parent.value ]
|
return [side, parent.value]
|
||||||
}
|
}
|
||||||
if (side === 'RIGHT') {
|
if (side === 'RIGHT') {
|
||||||
parent.right = node
|
parent.right = node
|
||||||
this.fix(node)
|
this.fix(node)
|
||||||
return [ side, parent.value ]
|
return [side, parent.value]
|
||||||
}
|
}
|
||||||
throw new Error('Unreachable')
|
throw new Error('Unreachable')
|
||||||
}
|
}
|
||||||
@ -74,8 +74,7 @@ define([ './Node' ], function (Node) {
|
|||||||
if (N.isRightChild && N.parent.isLeftChild) {
|
if (N.isRightChild && N.parent.isLeftChild) {
|
||||||
this.rotateLeft(N.parent)
|
this.rotateLeft(N.parent)
|
||||||
N = N.left
|
N = N.left
|
||||||
}
|
} else if (N.isLeftChild && N.parent.isRightChild) {
|
||||||
else if (N.isLeftChild && N.parent.isRightChild) {
|
|
||||||
this.rotateRight(N.parent)
|
this.rotateRight(N.parent)
|
||||||
N = N.right
|
N = N.right
|
||||||
}
|
}
|
||||||
@ -86,8 +85,7 @@ define([ './Node' ], function (Node) {
|
|||||||
G.color = 'RED'
|
G.color = 'RED'
|
||||||
if (N.isLeftChild) {
|
if (N.isLeftChild) {
|
||||||
this.rotateRight(G)
|
this.rotateRight(G)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.rotateLeft(G)
|
this.rotateLeft(G)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,11 +97,9 @@ define([ './Node' ], function (Node) {
|
|||||||
N.right = right.left
|
N.right = right.left
|
||||||
if (N.parent === undefined) {
|
if (N.parent === undefined) {
|
||||||
this.root = right
|
this.root = right
|
||||||
}
|
} else if (N.isLeftChild) {
|
||||||
else if (N.isLeftChild) {
|
|
||||||
N.parent.left = right
|
N.parent.left = right
|
||||||
}
|
} else if (N.isRightChild) {
|
||||||
else if (N.isRightChild) {
|
|
||||||
N.parent.right = right
|
N.parent.right = right
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +113,9 @@ define([ './Node' ], function (Node) {
|
|||||||
N.left = left.right
|
N.left = left.right
|
||||||
if (N.parent === undefined) {
|
if (N.parent === undefined) {
|
||||||
this.root = left
|
this.root = left
|
||||||
}
|
} else if (N.isLeftChild) {
|
||||||
else if (N.isLeftChild) {
|
|
||||||
N.parent.left = left
|
N.parent.left = left
|
||||||
}
|
} else if (N.isRightChild) {
|
||||||
else if (N.isRightChild) {
|
|
||||||
N.parent.right = left
|
N.parent.right = left
|
||||||
}
|
}
|
||||||
left.right = N
|
left.right = N
|
||||||
@ -129,4 +123,4 @@ define([ './Node' ], function (Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Tree
|
return Tree
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Throttler {
|
class Throttler {
|
||||||
constructor(callback, delay = 125) {
|
constructor(callback, delay = 125) {
|
||||||
@ -37,7 +37,7 @@ define([ ], function () {
|
|||||||
clearTimeout(this.timer)
|
clearTimeout(this.timer)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timer = setTimeout(_ => {
|
this.timer = setTimeout((_) => {
|
||||||
this.timer = null
|
this.timer = null
|
||||||
this.hot = false
|
this.hot = false
|
||||||
|
|
||||||
@ -60,8 +60,7 @@ define([ ], function () {
|
|||||||
guardedExecute() {
|
guardedExecute() {
|
||||||
if (this.hot) {
|
if (this.hot) {
|
||||||
this.awaiting = true
|
this.awaiting = true
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.execute()
|
this.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,11 +70,10 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set delay(newDelay) {
|
set delay(newDelay) {
|
||||||
if (this.awaiting && (Date.now() - this.last) > newDelay) {
|
if (this.awaiting && Date.now() - this.last > newDelay) {
|
||||||
this._delay = 0
|
this._delay = 0
|
||||||
this.setTimer()
|
this.setTimer()
|
||||||
}
|
} else if (this.timer) {
|
||||||
else if (this.timer) {
|
|
||||||
this._delay = Math.max(0, newDelay - (Date.now() - this.last))
|
this._delay = Math.max(0, newDelay - (Date.now() - this.last))
|
||||||
this.setTimer()
|
this.setTimer()
|
||||||
}
|
}
|
||||||
@ -98,4 +96,4 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return throttle
|
return throttle
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,11 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Date/Util'
|
define(['WoltLabSuite/Core/Date/Util', 'WoltLabSuite/Core/Language'], function (
|
||||||
, 'WoltLabSuite/Core/Language'
|
DateUtil,
|
||||||
], function (DateUtil, Language) {
|
Language
|
||||||
"use strict";
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Helper {
|
class Helper {
|
||||||
static deepFreeze(obj) {
|
static deepFreeze(obj) {
|
||||||
@ -38,11 +39,13 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
*/
|
*/
|
||||||
static isInput(element) {
|
static isInput(element) {
|
||||||
if (element.tagName === 'INPUT') {
|
if (element.tagName === 'INPUT') {
|
||||||
if (element.getAttribute('type') !== 'text' && element.getAttribute('type') !== 'password') {
|
if (
|
||||||
|
element.getAttribute('type') !== 'text' &&
|
||||||
|
element.getAttribute('type') !== 'password'
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
} else if (element.tagName !== 'TEXTAREA') {
|
||||||
else if (element.tagName !== 'TEXTAREA') {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,21 +56,20 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
let last = 0
|
let last = 0
|
||||||
let deferTimer = null
|
let deferTimer = null
|
||||||
|
|
||||||
return function() {
|
return function () {
|
||||||
const now = new Date().getTime()
|
const now = new Date().getTime()
|
||||||
const args = arguments
|
const args = arguments
|
||||||
const context = scope || this
|
const context = scope || this
|
||||||
|
|
||||||
if (last && (now < (last + threshold))) {
|
if (last && now < last + threshold) {
|
||||||
clearTimeout(deferTimer)
|
clearTimeout(deferTimer)
|
||||||
|
|
||||||
return deferTimer = setTimeout(function() {
|
return (deferTimer = setTimeout(function () {
|
||||||
last = now
|
last = now
|
||||||
|
|
||||||
return fn.apply(context, args)
|
return fn.apply(context, args)
|
||||||
}, threshold)
|
}, threshold))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
last = now
|
last = now
|
||||||
|
|
||||||
return fn.apply(context, args)
|
return fn.apply(context, args)
|
||||||
@ -108,8 +110,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
|
|
||||||
if (element.nextSibling) {
|
if (element.nextSibling) {
|
||||||
element.parentNode.insertBefore(wrapper, element.nextSibling)
|
element.parentNode.insertBefore(wrapper, element.nextSibling)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
element.parentNode.appendChild(wrapper)
|
element.parentNode.appendChild(wrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
throw new Error(`Unsupported element type: ${textarea.tagName}`)
|
throw new Error(`Unsupported element type: ${textarea.tagName}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const pre = document.createElement('pre')
|
const pre = document.createElement('pre')
|
||||||
const span = document.createElement('span')
|
const span = document.createElement('span')
|
||||||
|
|
||||||
const mirror = function () {
|
const mirror = function () {
|
||||||
@ -141,7 +142,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
pre.appendChild(document.createElement('br'))
|
pre.appendChild(document.createElement('br'))
|
||||||
textarea.parentNode.insertBefore(pre, textarea)
|
textarea.parentNode.insertBefore(pre, textarea)
|
||||||
|
|
||||||
textarea.addEventListener('input', mirror)
|
textarea.addEventListener('input', mirror)
|
||||||
mirror()
|
mirror()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,11 +151,12 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
constructor(size) {
|
constructor(size) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
Object.defineProperty(this, 'size', { enumerable: false
|
Object.defineProperty(this, 'size', {
|
||||||
, value: size
|
enumerable: false,
|
||||||
, writable: false
|
value: size,
|
||||||
, configurable: false
|
writable: false,
|
||||||
});
|
configurable: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
push() {
|
push() {
|
||||||
@ -164,7 +166,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
super.shift()
|
super.shift()
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.length;
|
return this.length
|
||||||
}
|
}
|
||||||
|
|
||||||
unshift() {
|
unshift() {
|
||||||
@ -174,7 +176,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
super.pop()
|
super.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.length;
|
return this.length
|
||||||
}
|
}
|
||||||
|
|
||||||
first() {
|
first() {
|
||||||
@ -190,9 +192,9 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
}
|
}
|
||||||
|
|
||||||
static intToRGBHex(integer) {
|
static intToRGBHex(integer) {
|
||||||
const r = ((integer >> 16) & 0xFF).toString(16)
|
const r = ((integer >> 16) & 0xff).toString(16)
|
||||||
const g = ((integer >> 8) & 0xFF).toString(16)
|
const g = ((integer >> 8) & 0xff).toString(16)
|
||||||
const b = ((integer >> 0) & 0xFF).toString(16)
|
const b = ((integer >> 0) & 0xff).toString(16)
|
||||||
|
|
||||||
const rr = r.length == 1 ? `0${r}` : r
|
const rr = r.length == 1 ? `0${r}` : r
|
||||||
const gg = g.length == 1 ? `0${g}` : g
|
const gg = g.length == 1 ? `0${g}` : g
|
||||||
@ -263,8 +265,7 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
if (firstTextNode) {
|
if (firstTextNode) {
|
||||||
nodeRange.setStart(firstTextNode, 0)
|
nodeRange.setStart(firstTextNode, 0)
|
||||||
nodeRange.setEnd(lastTextNode, lastTextNode.length)
|
nodeRange.setEnd(lastTextNode, lastTextNode.length)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
nodeRange.selectNodeContents(node)
|
nodeRange.selectNodeContents(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,9 +283,10 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
static getTextContent(node) {
|
static getTextContent(node) {
|
||||||
const acceptNode = node => {
|
const acceptNode = (node) => {
|
||||||
if (node instanceof Element) {
|
if (node instanceof Element) {
|
||||||
if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') return NodeFilter.FILTER_REJECT
|
if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE')
|
||||||
|
return NodeFilter.FILTER_REJECT
|
||||||
}
|
}
|
||||||
|
|
||||||
return NodeFilter.FILTER_ACCEPT
|
return NodeFilter.FILTER_ACCEPT
|
||||||
@ -295,33 +297,34 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
const flags = NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT
|
const flags = NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT
|
||||||
const treeWalker = document.createTreeWalker(node, flags, { acceptNode })
|
const treeWalker = document.createTreeWalker(node, flags, { acceptNode })
|
||||||
|
|
||||||
const ignoredLinks = [ ]
|
const ignoredLinks = []
|
||||||
|
|
||||||
while (treeWalker.nextNode()) {
|
while (treeWalker.nextNode()) {
|
||||||
const node = treeWalker.currentNode
|
const node = treeWalker.currentNode
|
||||||
|
|
||||||
if (node instanceof Text) {
|
if (node instanceof Text) {
|
||||||
if (node.parentNode.tagName === 'A' && ignoredLinks.indexOf(node.parentNode) >= 0) {
|
if (
|
||||||
|
node.parentNode.tagName === 'A' &&
|
||||||
|
ignoredLinks.indexOf(node.parentNode) >= 0
|
||||||
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
out += node.nodeValue.replace(/\n/g, '')
|
out += node.nodeValue.replace(/\n/g, '')
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
switch (node.tagName) {
|
switch (node.tagName) {
|
||||||
case 'IMG': {
|
case 'IMG': {
|
||||||
const alt = node.getAttribute('alt')
|
const alt = node.getAttribute('alt')
|
||||||
|
|
||||||
if (node.classList.contains('smiley')) {
|
if (node.classList.contains('smiley')) {
|
||||||
out += ` ${alt} `
|
out += ` ${alt} `
|
||||||
}
|
} else if (alt && alt !== '') {
|
||||||
else if (alt && alt !== '') {
|
|
||||||
out += ` ${alt} [Image ${node.src}] `
|
out += ` ${alt} [Image ${node.src}] `
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
out += ` [Image ${node.src}] `
|
out += ` [Image ${node.src}] `
|
||||||
}
|
}
|
||||||
break }
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case 'BR':
|
case 'BR':
|
||||||
case 'LI':
|
case 'LI':
|
||||||
@ -329,16 +332,16 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
case 'DIV':
|
case 'DIV':
|
||||||
case 'TR':
|
case 'TR':
|
||||||
out += '\n'
|
out += '\n'
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'TH':
|
case 'TH':
|
||||||
case 'TD':
|
case 'TD':
|
||||||
out += '\t'
|
out += '\t'
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
out += '\n\n'
|
out += '\n\n'
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'A': {
|
case 'A': {
|
||||||
let link = node.href
|
let link = node.href
|
||||||
@ -354,7 +357,9 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
const parts = text.split(/\u2026/)
|
const parts = text.split(/\u2026/)
|
||||||
|
|
||||||
if (parts.length === 2) {
|
if (parts.length === 2) {
|
||||||
truncated = node.href.startsWith(parts[0]) && node.href.endsWith(parts[1])
|
truncated =
|
||||||
|
node.href.startsWith(parts[0]) &&
|
||||||
|
node.href.endsWith(parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +369,8 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
}
|
}
|
||||||
|
|
||||||
out += link
|
out += link
|
||||||
break }
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,5 +380,4 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Helper
|
return Helper
|
||||||
});
|
})
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, LocalStorageEmulator) {
|
define(['WoltLabSuite/Core/Core', './LocalStorageEmulator'], function (
|
||||||
'use strict';
|
Core,
|
||||||
|
LocalStorageEmulator
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ ]
|
const DEPENDENCIES = []
|
||||||
class LocalStorage {
|
class LocalStorage {
|
||||||
constructor(subprefix) {
|
constructor(subprefix) {
|
||||||
this.subprefix = subprefix
|
this.subprefix = subprefix
|
||||||
@ -23,15 +26,17 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
}
|
}
|
||||||
|
|
||||||
static isQuotaExceeded(error) {
|
static isQuotaExceeded(error) {
|
||||||
return error instanceof DOMException && (
|
return (
|
||||||
|
error instanceof DOMException &&
|
||||||
// everything except Firefox
|
// everything except Firefox
|
||||||
error.code === 22 ||
|
(error.code === 22 ||
|
||||||
// Firefox
|
// Firefox
|
||||||
error.code === 1014 ||
|
error.code === 1014 ||
|
||||||
// everything except Firefox
|
// everything except Firefox
|
||||||
error.name === 'QuotaExceededError' ||
|
error.name === 'QuotaExceededError' ||
|
||||||
// Firefox
|
// Firefox
|
||||||
error.name === 'NS_ERROR_DOM_QUOTA_REACHED')
|
error.name === 'NS_ERROR_DOM_QUOTA_REACHED')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static isAvailable() {
|
static isAvailable() {
|
||||||
@ -40,8 +45,7 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
window.localStorage.setItem(x, x)
|
window.localStorage.setItem(x, x)
|
||||||
window.localStorage.removeItem(x)
|
window.localStorage.removeItem(x)
|
||||||
return true
|
return true
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return LocalStorage.isQuotaExceeded(error)
|
return LocalStorage.isQuotaExceeded(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,8 +54,7 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
if (LocalStorage.isAvailable()) {
|
if (LocalStorage.isAvailable()) {
|
||||||
this.storage = window.localStorage
|
this.storage = window.localStorage
|
||||||
this.hasLocalStorage = true
|
this.hasLocalStorage = true
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
console.info('Falling back to in-memory local storage emulation')
|
console.info('Falling back to in-memory local storage emulation')
|
||||||
this.storage = new LocalStorageEmulator()
|
this.storage = new LocalStorageEmulator()
|
||||||
}
|
}
|
||||||
@ -97,13 +100,19 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
*/
|
*/
|
||||||
set(key, value) {
|
set(key, value) {
|
||||||
try {
|
try {
|
||||||
this.storage.setItem(`${this.storagePrefix}${key}`, JSON.stringify(value))
|
this.storage.setItem(
|
||||||
}
|
`${this.storagePrefix}${key}`,
|
||||||
catch (error) {
|
JSON.stringify(value)
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
if (!LocalStorage.isQuotaExceeded(error)) throw error
|
if (!LocalStorage.isQuotaExceeded(error)) throw error
|
||||||
|
|
||||||
console.warn(`Your localStorage has exceeded the size quota for this domain`)
|
console.warn(
|
||||||
console.warn(`We are falling back to an in-memory storage, this does not persist data!`)
|
`Your localStorage has exceeded the size quota for this domain`
|
||||||
|
)
|
||||||
|
console.warn(
|
||||||
|
`We are falling back to an in-memory storage, this does not persist data!`
|
||||||
|
)
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
const storage = new LocalStorageEmulator()
|
const storage = new LocalStorageEmulator()
|
||||||
@ -152,7 +161,7 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
* @returns {string} The last value of the provided setting
|
* @returns {string} The last value of the provided setting
|
||||||
*/
|
*/
|
||||||
remove(key) {
|
remove(key) {
|
||||||
const value = this.get(key)
|
const value = this.get(key)
|
||||||
const storageKey = `${this.storagePrefix}${key}`
|
const storageKey = `${this.storagePrefix}${key}`
|
||||||
|
|
||||||
this.storage.removeItem(storageKey)
|
this.storage.removeItem(storageKey)
|
||||||
@ -167,13 +176,20 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
clear() {
|
clear() {
|
||||||
const _clear = (target) => {
|
const _clear = (target) => {
|
||||||
for (let key in target) {
|
for (let key in target) {
|
||||||
if (!key.startsWith(this.storagePrefix) || !target.hasOwnProperty(key)) continue
|
if (
|
||||||
|
!key.startsWith(this.storagePrefix) ||
|
||||||
|
!target.hasOwnProperty(key)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
target.removeItem(key)
|
target.removeItem(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasLocalStorage && this.storage instanceof LocalStorageEmulator) {
|
if (
|
||||||
|
this.hasLocalStorage &&
|
||||||
|
this.storage instanceof LocalStorageEmulator
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
// Try to clear the real localStorage
|
// Try to clear the real localStorage
|
||||||
_clear(localStorage)
|
_clear(localStorage)
|
||||||
@ -188,8 +204,9 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
this.storage = localStorage
|
this.storage = localStorage
|
||||||
|
|
||||||
console.log('Switched back to using the localStorage')
|
console.log('Switched back to using the localStorage')
|
||||||
|
} catch (error) {
|
||||||
|
/* no we can’t */
|
||||||
}
|
}
|
||||||
catch (error) { /* no we can’t */ }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_clear(this.storage)
|
_clear(this.storage)
|
||||||
@ -198,4 +215,4 @@ define([ 'WoltLabSuite/Core/Core', './LocalStorageEmulator' ], function (Core, L
|
|||||||
LocalStorage.DEPENDENCIES = DEPENDENCIES
|
LocalStorage.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return LocalStorage
|
return LocalStorage
|
||||||
});
|
})
|
||||||
|
@ -11,16 +11,19 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
class LocalStorageEmulator {
|
class LocalStorageEmulator {
|
||||||
constructor () {
|
constructor() {
|
||||||
this._data = new Map()
|
this._data = new Map()
|
||||||
return new Proxy(this, {
|
return new Proxy(this, {
|
||||||
get(target, property) {
|
get(target, property) {
|
||||||
// Check if the property exists on the object or its prototype
|
// Check if the property exists on the object or its prototype
|
||||||
if (target.hasOwnProperty(property) || Object.getPrototypeOf(target)[property]) {
|
if (
|
||||||
|
target.hasOwnProperty(property) ||
|
||||||
|
Object.getPrototypeOf(target)[property]
|
||||||
|
) {
|
||||||
return target[property]
|
return target[property]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,18 +32,22 @@ define([ ], function () {
|
|||||||
},
|
},
|
||||||
set(target, property, value) {
|
set(target, property, value) {
|
||||||
// Check if the property exists on the object or its prototype
|
// Check if the property exists on the object or its prototype
|
||||||
if (target.hasOwnProperty(property) || Object.getPrototypeOf(target)[property]) {
|
if (
|
||||||
|
target.hasOwnProperty(property) ||
|
||||||
|
Object.getPrototypeOf(target)[property]
|
||||||
|
) {
|
||||||
target[property] = value
|
target[property] = value
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Proxy to the underlying map
|
// Proxy to the underlying map
|
||||||
target.setItem(property, value)
|
target.setItem(property, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
has(target, property) {
|
has(target, property) {
|
||||||
return target.hasOwnProperty(property) // check the properties of the object
|
return (
|
||||||
|| Object.getPrototypeOf(target)[property] // check its prototype
|
target.hasOwnProperty(property) || // check the properties of the object
|
||||||
|| target._data.has(property) // check the underlying map
|
Object.getPrototypeOf(target)[property] || // check its prototype
|
||||||
|
target._data.has(property)
|
||||||
|
) // check the underlying map
|
||||||
},
|
},
|
||||||
ownKeys(target) {
|
ownKeys(target) {
|
||||||
// Proxy to the underlying map
|
// Proxy to the underlying map
|
||||||
@ -50,9 +57,9 @@ define([ ], function () {
|
|||||||
// Make the properties of the map visible
|
// Make the properties of the map visible
|
||||||
return {
|
return {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true
|
configurable: true,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,10 +87,10 @@ define([ ], function () {
|
|||||||
this._data.clear()
|
this._data.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
* [Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
yield * this._data.values()
|
yield* this._data.values()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return LocalStorageEmulator
|
return LocalStorageEmulator
|
||||||
});
|
})
|
||||||
|
@ -11,19 +11,32 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './console'
|
define([
|
||||||
, 'Bastelstu.be/bottle'
|
'./console',
|
||||||
, 'WoltLabSuite/Core/Core'
|
'Bastelstu.be/bottle',
|
||||||
, './Message'
|
'WoltLabSuite/Core/Core',
|
||||||
, './Messenger'
|
'./Message',
|
||||||
, './ProfileStore'
|
'./Messenger',
|
||||||
, './Room'
|
'./ProfileStore',
|
||||||
, './Template'
|
'./Room',
|
||||||
, './Ui/Log'
|
'./Template',
|
||||||
, './Ui/MessageStream'
|
'./Ui/Log',
|
||||||
, './Ui/MessageActions/Delete'
|
'./Ui/MessageStream',
|
||||||
], function (console, Bottle, Core, Message, Messenger, ProfileStore, Room, Template, Ui, UiMessageStream, UiMessageActionDelete) {
|
'./Ui/MessageActions/Delete',
|
||||||
"use strict";
|
], function (
|
||||||
|
console,
|
||||||
|
Bottle,
|
||||||
|
Core,
|
||||||
|
Message,
|
||||||
|
Messenger,
|
||||||
|
ProfileStore,
|
||||||
|
Room,
|
||||||
|
Template,
|
||||||
|
Ui,
|
||||||
|
UiMessageStream,
|
||||||
|
UiMessageActionDelete
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const loader = Symbol('loader')
|
const loader = Symbol('loader')
|
||||||
|
|
||||||
@ -31,10 +44,10 @@ define([ './console'
|
|||||||
constructor(params, config) {
|
constructor(params, config) {
|
||||||
console.debug('ChatLog.constructor', 'Constructing …')
|
console.debug('ChatLog.constructor', 'Constructing …')
|
||||||
|
|
||||||
this.config = config
|
this.config = config
|
||||||
|
|
||||||
this.sessionID = Core.getUuid()
|
this.sessionID = Core.getUuid()
|
||||||
this.bottle = new Bottle()
|
this.bottle = new Bottle()
|
||||||
this.bottle.value('bottle', this.bottle)
|
this.bottle.value('bottle', this.bottle)
|
||||||
this.bottle.value('config', config)
|
this.bottle.value('config', config)
|
||||||
this.bottle.constant('sessionID', this.sessionID)
|
this.bottle.constant('sessionID', this.sessionID)
|
||||||
@ -56,36 +69,45 @@ define([ './console'
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Register Templates
|
// Register Templates
|
||||||
const selector = [ '[type="x-text/template"]'
|
const selector = [
|
||||||
, '[data-application="be.bastelstu.chat"]'
|
'[type="x-text/template"]',
|
||||||
, '[data-template-name]'
|
'[data-application="be.bastelstu.chat"]',
|
||||||
].join('')
|
'[data-template-name]',
|
||||||
|
].join('')
|
||||||
const templates = elBySelAll(selector)
|
const templates = elBySelAll(selector)
|
||||||
templates.forEach((function (template) {
|
templates.forEach(
|
||||||
this.bottle.factory(`Template.${elData(template, 'template-name')}`, function (container) {
|
function (template) {
|
||||||
const includeNames = (elData(template, 'template-includes') || '').split(/ /).filter(item => item !== "")
|
this.bottle.factory(
|
||||||
const includes = { }
|
`Template.${elData(template, 'template-name')}`,
|
||||||
includeNames.forEach(item => includes[item] = container[item])
|
function (container) {
|
||||||
|
const includeNames = (elData(template, 'template-includes') || '')
|
||||||
|
.split(/ /)
|
||||||
|
.filter((item) => item !== '')
|
||||||
|
const includes = {}
|
||||||
|
includeNames.forEach((item) => (includes[item] = container[item]))
|
||||||
|
|
||||||
return new Template(template.textContent, includes)
|
return new Template(template.textContent, includes)
|
||||||
})
|
}
|
||||||
}).bind(this))
|
)
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
|
||||||
// Register MessageTypes
|
// Register MessageTypes
|
||||||
const messageTypes = Object.entries(this.config.messageTypes)
|
const messageTypes = Object.entries(this.config.messageTypes)
|
||||||
messageTypes.forEach(([ objectType, messageType ]) => {
|
messageTypes.forEach(([objectType, messageType]) => {
|
||||||
const MessageType = require(messageType.module)
|
const MessageType = require(messageType.module)
|
||||||
|
|
||||||
this.bottle.factory(`MessageType.${objectType.replace(/\./g, '-')}`, _ => {
|
this.bottle.factory(
|
||||||
const deps = this.bottle.digest(MessageType.DEPENDENCIES || [])
|
`MessageType.${objectType.replace(/\./g, '-')}`,
|
||||||
|
(_) => {
|
||||||
|
const deps = this.bottle.digest(MessageType.DEPENDENCIES || [])
|
||||||
|
|
||||||
return new MessageType(...deps, objectType)
|
return new MessageType(...deps, objectType)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.knows = { from: undefined
|
this.knows = { from: undefined, to: undefined }
|
||||||
, to: undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messageSinks = new Set()
|
this.messageSinks = new Set()
|
||||||
|
|
||||||
@ -94,9 +116,11 @@ define([ './console'
|
|||||||
this.pulling = false
|
this.pulling = false
|
||||||
}
|
}
|
||||||
|
|
||||||
service(name, _constructor, args = [ ]) {
|
service(name, _constructor, args = []) {
|
||||||
this.bottle.factory(name, function (container) {
|
this.bottle.factory(name, function (container) {
|
||||||
const deps = (_constructor.DEPENDENCIES || [ ]).map(dep => container[dep])
|
const deps = (_constructor.DEPENDENCIES || []).map(
|
||||||
|
(dep) => container[dep]
|
||||||
|
)
|
||||||
|
|
||||||
return new _constructor(...deps, ...args)
|
return new _constructor(...deps, ...args)
|
||||||
})
|
})
|
||||||
@ -111,20 +135,34 @@ define([ './console'
|
|||||||
this.registerMessageSink(this.bottle.container.UiMessageStream)
|
this.registerMessageSink(this.bottle.container.UiMessageStream)
|
||||||
|
|
||||||
if (this.params.messageID > 0) {
|
if (this.params.messageID > 0) {
|
||||||
await Promise.all([ this.pull(undefined, this.params.messageID)
|
await Promise.all([
|
||||||
, this.pull(this.params.messageID + 1)
|
this.pull(undefined, this.params.messageID),
|
||||||
])
|
this.pull(this.params.messageID + 1),
|
||||||
}
|
])
|
||||||
else {
|
} else {
|
||||||
await this.pull()
|
await this.pull()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bottle.container.UiMessageStream.on('nearTop', this.pullOlder.bind(this))
|
this.bottle.container.UiMessageStream.on(
|
||||||
this.bottle.container.UiMessageStream.on('reachedTop', this.pullOlder.bind(this))
|
'nearTop',
|
||||||
this.bottle.container.UiMessageStream.on('nearBottom', this.pullNewer.bind(this))
|
this.pullOlder.bind(this)
|
||||||
this.bottle.container.UiMessageStream.on('reachedBottom', this.pullNewer.bind(this))
|
)
|
||||||
|
this.bottle.container.UiMessageStream.on(
|
||||||
|
'reachedTop',
|
||||||
|
this.pullOlder.bind(this)
|
||||||
|
)
|
||||||
|
this.bottle.container.UiMessageStream.on(
|
||||||
|
'nearBottom',
|
||||||
|
this.pullNewer.bind(this)
|
||||||
|
)
|
||||||
|
this.bottle.container.UiMessageStream.on(
|
||||||
|
'reachedBottom',
|
||||||
|
this.pullNewer.bind(this)
|
||||||
|
)
|
||||||
|
|
||||||
const element = document.querySelector(`#message-${this.params.messageID}`)
|
const element = document.querySelector(
|
||||||
|
`#message-${this.params.messageID}`
|
||||||
|
)
|
||||||
|
|
||||||
// Force changing the hash to trigger a new lookup of the element.
|
// Force changing the hash to trigger a new lookup of the element.
|
||||||
// At least Chrome won’t target an element if it is not in the DOM
|
// At least Chrome won’t target an element if it is not in the DOM
|
||||||
@ -152,8 +190,7 @@ define([ './console'
|
|||||||
async pull(from, to) {
|
async pull(from, to) {
|
||||||
try {
|
try {
|
||||||
await this.handlePull(await this.performPull(from, to))
|
await this.handlePull(await this.performPull(from, to))
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
this.handleError(e)
|
this.handleError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +217,12 @@ define([ './console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
async performPull(from = undefined, to = undefined) {
|
async performPull(from = undefined, to = undefined) {
|
||||||
console.debug('ChatLog.performPull', `Pulling new messages; from: ${from !== undefined ? from : 'undefined'}, to: ${to !== undefined ? to : 'undefined'}`)
|
console.debug(
|
||||||
|
'ChatLog.performPull',
|
||||||
|
`Pulling new messages; from: ${
|
||||||
|
from !== undefined ? from : 'undefined'
|
||||||
|
}, to: ${to !== undefined ? to : 'undefined'}`
|
||||||
|
)
|
||||||
|
|
||||||
return this.bottle.container.Messenger.pull(from, to, true)
|
return this.bottle.container.Messenger.pull(from, to, true)
|
||||||
}
|
}
|
||||||
@ -199,25 +241,35 @@ define([ './console'
|
|||||||
let messages = payload.messages
|
let messages = payload.messages
|
||||||
|
|
||||||
if (this.knows.from !== undefined && this.knows.to !== undefined) {
|
if (this.knows.from !== undefined && this.knows.to !== undefined) {
|
||||||
messages = messages.filter((function (message) {
|
messages = messages.filter(
|
||||||
return !(this.knows.from <= message.messageID && message.messageID <= this.knows.to)
|
function (message) {
|
||||||
}).bind(this))
|
return !(
|
||||||
|
this.knows.from <= message.messageID &&
|
||||||
|
message.messageID <= this.knows.to
|
||||||
|
)
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.knows.from === undefined || payload.from < this.knows.from) this.knows.from = payload.from
|
if (this.knows.from === undefined || payload.from < this.knows.from)
|
||||||
if (this.knows.to === undefined || payload.to > this.knows.to) this.knows.to = payload.to
|
this.knows.from = payload.from
|
||||||
|
if (this.knows.to === undefined || payload.to > this.knows.to)
|
||||||
|
this.knows.to = payload.to
|
||||||
|
|
||||||
await Promise.all(messages.map((message) => {
|
await Promise.all(
|
||||||
return message.getMessageType().preProcess(message)
|
messages.map((message) => {
|
||||||
}))
|
return message.getMessageType().preProcess(message)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const userIDs = messages.map(message => message.userID)
|
const userIDs = messages
|
||||||
.filter(userID => userID !== null)
|
.map((message) => message.userID)
|
||||||
|
.filter((userID) => userID !== null)
|
||||||
await this.bottle.container.ProfileStore.ensureUsersByIDs(userIDs)
|
await this.bottle.container.ProfileStore.ensureUsersByIDs(userIDs)
|
||||||
|
|
||||||
this.messageSinks.forEach(sink => sink.ingest(messages))
|
this.messageSinks.forEach((sink) => sink.ingest(messages))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Log
|
return Log
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Helper'
|
define([
|
||||||
, 'WoltLabSuite/Core/Date/Util'
|
'./Helper',
|
||||||
, 'WoltLabSuite/Core/User'
|
'WoltLabSuite/Core/Date/Util',
|
||||||
], function (Helper, DateUtil, User) {
|
'WoltLabSuite/Core/User',
|
||||||
"use strict";
|
], function (Helper, DateUtil, User) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const m = Symbol('message')
|
const m = Symbol('message')
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
constructor(MessageType, message) {
|
constructor(MessageType, message) {
|
||||||
@ -87,4 +88,4 @@ define([ './Helper'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Message
|
return Message
|
||||||
});
|
})
|
||||||
|
@ -11,14 +11,15 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Date/Util'
|
define([
|
||||||
, 'WoltLabSuite/Core/Language'
|
'WoltLabSuite/Core/Date/Util',
|
||||||
, 'WoltLabSuite/Core/Dom/Util'
|
'WoltLabSuite/Core/Language',
|
||||||
, 'Bastelstu.be/Chat/User'
|
'WoltLabSuite/Core/Dom/Util',
|
||||||
], function (DateUtil, Language, DomUtil, User) {
|
'Bastelstu.be/Chat/User',
|
||||||
"use strict";
|
], function (DateUtil, Language, DomUtil, User) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore', 'Template' ]
|
const DEPENDENCIES = ['ProfileStore', 'Template']
|
||||||
class MessageType {
|
class MessageType {
|
||||||
constructor(profileStore, templates, objectType) {
|
constructor(profileStore, templates, objectType) {
|
||||||
this.profileStore = profileStore
|
this.profileStore = profileStore
|
||||||
@ -32,32 +33,31 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getReferencedUsers(message) {
|
getReferencedUsers(message) {
|
||||||
if (message.userID === null) return [ ]
|
if (message.userID === null) return []
|
||||||
|
|
||||||
return [ message.userID ]
|
return [message.userID]
|
||||||
}
|
}
|
||||||
|
|
||||||
preProcess(message) {
|
preProcess(message) {}
|
||||||
|
|
||||||
}
|
preRender(message) {}
|
||||||
|
|
||||||
preRender(message) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
render(message) {
|
render(message) {
|
||||||
const variables = { message
|
const variables = {
|
||||||
, users: this.profileStore
|
message,
|
||||||
, author: this.profileStore.get(message.userID)
|
users: this.profileStore,
|
||||||
, DateUtil
|
author: this.profileStore.get(message.userID),
|
||||||
, Language
|
DateUtil,
|
||||||
}
|
Language,
|
||||||
|
}
|
||||||
|
|
||||||
if (variables.author == null) {
|
if (variables.author == null) {
|
||||||
variables.author = User.getGuest(message.username)
|
variables.author = User.getGuest(message.username)
|
||||||
}
|
}
|
||||||
|
|
||||||
return DomUtil.createFragmentFromHtml(this.templates[message.objectType.replace(/\./g, '-')].fetch(variables))
|
return DomUtil.createFragmentFromHtml(
|
||||||
|
this.templates[message.objectType.replace(/\./g, '-')].fetch(variables)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPlainText(message) {
|
renderPlainText(message) {
|
||||||
@ -71,4 +71,4 @@ define([ 'WoltLabSuite/Core/Date/Util'
|
|||||||
MessageType.DEPENDENCIES = DEPENDENCIES
|
MessageType.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return MessageType
|
return MessageType
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore', 'roomID' ].concat(MessageType.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['ProfileStore', 'roomID'].concat(
|
||||||
|
MessageType.DEPENDENCIES || []
|
||||||
|
)
|
||||||
class Away extends MessageType {
|
class Away extends MessageType {
|
||||||
constructor(profileStore, roomID, ...superDeps) {
|
constructor(profileStore, roomID, ...superDeps) {
|
||||||
super(...superDeps)
|
super(...superDeps)
|
||||||
@ -24,12 +26,13 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(message) {
|
render(message) {
|
||||||
const isSilent = message.payload.rooms.find(room => room.roomID === this.roomID).isSilent
|
const isSilent = message.payload.rooms.find(
|
||||||
|
(room) => room.roomID === this.roomID
|
||||||
|
).isSilent
|
||||||
|
|
||||||
if (!isSilent) {
|
if (!isSilent) {
|
||||||
return super.render(message)
|
return super.render(message)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,4 +48,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
Away.DEPENDENCIES = DEPENDENCIES
|
Away.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Away
|
return Away
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore', 'roomID' ].concat(MessageType.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['ProfileStore', 'roomID'].concat(
|
||||||
|
MessageType.DEPENDENCIES || []
|
||||||
|
)
|
||||||
class Back extends MessageType {
|
class Back extends MessageType {
|
||||||
constructor(profileStore, roomID, ...superDeps) {
|
constructor(profileStore, roomID, ...superDeps) {
|
||||||
super(...superDeps)
|
super(...superDeps)
|
||||||
@ -24,12 +26,13 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(message) {
|
render(message) {
|
||||||
const isSilent = message.payload.rooms.find(room => room.roomID === this.roomID).isSilent
|
const isSilent = message.payload.rooms.find(
|
||||||
|
(room) => room.roomID === this.roomID
|
||||||
|
).isSilent
|
||||||
|
|
||||||
if (!isSilent) {
|
if (!isSilent) {
|
||||||
return super.render(message)
|
return super.render(message)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,4 +48,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
Back.DEPENDENCIES = DEPENDENCIES
|
Back.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Back
|
return Back
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain' ], function (Plain) {
|
define(['./Plain'], function (Plain) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Broadcast extends Plain {
|
class Broadcast extends Plain {
|
||||||
renderPlainText(message) {
|
renderPlainText(message) {
|
||||||
@ -21,4 +21,4 @@ define([ './Plain' ], function (Plain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Broadcast
|
return Broadcast
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class ChatUpdate extends MessageType {
|
class ChatUpdate extends MessageType {
|
||||||
preRender(message) {
|
preRender(message) {
|
||||||
@ -25,4 +25,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ChatUpdate
|
return ChatUpdate
|
||||||
});
|
})
|
||||||
|
@ -11,15 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Color extends MessageType {
|
class Color extends MessageType {
|
||||||
render(message) {
|
render(message) {
|
||||||
if (message.isOwnMessage()) {
|
if (message.isOwnMessage()) {
|
||||||
return super.render(message)
|
return super.render(message)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,4 +33,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Color
|
return Color
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,13 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Traverse'
|
define([
|
||||||
, 'WoltLabSuite/Core/Language'
|
'WoltLabSuite/Core/Dom/Traverse',
|
||||||
, '../Helper'
|
'WoltLabSuite/Core/Language',
|
||||||
, '../MessageType'
|
'../Helper',
|
||||||
], function (DomTraverse, Language, Helper, MessageType) {
|
'../MessageType',
|
||||||
"use strict";
|
], function (DomTraverse, Language, Helper, MessageType) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const decorators = Symbol('decorators')
|
const decorators = Symbol('decorators')
|
||||||
|
|
||||||
@ -36,16 +37,19 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
}
|
}
|
||||||
|
|
||||||
getReferencedUsers(message) {
|
getReferencedUsers(message) {
|
||||||
return super.getReferencedUsers(message).concat([ message.payload.user.userID ])
|
return super
|
||||||
|
.getReferencedUsers(message)
|
||||||
|
.concat([message.payload.user.userID])
|
||||||
}
|
}
|
||||||
|
|
||||||
render(message) {
|
render(message) {
|
||||||
const rooms = message.payload.rooms.map(function (item) {
|
const rooms = message.payload.rooms.map(function (item) {
|
||||||
const aug = { lastPull: null
|
const aug = {
|
||||||
, lastPullHTML: null
|
lastPull: null,
|
||||||
, lastPush: null
|
lastPullHTML: null,
|
||||||
, lastPushHTML: null
|
lastPush: null,
|
||||||
}
|
lastPushHTML: null,
|
||||||
|
}
|
||||||
|
|
||||||
if (item.lastPull) {
|
if (item.lastPull) {
|
||||||
aug.lastPull = new Date(item.lastPull * 1000)
|
aug.lastPull = new Date(item.lastPull * 1000)
|
||||||
@ -57,28 +61,35 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
aug.lastPushHTML = Helper.getTimeElementHTML(aug.lastPush)
|
aug.lastPushHTML = Helper.getTimeElementHTML(aug.lastPush)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign({ }, item, aug)
|
return Object.assign({}, item, aug)
|
||||||
})
|
})
|
||||||
|
|
||||||
const payload = Helper.deepFreeze(
|
const payload = Helper.deepFreeze(
|
||||||
Array.from(this[decorators]).reduce( (payload, decorator) => decorator(payload)
|
Array.from(this[decorators]).reduce(
|
||||||
, Object.assign({ }, message.payload, { rooms })
|
(payload, decorator) => decorator(payload),
|
||||||
)
|
Object.assign({}, message.payload, { rooms })
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const fragment = super.render(new Proxy(message, {
|
const fragment = super.render(
|
||||||
get: function (target, property) {
|
new Proxy(message, {
|
||||||
if (property === 'payload') return payload
|
get: function (target, property) {
|
||||||
return target[property]
|
if (property === 'payload') return payload
|
||||||
}
|
return target[property]
|
||||||
}))
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const icon = elCreate('span')
|
const icon = elCreate('span')
|
||||||
icon.classList.add('icon', 'icon16', 'fa-times', 'jsTooltip', 'hideIcon')
|
icon.classList.add('icon', 'icon16', 'fa-times', 'jsTooltip', 'hideIcon')
|
||||||
icon.setAttribute('title', Language.get('wcf.global.button.hide'))
|
icon.setAttribute('title', Language.get('wcf.global.button.hide'))
|
||||||
icon.addEventListener('click', () => elHide(DomTraverse.parentBySel(icon, '.chatMessageBoundary')))
|
icon.addEventListener('click', () =>
|
||||||
|
elHide(DomTraverse.parentBySel(icon, '.chatMessageBoundary'))
|
||||||
|
)
|
||||||
|
|
||||||
const elem = fragment.querySelector('.chatMessage .containerList > li:first-child .containerHeadline')
|
const elem = fragment.querySelector(
|
||||||
|
'.chatMessage .containerList > li:first-child .containerHeadline'
|
||||||
|
)
|
||||||
elem.insertBefore(icon, elem.firstChild)
|
elem.insertBefore(icon, elem.firstChild)
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
@ -86,4 +97,4 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Info
|
return Info
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,11 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType', 'WoltLabSuite/Core/Language' ], function (MessageType, Language) {
|
define(['../MessageType', 'WoltLabSuite/Core/Language'], function (
|
||||||
"use strict";
|
MessageType,
|
||||||
|
Language
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Join extends MessageType {
|
class Join extends MessageType {
|
||||||
shouldUpdateUserList(message) {
|
shouldUpdateUserList(message) {
|
||||||
@ -20,9 +23,15 @@ define([ '../MessageType', 'WoltLabSuite/Core/Language' ], function (MessageType
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderPlainText(message) {
|
renderPlainText(message) {
|
||||||
return '[➡️] ' + Language.get('chat.messageType.be.bastelstu.chat.messageType.join.plain', { author: { username: message.username } })
|
return (
|
||||||
|
'[➡️] ' +
|
||||||
|
Language.get(
|
||||||
|
'chat.messageType.be.bastelstu.chat.messageType.join.plain',
|
||||||
|
{ author: { username: message.username } }
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Join
|
return Join
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,11 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType', 'WoltLabSuite/Core/Language' ], function (MessageType, Language) {
|
define(['../MessageType', 'WoltLabSuite/Core/Language'], function (
|
||||||
"use strict";
|
MessageType,
|
||||||
|
Language
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Leave extends MessageType {
|
class Leave extends MessageType {
|
||||||
shouldUpdateUserList(message) {
|
shouldUpdateUserList(message) {
|
||||||
@ -20,9 +23,15 @@ define([ '../MessageType', 'WoltLabSuite/Core/Language' ], function (MessageType
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderPlainText(message) {
|
renderPlainText(message) {
|
||||||
return '[⬅️️] ' + Language.get('chat.messageType.be.bastelstu.chat.messageType.leave.plain', { author: { username: message.username } })
|
return (
|
||||||
|
'[⬅️️] ' +
|
||||||
|
Language.get(
|
||||||
|
'chat.messageType.be.bastelstu.chat.messageType.leave.plain',
|
||||||
|
{ author: { username: message.username } }
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Leave
|
return Leave
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Me extends MessageType {
|
class Me extends MessageType {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Me
|
return Me
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Plain extends MessageType {
|
class Plain extends MessageType {
|
||||||
joinable(a, b) {
|
joinable(a, b) {
|
||||||
@ -25,4 +25,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Plain
|
return Plain
|
||||||
});
|
})
|
||||||
|
@ -11,28 +11,35 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Helper'
|
define([
|
||||||
, 'WoltLabSuite/Core/Date/Util'
|
'../Helper',
|
||||||
, '../MessageType'
|
'WoltLabSuite/Core/Date/Util',
|
||||||
], function (Helper, DateUtil, MessageType) {
|
'../MessageType',
|
||||||
"use strict";
|
], function (Helper, DateUtil, MessageType) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Suspend extends MessageType {
|
class Suspend extends MessageType {
|
||||||
render(message) {
|
render(message) {
|
||||||
const expires = message.payload.suspension.expires !== null ? new Date(message.payload.suspension.expires * 1000) : null
|
const expires =
|
||||||
const formattedExpires = expires !== null ? DateUtil.formatDateTime(expires) : null
|
message.payload.suspension.expires !== null
|
||||||
const aug = { expires
|
? new Date(message.payload.suspension.expires * 1000)
|
||||||
, formattedExpires
|
: null
|
||||||
}
|
const formattedExpires =
|
||||||
const suspension = Object.assign({ }, message.payload.suspension, aug)
|
expires !== null ? DateUtil.formatDateTime(expires) : null
|
||||||
const payload = Helper.deepFreeze(Object.assign({ }, message.payload, { suspension }))
|
const aug = { expires, formattedExpires }
|
||||||
|
const suspension = Object.assign({}, message.payload.suspension, aug)
|
||||||
|
const payload = Helper.deepFreeze(
|
||||||
|
Object.assign({}, message.payload, { suspension })
|
||||||
|
)
|
||||||
|
|
||||||
return super.render(new Proxy(message, {
|
return super.render(
|
||||||
get: function (target, property) {
|
new Proxy(message, {
|
||||||
if (property === 'payload') return payload
|
get: function (target, property) {
|
||||||
return target[property]
|
if (property === 'payload') return payload
|
||||||
}
|
return target[property]
|
||||||
}))
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdateUserList(message) {
|
shouldUpdateUserList(message) {
|
||||||
@ -41,4 +48,4 @@ define([ '../Helper'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Suspend
|
return Suspend
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain' ], function (Plain) {
|
define(['./Plain'], function (Plain) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Team extends Plain {
|
class Team extends Plain {
|
||||||
joinable(a, b) {
|
joinable(a, b) {
|
||||||
@ -25,4 +25,4 @@ define([ './Plain' ], function (Plain) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Team
|
return Team
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class TemproomCreated extends MessageType {
|
class TemproomCreated extends MessageType {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return TemproomCreated
|
return TemproomCreated
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class TemproomInvited extends MessageType {
|
class TemproomInvited extends MessageType {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return TemproomInvited
|
return TemproomInvited
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiMessageStream' ].concat(MessageType.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['UiMessageStream'].concat(
|
||||||
|
MessageType.DEPENDENCIES || []
|
||||||
|
)
|
||||||
class Tombstone extends MessageType {
|
class Tombstone extends MessageType {
|
||||||
constructor(messageStream, ...superDeps) {
|
constructor(messageStream, ...superDeps) {
|
||||||
super(...superDeps)
|
super(...superDeps)
|
||||||
@ -37,17 +39,21 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
if (!chatMessage) return false
|
if (!chatMessage) return false
|
||||||
|
|
||||||
const rendered = super.render(message)
|
const rendered = super.render(message)
|
||||||
const oldIcon = node.querySelector('.chatMessageContent > .chatMessageIcon')
|
const oldIcon = node.querySelector(
|
||||||
|
'.chatMessageContent > .chatMessageIcon'
|
||||||
|
)
|
||||||
const newIcon = rendered.querySelector('.chatMessageIcon')
|
const newIcon = rendered.querySelector('.chatMessageIcon')
|
||||||
|
|
||||||
if (oldIcon) {
|
if (oldIcon) {
|
||||||
oldIcon.parentNode.replaceChild(newIcon, oldIcon)
|
oldIcon.parentNode.replaceChild(newIcon, oldIcon)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
chatMessage.parentNode.insertBefore(newIcon, chatMessage)
|
chatMessage.parentNode.insertBefore(newIcon, chatMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
chatMessage.parentNode.replaceChild(rendered.querySelector('.chatMessage'), chatMessage)
|
chatMessage.parentNode.replaceChild(
|
||||||
|
rendered.querySelector('.chatMessage'),
|
||||||
|
chatMessage
|
||||||
|
)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -55,4 +61,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
Tombstone.DEPENDENCIES = DEPENDENCIES
|
Tombstone.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Tombstone
|
return Tombstone
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../MessageType' ], function (MessageType) {
|
define(['../MessageType'], function (MessageType) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Unsuspend extends MessageType {
|
class Unsuspend extends MessageType {
|
||||||
shouldUpdateUserList(message) {
|
shouldUpdateUserList(message) {
|
||||||
@ -21,4 +21,4 @@ define([ '../MessageType' ], function (MessageType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Unsuspend
|
return Unsuspend
|
||||||
});
|
})
|
||||||
|
@ -11,11 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Traverse'
|
define([
|
||||||
, 'WoltLabSuite/Core/Language'
|
'WoltLabSuite/Core/Dom/Traverse',
|
||||||
, '../MessageType'
|
'WoltLabSuite/Core/Language',
|
||||||
], function (DomTraverse, Language, MessageType) {
|
'../MessageType',
|
||||||
"use strict";
|
], function (DomTraverse, Language, MessageType) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Where extends MessageType {
|
class Where extends MessageType {
|
||||||
render(message) {
|
render(message) {
|
||||||
@ -24,7 +25,9 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
const icon = elCreate('span')
|
const icon = elCreate('span')
|
||||||
icon.classList.add('icon', 'icon16', 'fa-times', 'jsTooltip', 'hideIcon')
|
icon.classList.add('icon', 'icon16', 'fa-times', 'jsTooltip', 'hideIcon')
|
||||||
icon.setAttribute('title', Language.get('wcf.global.button.hide'))
|
icon.setAttribute('title', Language.get('wcf.global.button.hide'))
|
||||||
icon.addEventListener('click', () => elHide(DomTraverse.parentBySel(icon, '.chatMessageBoundary')))
|
icon.addEventListener('click', () =>
|
||||||
|
elHide(DomTraverse.parentBySel(icon, '.chatMessageBoundary'))
|
||||||
|
)
|
||||||
|
|
||||||
const elem = fragment.querySelector('.jsRoomInfo > .containerHeadline')
|
const elem = fragment.querySelector('.jsRoomInfo > .containerHeadline')
|
||||||
elem.insertBefore(icon, elem.firstChild)
|
elem.insertBefore(icon, elem.firstChild)
|
||||||
@ -34,4 +37,4 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Where
|
return Where
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Plain' ], function (Plain) {
|
define(['./Plain'], function (Plain) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ].concat(Plain.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['UiInput'].concat(Plain.DEPENDENCIES || [])
|
||||||
class Whisper extends Plain {
|
class Whisper extends Plain {
|
||||||
constructor(input, ...superDeps) {
|
constructor(input, ...superDeps) {
|
||||||
super(...superDeps)
|
super(...superDeps)
|
||||||
@ -26,28 +26,39 @@ define([ './Plain' ], function (Plain) {
|
|||||||
const fragment = super.render(message)
|
const fragment = super.render(message)
|
||||||
|
|
||||||
if (this.input != null) {
|
if (this.input != null) {
|
||||||
Array.prototype.forEach.call(fragment.querySelectorAll('[data-insert-whisper]'), (function (el) {
|
Array.prototype.forEach.call(
|
||||||
el.addEventListener('click', (function () {
|
fragment.querySelectorAll('[data-insert-whisper]'),
|
||||||
const username = el.dataset.insertWhisper
|
function (el) {
|
||||||
const sanitizedUsername = username.replace(/"/g, '""')
|
el.addEventListener(
|
||||||
const command = `/whisper "${sanitizedUsername}"`
|
'click',
|
||||||
|
function () {
|
||||||
|
const username = el.dataset.insertWhisper
|
||||||
|
const sanitizedUsername = username.replace(/"/g, '""')
|
||||||
|
const command = `/whisper "${sanitizedUsername}"`
|
||||||
|
|
||||||
if (this.input.getText().indexOf(command) !== 0) {
|
if (this.input.getText().indexOf(command) !== 0) {
|
||||||
this.input.insertText(`${command} `, { prepend: true, append: false })
|
this.input.insertText(`${command} `, {
|
||||||
this.input.focus()
|
prepend: true,
|
||||||
}
|
append: false,
|
||||||
}).bind(this))
|
})
|
||||||
}).bind(this))
|
this.input.focus()
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
joinable(a, b) {
|
joinable(a, b) {
|
||||||
return a.userID === b.userID && a.payload.recipient === b.payload.recipient
|
return (
|
||||||
|
a.userID === b.userID && a.payload.recipient === b.payload.recipient
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Whisper.DEPENDENCIES = DEPENDENCIES
|
Whisper.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Whisper
|
return Whisper
|
||||||
});
|
})
|
||||||
|
@ -11,16 +11,18 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './console'
|
define(['./console', 'Bastelstu.be/PromiseWrap/Ajax', './Room'], function (
|
||||||
, 'Bastelstu.be/PromiseWrap/Ajax'
|
console,
|
||||||
, './Room'
|
Ajax,
|
||||||
], function (console, Ajax, Room) {
|
Room
|
||||||
"use strict";
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'sessionID', 'Room', 'Message' ]
|
const DEPENDENCIES = ['sessionID', 'Room', 'Message']
|
||||||
class Messenger {
|
class Messenger {
|
||||||
constructor(sessionID, room, Message) {
|
constructor(sessionID, room, Message) {
|
||||||
if (!(room instanceof Room)) throw new TypeError('You must pass a Room to the Messenger')
|
if (!(room instanceof Room))
|
||||||
|
throw new TypeError('You must pass a Room to the Messenger')
|
||||||
|
|
||||||
this.sessionID = sessionID
|
this.sessionID = sessionID
|
||||||
this.room = room
|
this.room = room
|
||||||
@ -30,9 +32,7 @@ define([ './console'
|
|||||||
async pull(from = 0, to = 0, inLog = false) {
|
async pull(from = 0, to = 0, inLog = false) {
|
||||||
console.debug(`Messenger.pull`, 'from', from, 'to', to, 'inLog', inLog)
|
console.debug(`Messenger.pull`, 'from', from, 'to', to, 'inLog', inLog)
|
||||||
|
|
||||||
const payload = { actionName: 'pull'
|
const payload = { actionName: 'pull', parameters: { inLog } }
|
||||||
, parameters: { inLog }
|
|
||||||
}
|
|
||||||
if (from !== 0 && to !== 0) {
|
if (from !== 0 && to !== 0) {
|
||||||
throw new Error('You must not set both from and to')
|
throw new Error('You must not set both from and to')
|
||||||
}
|
}
|
||||||
@ -40,42 +40,41 @@ define([ './console'
|
|||||||
if (to !== 0) payload.parameters.to = to
|
if (to !== 0) payload.parameters.to = to
|
||||||
|
|
||||||
const data = await Ajax.api(this, payload)
|
const data = await Ajax.api(this, payload)
|
||||||
const messages = Object.values(data.returnValues.messages).map((item) => this.Message.instance(item))
|
const messages = Object.values(data.returnValues.messages).map((item) =>
|
||||||
|
this.Message.instance(item)
|
||||||
|
)
|
||||||
const { from: newFrom, to: newTo } = data.returnValues
|
const { from: newFrom, to: newTo } = data.returnValues
|
||||||
|
|
||||||
return { messages, from: newFrom, to: newTo }
|
return { messages, from: newFrom, to: newTo }
|
||||||
}
|
}
|
||||||
|
|
||||||
async push({ commandID, parameters }) {
|
async push({ commandID, parameters }) {
|
||||||
const payload = { actionName: 'push'
|
const payload = {
|
||||||
, parameters: { commandID
|
actionName: 'push',
|
||||||
, parameters: JSON.stringify(parameters)
|
parameters: { commandID, parameters: JSON.stringify(parameters) },
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Ajax.api(this, payload)
|
return Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
async pushAttachment(tmpHash) {
|
async pushAttachment(tmpHash) {
|
||||||
const payload = { actionName: 'pushAttachment'
|
const payload = { actionName: 'pushAttachment', parameters: { tmpHash } }
|
||||||
, parameters: { tmpHash }
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ajax.api(this, payload)
|
return Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return {
|
||||||
, ignoreError: true
|
silent: true,
|
||||||
, data: { className: 'chat\\data\\message\\MessageAction'
|
ignoreError: true,
|
||||||
, parameters: { roomID: this.room.roomID
|
data: {
|
||||||
, sessionID: this.sessionID
|
className: 'chat\\data\\message\\MessageAction',
|
||||||
}
|
parameters: { roomID: this.room.roomID, sessionID: this.sessionID },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Messenger.DEPENDENCIES = DEPENDENCIES
|
Messenger.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Messenger
|
return Messenger
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class ParseError extends Error {
|
class ParseError extends Error {
|
||||||
constructor(message, data) {
|
constructor(message, data) {
|
||||||
@ -23,4 +23,4 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ParseError
|
return ParseError
|
||||||
});
|
})
|
||||||
|
@ -11,88 +11,112 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'Bastelstu.be/parser-combinator'
|
define(['Bastelstu.be/parser-combinator'], function (parsec) {
|
||||||
], function (parsec) {
|
'use strict'
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const { C, F, N, X, parser, Streams } = parsec
|
const { C, F, N, X, parser, Streams } = parsec
|
||||||
const response = parsec.parsec.response
|
const response = parsec.parsec.response
|
||||||
|
|
||||||
const peek = function (p) {
|
const peek = function (p) {
|
||||||
return new parser((input, index = 0) =>
|
return new parser((input, index = 0) =>
|
||||||
p
|
p.parse(input, index).fold(
|
||||||
.parse(input, index)
|
(accept) => response.accept(accept.value, accept.input, index, false),
|
||||||
.fold(
|
(reject) => response.reject(input.location(reject.offset), false)
|
||||||
accept => response.accept(accept.value, accept.input, index, false),
|
|
||||||
reject => response.reject(input.location(reject.offset), false)
|
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Whitespace = F.satisfy(item => /\s/.test(item))
|
const Whitespace = F.satisfy((item) => /\s/.test(item))
|
||||||
|
|
||||||
const Rest = F.any.optrep().map(item => item.join(''))
|
const Rest = F.any.optrep().map((item) => item.join(''))
|
||||||
const Rest1 = F.any.rep().map(item => item.join(''))
|
const Rest1 = F.any.rep().map((item) => item.join(''))
|
||||||
|
|
||||||
const AlnumTrigger = C.letter.or(N.digit).rep().map(item => item.join(''))
|
const AlnumTrigger = C.letter
|
||||||
const SymbolicTrigger = F.not(C.letter.or(N.digit).or(Whitespace)).rep().map(item => item.join(''))
|
.or(N.digit)
|
||||||
|
.rep()
|
||||||
|
.map((item) => item.join(''))
|
||||||
|
const SymbolicTrigger = F.not(C.letter.or(N.digit).or(Whitespace))
|
||||||
|
.rep()
|
||||||
|
.map((item) => item.join(''))
|
||||||
const Slash = C.char('/')
|
const Slash = C.char('/')
|
||||||
const Trigger = Slash.thenRight(
|
const Trigger = Slash.thenRight(
|
||||||
peek(Slash.map(item => null)).or(AlnumTrigger.thenLeft(Whitespace.rep().or(F.eos))).or(SymbolicTrigger.thenLeft(Whitespace.optrep()))
|
peek(Slash.map((item) => null))
|
||||||
|
.or(AlnumTrigger.thenLeft(Whitespace.rep().or(F.eos)))
|
||||||
|
.or(SymbolicTrigger.thenLeft(Whitespace.optrep()))
|
||||||
).or(F.returns(null))
|
).or(F.returns(null))
|
||||||
const Command = Trigger.then(Rest)
|
const Command = Trigger.then(Rest)
|
||||||
|
|
||||||
const Quote = C.char('"')
|
const Quote = C.char('"')
|
||||||
const QuotedUsername = Quote.thenRight(
|
const QuotedUsername = Quote.thenRight(
|
||||||
((Quote.thenRight(Quote)).or(F.not(Quote))).rep()
|
Quote.thenRight(Quote).or(F.not(Quote)).rep()
|
||||||
).thenLeft(Quote).map(item => item.join(''))
|
)
|
||||||
|
.thenLeft(Quote)
|
||||||
|
.map((item) => item.join(''))
|
||||||
const Comma = C.char(',')
|
const Comma = C.char(',')
|
||||||
const UnquotedUsername = F.not(Comma.or(Quote).or(Whitespace)).then(F.not(Comma.or(Whitespace)).optrep().map(item => item.join(''))).map(item => item.join(''))
|
const UnquotedUsername = F.not(Comma.or(Quote).or(Whitespace))
|
||||||
|
.then(
|
||||||
|
F.not(Comma.or(Whitespace))
|
||||||
|
.optrep()
|
||||||
|
.map((item) => item.join(''))
|
||||||
|
)
|
||||||
|
.map((item) => item.join(''))
|
||||||
const Username = QuotedUsername.or(UnquotedUsername)
|
const Username = QuotedUsername.or(UnquotedUsername)
|
||||||
|
|
||||||
const Decimal = (length) => N.digit.occurrence(length).map(item => parseInt(item.join(''), 10))
|
const Decimal = (length) =>
|
||||||
|
N.digit.occurrence(length).map((item) => parseInt(item.join(''), 10))
|
||||||
|
|
||||||
const Hexadecimal = N.digit
|
const Hexadecimal = N.digit
|
||||||
.or(C.charIn('abcdefABCDEF'))
|
.or(C.charIn('abcdefABCDEF'))
|
||||||
.rep()
|
.rep()
|
||||||
.map(x => x.join(''))
|
.map((x) => x.join(''))
|
||||||
|
|
||||||
const RGBHex = (C.char('#').opt())
|
const RGBHex = C.char('#')
|
||||||
|
.opt()
|
||||||
.thenRight(
|
.thenRight(
|
||||||
Hexadecimal.filter(x => x.length === 3 || x.length === 6)
|
Hexadecimal.filter((x) => x.length === 3 || x.length === 6).map(
|
||||||
.map(item => {
|
(item) => {
|
||||||
if (item.length === 3) {
|
if (item.length === 3) {
|
||||||
item = `${item[0]}${item[0]}${item[1]}${item[1]}${item[2]}${item[2]}`
|
item = `${item[0]}${item[0]}${item[1]}${item[1]}${item[2]}${item[2]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
})
|
}
|
||||||
).map(item => `#${item}`)
|
)
|
||||||
|
)
|
||||||
|
.map((item) => `#${item}`)
|
||||||
|
|
||||||
const Dash = C.char('-')
|
const Dash = C.char('-')
|
||||||
const Datestring = Decimal(4).filter(item => 2000 <= item && item <= 2030)
|
const Datestring = Decimal(4)
|
||||||
.thenLeft(Dash).then(Decimal(2).filter(item => 1 <= item && item <= 12))
|
.filter((item) => 2000 <= item && item <= 2030)
|
||||||
.thenLeft(Dash).then(Decimal(2).filter(item => 1 <= item))
|
.thenLeft(Dash)
|
||||||
|
.then(Decimal(2).filter((item) => 1 <= item && item <= 12))
|
||||||
|
.thenLeft(Dash)
|
||||||
|
.then(Decimal(2).filter((item) => 1 <= item))
|
||||||
|
|
||||||
const Colon = C.char(':')
|
const Colon = C.char(':')
|
||||||
const Timestring = Decimal(2).filter(item => 0 <= item && item <= 23)
|
const Timestring = Decimal(2)
|
||||||
.thenLeft(Colon).then(Decimal(2).filter(item => 0 <= item && item <= 59))
|
.filter((item) => 0 <= item && item <= 23)
|
||||||
.thenLeft(Colon).then(Decimal(2).filter(item => 0 <= item && item <= 59))
|
.thenLeft(Colon)
|
||||||
|
.then(Decimal(2).filter((item) => 0 <= item && item <= 59))
|
||||||
|
.thenLeft(Colon)
|
||||||
|
.then(Decimal(2).filter((item) => 0 <= item && item <= 59))
|
||||||
|
|
||||||
const ISODate = Datestring.then(C.char('T').thenRight(Timestring).opt()).map(function ([ year, month, day, time ]) {
|
const ISODate = Datestring.then(C.char('T').thenRight(Timestring).opt()).map(
|
||||||
const date = new Date()
|
function ([year, month, day, time]) {
|
||||||
date.setFullYear(year)
|
const date = new Date()
|
||||||
date.setMonth(month - 1)
|
date.setFullYear(year)
|
||||||
date.setDate(day)
|
date.setMonth(month - 1)
|
||||||
|
date.setDate(day)
|
||||||
|
|
||||||
time.map(function ([ hour, minute, second ]) {
|
time.map(function ([hour, minute, second]) {
|
||||||
date.setHours(hour)
|
date.setHours(hour)
|
||||||
date.setMinutes(minute)
|
date.setMinutes(minute)
|
||||||
date.setSeconds(second)
|
date.setSeconds(second)
|
||||||
})
|
})
|
||||||
|
|
||||||
return date
|
return date
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Streams,
|
Streams,
|
||||||
@ -122,4 +146,4 @@ define([ 'Bastelstu.be/parser-combinator'
|
|||||||
N,
|
N,
|
||||||
X,
|
X,
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
@ -11,14 +11,15 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
define([
|
||||||
, './DataStructure/LRU'
|
'Bastelstu.be/PromiseWrap/Ajax',
|
||||||
, './User'
|
'./DataStructure/LRU',
|
||||||
, 'WoltLabSuite/Core/User'
|
'./User',
|
||||||
], function (Ajax, LRU, User, CoreUser) {
|
'WoltLabSuite/Core/User',
|
||||||
"use strict";
|
], function (Ajax, LRU, User, CoreUser) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ ]
|
const DEPENDENCIES = []
|
||||||
/**
|
/**
|
||||||
* ProfileStore stores information about users.
|
* ProfileStore stores information about users.
|
||||||
*/
|
*/
|
||||||
@ -40,44 +41,47 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
*/
|
*/
|
||||||
async ensureUsersByIDs(userIDs) {
|
async ensureUsersByIDs(userIDs) {
|
||||||
// Dedup
|
// Dedup
|
||||||
userIDs = userIDs.filter((value, index, self) => self.indexOf(value) === index)
|
userIDs = userIDs
|
||||||
.map(userID => parseInt(userID, 10))
|
.filter((value, index, self) => self.indexOf(value) === index)
|
||||||
|
.map((userID) => parseInt(userID, 10))
|
||||||
|
|
||||||
const missing = [ ]
|
const missing = []
|
||||||
const promises = [ ]
|
const promises = []
|
||||||
userIDs.forEach((function (userID) {
|
userIDs.forEach(
|
||||||
if (this.isRecent(userID)) return
|
function (userID) {
|
||||||
if (this.processing.has(userID)) {
|
if (this.isRecent(userID)) return
|
||||||
promises.push(this.processing.get(userID))
|
if (this.processing.has(userID)) {
|
||||||
return
|
promises.push(this.processing.get(userID))
|
||||||
}
|
return
|
||||||
missing.push(userID)
|
}
|
||||||
}).bind(this))
|
missing.push(userID)
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
|
||||||
if (missing.length > 0) {
|
if (missing.length > 0) {
|
||||||
const payload = { actionName: 'getUsersByID'
|
const payload = {
|
||||||
, parameters: { userIDs: missing }
|
actionName: 'getUsersByID',
|
||||||
}
|
parameters: { userIDs: missing },
|
||||||
const request = (async _ => {
|
}
|
||||||
|
const request = (async (_) => {
|
||||||
try {
|
try {
|
||||||
const response = await Ajax.api(this, payload)
|
const response = await Ajax.api(this, payload)
|
||||||
return Object.entries(response.returnValues).forEach(([ userID, user ]) => {
|
return Object.entries(response.returnValues).forEach(
|
||||||
userID = parseInt(userID, 10)
|
([userID, user]) => {
|
||||||
const data = { user: new User(user)
|
userID = parseInt(userID, 10)
|
||||||
, date: Date.now()
|
const data = { user: new User(user), date: Date.now() }
|
||||||
}
|
this.users.set(userID, data)
|
||||||
this.users.set(userID, data)
|
this.processing.delete(userID)
|
||||||
this.processing.delete(userID)
|
}
|
||||||
})
|
)
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
missing.forEach((userID) => this.processing.delete(userID))
|
||||||
missing.forEach(userID => this.processing.delete(userID))
|
|
||||||
|
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
missing.forEach(userID => this.processing.set(userID, request))
|
missing.forEach((userID) => this.processing.set(userID, request))
|
||||||
promises.push(request)
|
promises.push(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +97,7 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
async getUsersByIDs(userIDs) {
|
async getUsersByIDs(userIDs) {
|
||||||
await this.ensureUsersByIDs(userIDs)
|
await this.ensureUsersByIDs(userIDs)
|
||||||
|
|
||||||
return new Map(userIDs.map(userID => [ userID, this.get(userID) ]))
|
return new Map(userIDs.map((userID) => [userID, this.get(userID)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,7 +161,7 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
const user = this.users.get(userID)
|
const user = this.users.get(userID)
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return user.date > (Date.now() - (5 * 60e3))
|
return user.date > Date.now() - 5 * 60e3
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -169,7 +173,7 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
* @returns {User[]}
|
* @returns {User[]}
|
||||||
*/
|
*/
|
||||||
values() {
|
values() {
|
||||||
return Array.from(this.users.values()).map(item => item.user)
|
return Array.from(this.users.values()).map((item) => item.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
pushLastActivity(userID) {
|
pushLastActivity(userID) {
|
||||||
@ -178,18 +182,19 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
this.lastActivity.add(userID)
|
this.lastActivity.add(userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
* getLastActivity() {
|
*getLastActivity() {
|
||||||
yield * this.lastActivity
|
yield* this.lastActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return {
|
||||||
, ignoreError: true
|
silent: true,
|
||||||
, data: { className: 'chat\\data\\user\\UserAction' }
|
ignoreError: true,
|
||||||
}
|
data: { className: 'chat\\data\\user\\UserAction' },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProfileStore.DEPENDENCIES = DEPENDENCIES
|
ProfileStore.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return ProfileStore
|
return ProfileStore
|
||||||
});
|
})
|
||||||
|
@ -11,20 +11,21 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
define([
|
||||||
, 'WoltLabSuite/Core/Core'
|
'Bastelstu.be/PromiseWrap/Ajax',
|
||||||
, './User'
|
'WoltLabSuite/Core/Core',
|
||||||
], function (Ajax, Core, User) {
|
'./User',
|
||||||
"use strict";
|
], function (Ajax, Core, User) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'sessionID', 'roomID' ]
|
const DEPENDENCIES = ['sessionID', 'roomID']
|
||||||
/**
|
/**
|
||||||
* Represents a chat room.
|
* Represents a chat room.
|
||||||
*/
|
*/
|
||||||
class Room {
|
class Room {
|
||||||
constructor(sessionID, roomID) {
|
constructor(sessionID, roomID) {
|
||||||
this.sessionID = sessionID
|
this.sessionID = sessionID
|
||||||
this.roomID = roomID
|
this.roomID = roomID
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,12 +34,11 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async join() {
|
async join() {
|
||||||
const payload = { className: 'chat\\data\\room\\RoomAction'
|
const payload = {
|
||||||
, actionName: 'join'
|
className: 'chat\\data\\room\\RoomAction',
|
||||||
, parameters: { roomID: this.roomID
|
actionName: 'join',
|
||||||
, sessionID: this.sessionID
|
parameters: { roomID: this.roomID, sessionID: this.sessionID },
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Ajax.api(this, payload)
|
return Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
@ -49,12 +49,11 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
* @param {boolean} unload Send a beacon if true'ish and a regular AJAX request otherwise.
|
* @param {boolean} unload Send a beacon if true'ish and a regular AJAX request otherwise.
|
||||||
*/
|
*/
|
||||||
leave(unload = false) {
|
leave(unload = false) {
|
||||||
const payload = { className: 'chat\\data\\room\\RoomAction'
|
const payload = {
|
||||||
, actionName: 'leave'
|
className: 'chat\\data\\room\\RoomAction',
|
||||||
, parameters: { roomID: this.roomID
|
actionName: 'leave',
|
||||||
, sessionID: this.sessionID
|
parameters: { roomID: this.roomID, sessionID: this.sessionID },
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (unload && FormData && (navigator.sendBeacon || window.fetch)) {
|
if (unload && FormData && (navigator.sendBeacon || window.fetch)) {
|
||||||
// Ordinary AJAX requests are unreliable during unload:
|
// Ordinary AJAX requests are unreliable during unload:
|
||||||
@ -65,22 +64,26 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
Core.serialize(payload)
|
Core.serialize(payload)
|
||||||
.split('&')
|
.split('&')
|
||||||
.map((item) => item.split('='))
|
.map((item) => item.split('='))
|
||||||
.map((item) => item.map(decodeURIComponent))
|
.map((item) => item.map(decodeURIComponent))
|
||||||
.forEach((item) => formData.append(item[0], item[1]))
|
.forEach((item) => formData.append(item[0], item[1]))
|
||||||
|
|
||||||
if (navigator.sendBeacon) {
|
if (navigator.sendBeacon) {
|
||||||
navigator.sendBeacon(url, formData)
|
navigator.sendBeacon(url, formData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.fetch) {
|
if (window.fetch) {
|
||||||
fetch(url, { method: 'POST', keepalive: true, redirect: 'follow', body: formData })
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
keepalive: true,
|
||||||
|
redirect: 'follow',
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Ajax.api(this, payload)
|
return Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,23 +94,22 @@ define([ 'Bastelstu.be/PromiseWrap/Ajax'
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async getUsers() {
|
async getUsers() {
|
||||||
const payload = { className: 'chat\\data\\room\\RoomAction'
|
const payload = {
|
||||||
, actionName: 'getUsers'
|
className: 'chat\\data\\room\\RoomAction',
|
||||||
, objectIDs: [ this.roomID ]
|
actionName: 'getUsers',
|
||||||
}
|
objectIDs: [this.roomID],
|
||||||
|
}
|
||||||
|
|
||||||
const result = await Ajax.api(this, payload)
|
const result = await Ajax.api(this, payload)
|
||||||
|
|
||||||
return Object.values(result.returnValues).map(user => new User(user))
|
return Object.values(result.returnValues).map((user) => new User(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return { silent: true, ignoreError: true }
|
||||||
, ignoreError: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Room.DEPENDENCIES = DEPENDENCIES
|
Room.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Room
|
return Room
|
||||||
});
|
})
|
||||||
|
@ -11,30 +11,30 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Template' ], function (_Template) {
|
define(['WoltLabSuite/Core/Template'], function (_Template) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template extends WoltLab Suite's Templates by passing in a list of
|
* Template extends WoltLab Suite's Templates by passing in a list of
|
||||||
* re-usable sub-templates.
|
* re-usable sub-templates.
|
||||||
*/
|
*/
|
||||||
class Template extends _Template {
|
class Template extends _Template {
|
||||||
constructor(string, templates = { }) {
|
constructor(string, templates = {}) {
|
||||||
super(string)
|
super(string)
|
||||||
|
|
||||||
this.templates = templates
|
this.templates = templates
|
||||||
|
|
||||||
const oldFetch = this.fetch
|
const oldFetch = this.fetch
|
||||||
this.fetch = (function (variables) {
|
this.fetch = function (variables) {
|
||||||
variables = Object.assign({ }, variables)
|
variables = Object.assign({}, variables)
|
||||||
|
|
||||||
const templates = Object.assign({ }, this.templates, variables.t || { })
|
const templates = Object.assign({}, this.templates, variables.t || {})
|
||||||
variables.t = templates
|
variables.t = templates
|
||||||
|
|
||||||
return oldFetch(variables)
|
return oldFetch(variables)
|
||||||
}).bind(this)
|
}.bind(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Template
|
return Template
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Ui {
|
class Ui {
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ui
|
return Ui
|
||||||
});
|
})
|
||||||
|
@ -11,30 +11,42 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Language'
|
define([
|
||||||
, 'WoltLabSuite/Core/Upload'
|
'WoltLabSuite/Core/Language',
|
||||||
, 'WoltLabSuite/Core/Dom/Change/Listener'
|
'WoltLabSuite/Core/Upload',
|
||||||
, 'WoltLabSuite/Core/Dom/Util'
|
'WoltLabSuite/Core/Dom/Change/Listener',
|
||||||
, 'WoltLabSuite/Core/Ui/Dialog'
|
'WoltLabSuite/Core/Dom/Util',
|
||||||
, '../../DataStructure/EventEmitter'
|
'WoltLabSuite/Core/Ui/Dialog',
|
||||||
], function(Language, Upload, DomChangeListener, DomUtil, Dialog, EventEmitter) {
|
'../../DataStructure/EventEmitter',
|
||||||
"use strict";
|
], function (
|
||||||
|
Language,
|
||||||
|
Upload,
|
||||||
|
DomChangeListener,
|
||||||
|
DomUtil,
|
||||||
|
Dialog,
|
||||||
|
EventEmitter
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DIALOG_BUTTON_ID = 'chatAttachmentUploadButton'
|
const DIALOG_BUTTON_ID = 'chatAttachmentUploadButton'
|
||||||
const DIALOG_CONTAINER_ID = 'chatAttachmentUploadDialog'
|
const DIALOG_CONTAINER_ID = 'chatAttachmentUploadDialog'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput', 'Room' ];
|
const DEPENDENCIES = ['UiInput', 'Room']
|
||||||
class UiAttachmentUpload extends Upload {
|
class UiAttachmentUpload extends Upload {
|
||||||
constructor(input, room) {
|
constructor(input, room) {
|
||||||
const buttonContainer = document.querySelector(`#${DIALOG_CONTAINER_ID} > .upload`)
|
const buttonContainer = document.querySelector(
|
||||||
|
`#${DIALOG_CONTAINER_ID} > .upload`
|
||||||
|
)
|
||||||
const buttonContainerId = DomUtil.identify(buttonContainer)
|
const buttonContainerId = DomUtil.identify(buttonContainer)
|
||||||
|
|
||||||
const previewContainer = document.querySelector(`#${DIALOG_CONTAINER_ID} > .attachmentPreview`)
|
const previewContainer = document.querySelector(
|
||||||
|
`#${DIALOG_CONTAINER_ID} > .attachmentPreview`
|
||||||
|
)
|
||||||
const previewContainerId = DomUtil.identify(previewContainer)
|
const previewContainerId = DomUtil.identify(previewContainer)
|
||||||
|
|
||||||
super(buttonContainerId, previewContainerId, {
|
super(buttonContainerId, previewContainerId, {
|
||||||
className: 'wcf\\data\\attachment\\AttachmentAction',
|
className: 'wcf\\data\\attachment\\AttachmentAction',
|
||||||
acceptableFiles: [ '.png', '.gif', '.jpg', '.jpeg' ]
|
acceptableFiles: ['.png', '.gif', '.jpg', '.jpeg'],
|
||||||
})
|
})
|
||||||
|
|
||||||
this.input = input
|
this.input = input
|
||||||
@ -44,7 +56,9 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.uploadDescription = document.querySelector(`#${DIALOG_CONTAINER_ID} > small`)
|
this.uploadDescription = document.querySelector(
|
||||||
|
`#${DIALOG_CONTAINER_ID} > small`
|
||||||
|
)
|
||||||
|
|
||||||
const button = document.getElementById(DIALOG_BUTTON_ID)
|
const button = document.getElementById(DIALOG_BUTTON_ID)
|
||||||
const container = document.getElementById(DIALOG_CONTAINER_ID)
|
const container = document.getElementById(DIALOG_CONTAINER_ID)
|
||||||
@ -59,18 +73,20 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
|
|
||||||
Dialog.openStatic(container.id, null, {
|
Dialog.openStatic(container.id, null, {
|
||||||
title: elData(container, 'title'),
|
title: elData(container, 'title'),
|
||||||
onShow: () => this.showDialog()
|
onShow: () => this.showDialog(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const deleteAction = new WCF.Action.Delete('wcf\\data\\attachment\\AttachmentAction', `#${this.previewContainer.id} > p`)
|
const deleteAction = new WCF.Action.Delete(
|
||||||
|
'wcf\\data\\attachment\\AttachmentAction',
|
||||||
|
`#${this.previewContainer.id} > p`
|
||||||
|
)
|
||||||
deleteAction.setCallback(() => this.closeDialog())
|
deleteAction.setCallback(() => this.closeDialog())
|
||||||
|
|
||||||
this.input.on('input', (event) => {
|
this.input.on('input', (event) => {
|
||||||
if (event.target.input.value.length == 0) {
|
if (event.target.input.value.length == 0) {
|
||||||
button.classList.remove('disabled')
|
button.classList.remove('disabled')
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
button.classList.add('disabled')
|
button.classList.add('disabled')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -95,16 +111,13 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
|
|
||||||
async send(tmpHash, event) {
|
async send(tmpHash, event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const parameters = { promise: Promise.resolve()
|
const parameters = { promise: Promise.resolve(), tmpHash }
|
||||||
, tmpHash
|
|
||||||
}
|
|
||||||
this.emit('send', parameters)
|
this.emit('send', parameters)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await parameters.promise
|
await parameters.promise
|
||||||
this.closeDialog()
|
this.closeDialog()
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
@ -141,14 +154,15 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
* @see WoltLabSuite/Core/Upload#_getParameters
|
* @see WoltLabSuite/Core/Upload#_getParameters
|
||||||
*/
|
*/
|
||||||
_getParameters() {
|
_getParameters() {
|
||||||
this.tmpHash = [ ...crypto.getRandomValues(new Uint8Array(20)) ]
|
this.tmpHash = [...crypto.getRandomValues(new Uint8Array(20))]
|
||||||
.map(m => ('0' + m.toString(16)).slice(-2))
|
.map((m) => ('0' + m.toString(16)).slice(-2))
|
||||||
.join('')
|
.join('')
|
||||||
|
|
||||||
return { objectType: "be.bastelstu.chat.message"
|
return {
|
||||||
, parentObjectID: this.room.roomID
|
objectType: 'be.bastelstu.chat.message',
|
||||||
, tmpHash: this.tmpHash
|
parentObjectID: this.room.roomID,
|
||||||
}
|
tmpHash: this.tmpHash,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,22 +172,28 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
if (data.returnValues.errors && data.returnValues.errors[0]) {
|
if (data.returnValues.errors && data.returnValues.errors[0]) {
|
||||||
const error = data.returnValues.errors[0]
|
const error = data.returnValues.errors[0]
|
||||||
|
|
||||||
elInnerError(this._button, Language.get(`wcf.attachment.upload.error.${error.errorType}`, {
|
elInnerError(
|
||||||
filename: error.filename
|
this._button,
|
||||||
}))
|
Language.get(`wcf.attachment.upload.error.${error.errorType}`, {
|
||||||
|
filename: error.filename,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
elInnerError(this._button, '')
|
elInnerError(this._button, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.returnValues.attachments && data.returnValues.attachments[uploadId]) {
|
if (
|
||||||
|
data.returnValues.attachments &&
|
||||||
|
data.returnValues.attachments[uploadId]
|
||||||
|
) {
|
||||||
this._removeButton()
|
this._removeButton()
|
||||||
elHide(this.uploadDescription)
|
elHide(this.uploadDescription)
|
||||||
|
|
||||||
const attachment = data.returnValues.attachments[uploadId]
|
const attachment = data.returnValues.attachments[uploadId]
|
||||||
const url = attachment.thumbnailURL || attachment.tinyURL || attachment.url
|
const url =
|
||||||
|
attachment.thumbnailURL || attachment.tinyURL || attachment.url
|
||||||
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new Error('Missing image URL')
|
throw new Error('Missing image URL')
|
||||||
@ -188,8 +208,7 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
|
|
||||||
if (url === attachment.thumbnailURL) {
|
if (url === attachment.thumbnailURL) {
|
||||||
img.classList.add('attachmentThumbnail')
|
img.classList.add('attachmentThumbnail')
|
||||||
}
|
} else if (url === attachment.tinyURL) {
|
||||||
else if (url === attachment.tinyURL) {
|
|
||||||
img.classList.add('attachmentTinyThumbnail')
|
img.classList.add('attachmentTinyThumbnail')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +218,8 @@ define([ 'WoltLabSuite/Core/Language'
|
|||||||
DomUtil.replaceElement(progress, img)
|
DomUtil.replaceElement(progress, img)
|
||||||
|
|
||||||
this.createButtonGroup(uploadId, attachment.attachmentID, this.tmpHash)
|
this.createButtonGroup(uploadId, attachment.attachmentID, this.tmpHash)
|
||||||
}
|
} else {
|
||||||
else {
|
console.error('Received neither an error nor an attachment response')
|
||||||
console.error("Received neither an error nor an attachment response")
|
|
||||||
console.error(data.returnValues)
|
console.error(data.returnValues)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,25 +11,46 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../console'
|
define([
|
||||||
, '../CommandHandler'
|
'../console',
|
||||||
, '../LocalStorage'
|
'../CommandHandler',
|
||||||
, '../Messenger'
|
'../LocalStorage',
|
||||||
, '../ProfileStore'
|
'../Messenger',
|
||||||
, 'WoltLabSuite/Core/Language'
|
'../ProfileStore',
|
||||||
, 'WoltLabSuite/Core/Timer/Repeating'
|
'WoltLabSuite/Core/Language',
|
||||||
], function (console, CommandHandler, LocalStorage, Messenger, ProfileStore, Language, RepeatingTimer) {
|
'WoltLabSuite/Core/Timer/Repeating',
|
||||||
"use strict";
|
], function (
|
||||||
|
console,
|
||||||
|
CommandHandler,
|
||||||
|
LocalStorage,
|
||||||
|
Messenger,
|
||||||
|
ProfileStore,
|
||||||
|
Language,
|
||||||
|
RepeatingTimer
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'config', 'CommandHandler', 'Messenger', 'ProfileStore', 'UiInput' ]
|
const DEPENDENCIES = [
|
||||||
|
'config',
|
||||||
|
'CommandHandler',
|
||||||
|
'Messenger',
|
||||||
|
'ProfileStore',
|
||||||
|
'UiInput',
|
||||||
|
]
|
||||||
class AutoAway {
|
class AutoAway {
|
||||||
constructor(config, commandHandler, messenger, profileStore, input) {
|
constructor(config, commandHandler, messenger, profileStore, input) {
|
||||||
if (!(commandHandler instanceof CommandHandler)) throw new TypeError('You must pass a CommandHandler to the AutoAway')
|
if (!(commandHandler instanceof CommandHandler))
|
||||||
if (!(messenger instanceof Messenger)) throw new TypeError('You must pass a Messenger to the AutoAway')
|
throw new TypeError('You must pass a CommandHandler to the AutoAway')
|
||||||
if (!(profileStore instanceof ProfileStore)) throw new TypeError('You must pass a ProfileStore to the AutoAway')
|
if (!(messenger instanceof Messenger))
|
||||||
|
throw new TypeError('You must pass a Messenger to the AutoAway')
|
||||||
|
if (!(profileStore instanceof ProfileStore))
|
||||||
|
throw new TypeError('You must pass a ProfileStore to the AutoAway')
|
||||||
|
|
||||||
this.storage = new LocalStorage('AutoAway.')
|
this.storage = new LocalStorage('AutoAway.')
|
||||||
this.awayCommand = commandHandler.getCommandByIdentifier('be.bastelstu.chat', 'away')
|
this.awayCommand = commandHandler.getCommandByIdentifier(
|
||||||
|
'be.bastelstu.chat',
|
||||||
|
'away'
|
||||||
|
)
|
||||||
if (this.awayCommand == null) {
|
if (this.awayCommand == null) {
|
||||||
throw new Error('Unreachable')
|
throw new Error('Unreachable')
|
||||||
}
|
}
|
||||||
@ -47,16 +68,22 @@ define([ '../console'
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timer = new RepeatingTimer(this.setAway.bind(this), this.config.autoAwayTime * 60e3)
|
this.timer = new RepeatingTimer(
|
||||||
this.input.on('input', this.inputListener = (event) => {
|
this.setAway.bind(this),
|
||||||
this.storage.set('channel', Date.now())
|
this.config.autoAwayTime * 60e3
|
||||||
this.reset()
|
)
|
||||||
})
|
this.input.on(
|
||||||
|
'input',
|
||||||
|
(this.inputListener = (event) => {
|
||||||
|
this.storage.set('channel', Date.now())
|
||||||
|
this.reset()
|
||||||
|
})
|
||||||
|
)
|
||||||
this.storage.observe('channel', this.reset.bind(this))
|
this.storage.observe('channel', this.reset.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
ingest(messages) {
|
ingest(messages) {
|
||||||
if (messages.some(message => message.isOwnMessage())) this.reset()
|
if (messages.some((message) => message.isOwnMessage())) this.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
@ -70,8 +97,11 @@ define([ '../console'
|
|||||||
async setAway() {
|
async setAway() {
|
||||||
console.debug('AutoAway.setAway', `Attempting to set as away`)
|
console.debug('AutoAway.setAway', `Attempting to set as away`)
|
||||||
|
|
||||||
if (this.storage.get('setAway') >= (Date.now() - 10e3)) {
|
if (this.storage.get('setAway') >= Date.now() - 10e3) {
|
||||||
console.debug('AutoAway.setAway', `setAway called within the last 10 seconds in another Tab`)
|
console.debug(
|
||||||
|
'AutoAway.setAway',
|
||||||
|
`setAway called within the last 10 seconds in another Tab`
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.storage.set('setAway', Date.now())
|
this.storage.set('setAway', Date.now())
|
||||||
@ -81,10 +111,13 @@ define([ '../console'
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messenger.push({ commandID: this.awayCommand.id, parameters: { reason: Language.get('chat.user.autoAway') } })
|
this.messenger.push({
|
||||||
|
commandID: this.awayCommand.id,
|
||||||
|
parameters: { reason: Language.get('chat.user.autoAway') },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AutoAway.DEPENDENCIES = DEPENDENCIES
|
AutoAway.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return AutoAway
|
return AutoAway
|
||||||
});
|
})
|
||||||
|
@ -11,42 +11,58 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Ui' ], function (Ui) {
|
define(['../Ui'], function (Ui) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiAttachmentUpload'
|
const DEPENDENCIES = [
|
||||||
, 'UiAutoAway'
|
'UiAttachmentUpload',
|
||||||
, 'UiConnectionWarning'
|
'UiAutoAway',
|
||||||
, 'UiInput'
|
'UiConnectionWarning',
|
||||||
, 'UiInputAutocompleter'
|
'UiInput',
|
||||||
, 'UiMessageActionDelete'
|
'UiInputAutocompleter',
|
||||||
, 'UiMessageStream'
|
'UiMessageActionDelete',
|
||||||
, 'UiMobile'
|
'UiMessageStream',
|
||||||
, 'UiNotification'
|
'UiMobile',
|
||||||
, 'UiReadMarker'
|
'UiNotification',
|
||||||
, 'UiSettings'
|
'UiReadMarker',
|
||||||
, 'UiTopic'
|
'UiSettings',
|
||||||
, 'UiUserActionDropdownHandler'
|
'UiTopic',
|
||||||
, 'UiUserList'
|
'UiUserActionDropdownHandler',
|
||||||
]
|
'UiUserList',
|
||||||
|
]
|
||||||
class Chat extends Ui {
|
class Chat extends Ui {
|
||||||
constructor(attachmentUpload, autoAway, connectionWarning, input, autocompleter, messageActionDelete, messageStream, mobile, notification, readMarker, settings, topic, userActionDropdownHandler, userList) {
|
constructor(
|
||||||
|
attachmentUpload,
|
||||||
|
autoAway,
|
||||||
|
connectionWarning,
|
||||||
|
input,
|
||||||
|
autocompleter,
|
||||||
|
messageActionDelete,
|
||||||
|
messageStream,
|
||||||
|
mobile,
|
||||||
|
notification,
|
||||||
|
readMarker,
|
||||||
|
settings,
|
||||||
|
topic,
|
||||||
|
userActionDropdownHandler,
|
||||||
|
userList
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.actionDropdownHandler = userActionDropdownHandler
|
this.actionDropdownHandler = userActionDropdownHandler
|
||||||
this.attachmentUpload = attachmentUpload
|
this.attachmentUpload = attachmentUpload
|
||||||
this.autoAway = autoAway
|
this.autoAway = autoAway
|
||||||
this.autocompleter = autocompleter
|
this.autocompleter = autocompleter
|
||||||
this.connectionWarning = connectionWarning
|
this.connectionWarning = connectionWarning
|
||||||
this.input = input
|
this.input = input
|
||||||
this.messageActionDelete = messageActionDelete
|
this.messageActionDelete = messageActionDelete
|
||||||
this.messageStream = messageStream
|
this.messageStream = messageStream
|
||||||
this.mobile = mobile
|
this.mobile = mobile
|
||||||
this.notification = notification
|
this.notification = notification
|
||||||
this.readMarker = readMarker
|
this.readMarker = readMarker
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
this.topic = topic
|
this.topic = topic
|
||||||
this.userList = userList
|
this.userList = userList
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
@ -69,4 +85,4 @@ define([ '../Ui' ], function (Ui) {
|
|||||||
Chat.DEPENDENCIES = DEPENDENCIES
|
Chat.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Chat
|
return Chat
|
||||||
});
|
})
|
||||||
|
@ -11,25 +11,22 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../console'
|
define(['../console'], function (console) {
|
||||||
], function (console) {
|
'use strict'
|
||||||
"use strict";
|
|
||||||
|
|
||||||
class ConnectionWarning {
|
class ConnectionWarning {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.warning = elById('chatConnectionWarning')
|
this.warning = elById('chatConnectionWarning')
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
elShow(this.warning)
|
elShow(this.warning)
|
||||||
if (this.timeout) return
|
if (this.timeout) return
|
||||||
|
|
||||||
console.debug('ConnectionWarning.show', 'Setting timeout')
|
console.debug('ConnectionWarning.show', 'Setting timeout')
|
||||||
this.timeout = setTimeout(_ => {
|
this.timeout = setTimeout((_) => {
|
||||||
console.debug('ConnectionWarning.show', 'Timeout has passed')
|
console.debug('ConnectionWarning.show', 'Timeout has passed')
|
||||||
this.timeout = undefined
|
this.timeout = undefined
|
||||||
|
|
||||||
@ -44,9 +41,11 @@ define([ '../console'
|
|||||||
if (!this.timeout || force) {
|
if (!this.timeout || force) {
|
||||||
elHide(this.warning)
|
elHide(this.warning)
|
||||||
window.clearTimeout(this.timeout)
|
window.clearTimeout(this.timeout)
|
||||||
}
|
} else {
|
||||||
else {
|
console.debug(
|
||||||
console.debug('ConnectionWarning.hide', 'Automatically hiding after timeout has passed')
|
'ConnectionWarning.hide',
|
||||||
|
'Automatically hiding after timeout has passed'
|
||||||
|
)
|
||||||
this.autoHide = true
|
this.autoHide = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,4 +56,4 @@ define([ '../console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ConnectionWarning
|
return ConnectionWarning
|
||||||
});
|
})
|
||||||
|
@ -11,28 +11,31 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Language'
|
define([
|
||||||
, 'WoltLabSuite/Core/Template'
|
'WoltLabSuite/Core/Language',
|
||||||
, 'WoltLabSuite/Core/Ui/Dialog'
|
'WoltLabSuite/Core/Template',
|
||||||
], function (Language, Template, UiDialog) {
|
'WoltLabSuite/Core/Ui/Dialog',
|
||||||
"use strict";
|
], function (Language, Template, UiDialog) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const html = [ '[type="x-text/template"]'
|
const html = [
|
||||||
, '[data-application="be.bastelstu.chat"]'
|
'[type="x-text/template"]',
|
||||||
, '[data-template-name="be-bastelstu-chat-errorDialog"]'
|
'[data-application="be.bastelstu.chat"]',
|
||||||
].join('')
|
'[data-template-name="be-bastelstu-chat-errorDialog"]',
|
||||||
|
].join('')
|
||||||
|
|
||||||
const wrapper = new Template(elBySel(html).textContent)
|
const wrapper = new Template(elBySel(html).textContent)
|
||||||
|
|
||||||
class ErrorDialog {
|
class ErrorDialog {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
const options = { title: Language.get('wcf.global.error.title')
|
const options = {
|
||||||
, closable: false
|
title: Language.get('wcf.global.error.title'),
|
||||||
}
|
closable: false,
|
||||||
|
}
|
||||||
|
|
||||||
UiDialog.openStatic('chatError', wrapper.fetch({ message }), options)
|
UiDialog.openStatic('chatError', wrapper.fetch({ message }), options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorDialog
|
return ErrorDialog
|
||||||
});
|
})
|
||||||
|
@ -11,21 +11,22 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../console'
|
define([
|
||||||
, '../Helper'
|
'../console',
|
||||||
, 'WoltLabSuite/Core/Core'
|
'../Helper',
|
||||||
, 'WoltLabSuite/Core/Event/Key'
|
'WoltLabSuite/Core/Core',
|
||||||
, '../DataStructure/EventEmitter'
|
'WoltLabSuite/Core/Event/Key',
|
||||||
, '../DataStructure/Throttle'
|
'../DataStructure/EventEmitter',
|
||||||
], function (console, Helper, Core, EventKey, EventEmitter, Throttle) {
|
'../DataStructure/Throttle',
|
||||||
"use strict";
|
], function (console, Helper, Core, EventKey, EventEmitter, Throttle) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
class Input {
|
class Input {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.inputContainer = elById('chatInputContainer')
|
this.inputContainer = elById('chatInputContainer')
|
||||||
this.input = elBySel('textarea', this.inputContainer)
|
this.input = elBySel('textarea', this.inputContainer)
|
||||||
this.charCounter = elBySel('.charCounter', this.inputContainer)
|
this.charCounter = elBySel('.charCounter', this.inputContainer)
|
||||||
this.errorElement = elBySel('.innerError', this.inputContainer)
|
this.errorElement = elBySel('.innerError', this.inputContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
@ -34,21 +35,29 @@ define([ '../console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.input.addEventListener('keydown', this.handleInputKeyDown.bind(this))
|
this.input.addEventListener('keydown', this.handleInputKeyDown.bind(this))
|
||||||
this.input.addEventListener('input', Throttle(this.handleInput.bind(this)))
|
this.input.addEventListener(
|
||||||
|
'input',
|
||||||
|
Throttle(this.handleInput.bind(this))
|
||||||
|
)
|
||||||
|
|
||||||
Helper.makeFlexible(this.input)
|
Helper.makeFlexible(this.input)
|
||||||
this.handleInput()
|
this.handleInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInput(event) {
|
handleInput(event) {
|
||||||
this.charCounter.textContent = `${this.input.value.length} / ${this.input.getAttribute('maxlength')}`
|
this.charCounter.textContent = `${
|
||||||
|
this.input.value.length
|
||||||
|
} / ${this.input.getAttribute('maxlength')}`
|
||||||
this.emit('input')
|
this.emit('input')
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputKeyDown(event) {
|
handleInputKeyDown(event) {
|
||||||
if (EventKey.Enter(event) && !event.shiftKey) {
|
if (EventKey.Enter(event) && !event.shiftKey) {
|
||||||
if (event.isComposing) {
|
if (event.isComposing) {
|
||||||
console.debug('Ui/Input.handleInputKeyDown', 'Ignored Enter key while composing characters.')
|
console.debug(
|
||||||
|
'Ui/Input.handleInputKeyDown',
|
||||||
|
'Ignored Enter key while composing characters.'
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,8 +71,7 @@ define([ '../console'
|
|||||||
if (!parameters.cancel) {
|
if (!parameters.cancel) {
|
||||||
this.emit('submit')
|
this.emit('submit')
|
||||||
}
|
}
|
||||||
}
|
} else if (EventKey.Tab(event)) {
|
||||||
else if (EventKey.Tab(event)) {
|
|
||||||
// prevent leaving the input
|
// prevent leaving the input
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
@ -92,9 +100,7 @@ define([ '../console'
|
|||||||
insertText(text, options) {
|
insertText(text, options) {
|
||||||
this.focus()
|
this.focus()
|
||||||
|
|
||||||
options = Object.assign({ append: true
|
options = Object.assign({ append: true, prepend: false }, options)
|
||||||
, prepend: false
|
|
||||||
}, options)
|
|
||||||
|
|
||||||
if (!(options.append || options.prepend)) {
|
if (!(options.append || options.prepend)) {
|
||||||
// replace
|
// replace
|
||||||
@ -102,11 +108,11 @@ define([ '../console'
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.append) {
|
if (options.append) {
|
||||||
this.input.value += text;
|
this.input.value += text
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.prepend) {
|
if (options.prepend) {
|
||||||
this.input.value = text + this.input.value;
|
this.input.value = text + this.input.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// always position caret at the end
|
// always position caret at the end
|
||||||
@ -119,8 +125,7 @@ define([ '../console'
|
|||||||
inputError(message) {
|
inputError(message) {
|
||||||
if (typeof window.elInnerError === 'function') {
|
if (typeof window.elInnerError === 'function') {
|
||||||
elInnerError(this.inputContainer.firstElementChild, message)
|
elInnerError(this.inputContainer.firstElementChild, message)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.inputContainer.classList.add('formError')
|
this.inputContainer.classList.add('formError')
|
||||||
this.errorElement.textContent = message
|
this.errorElement.textContent = message
|
||||||
elShow(this.errorElement)
|
elShow(this.errorElement)
|
||||||
@ -130,8 +135,7 @@ define([ '../console'
|
|||||||
hideInputError() {
|
hideInputError() {
|
||||||
if (typeof window.elInnerError === 'function') {
|
if (typeof window.elInnerError === 'function') {
|
||||||
elInnerError(this.inputContainer.firstElementChild, false)
|
elInnerError(this.inputContainer.firstElementChild, false)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.inputContainer.classList.remove('formError')
|
this.inputContainer.classList.remove('formError')
|
||||||
this.errorElement.textContent = ''
|
this.errorElement.textContent = ''
|
||||||
elHide(this.errorElement)
|
elHide(this.errorElement)
|
||||||
@ -141,4 +145,4 @@ define([ '../console'
|
|||||||
EventEmitter(Input.prototype)
|
EventEmitter(Input.prototype)
|
||||||
|
|
||||||
return Input
|
return Input
|
||||||
});
|
})
|
||||||
|
@ -11,17 +11,21 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Util'
|
define([
|
||||||
, 'WoltLabSuite/Core/Event/Key'
|
'WoltLabSuite/Core/Dom/Util',
|
||||||
, 'WoltLabSuite/Core/Ui/Suggestion'
|
'WoltLabSuite/Core/Event/Key',
|
||||||
], function (DomUtil, EventKey, Suggestion) {
|
'WoltLabSuite/Core/Ui/Suggestion',
|
||||||
"use strict";
|
], function (DomUtil, EventKey, Suggestion) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ]
|
const DEPENDENCIES = ['UiInput']
|
||||||
class Autocompleter extends Suggestion {
|
class Autocompleter extends Suggestion {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
const elementId = DomUtil.identify(input.input)
|
const elementId = DomUtil.identify(input.input)
|
||||||
const options = { callbackSelect: (_elementId, selection) => this.insertSelection(selection) }
|
const options = {
|
||||||
|
callbackSelect: (_elementId, selection) =>
|
||||||
|
this.insertSelection(selection),
|
||||||
|
}
|
||||||
|
|
||||||
super(elementId, options)
|
super(elementId, options)
|
||||||
|
|
||||||
@ -76,11 +80,9 @@ define([ 'WoltLabSuite/Core/Dom/Util'
|
|||||||
sendCompletions(completions) {
|
sendCompletions(completions) {
|
||||||
this.completions = new Map()
|
this.completions = new Map()
|
||||||
|
|
||||||
const returnValues = completions.map(completion => {
|
const returnValues = completions.map((completion) => {
|
||||||
this.completions.set(++this.completionId, completion)
|
this.completions.set(++this.completionId, completion)
|
||||||
return { label: completion
|
return { label: completion, objectID: this.completionId }
|
||||||
, objectID: this.completionId
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this._ajaxSuccess({ returnValues })
|
this._ajaxSuccess({ returnValues })
|
||||||
@ -89,4 +91,4 @@ define([ 'WoltLabSuite/Core/Dom/Util'
|
|||||||
Autocompleter.DEPENDENCIES = DEPENDENCIES
|
Autocompleter.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Autocompleter
|
return Autocompleter
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Ui' ], function (Ui) {
|
define(['../Ui'], function (Ui) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiMessageStream', 'UiMessageActionDelete' ]
|
const DEPENDENCIES = ['UiMessageStream', 'UiMessageActionDelete']
|
||||||
class Log extends Ui {
|
class Log extends Ui {
|
||||||
constructor(messageStream, messageActionDelete) {
|
constructor(messageStream, messageActionDelete) {
|
||||||
super()
|
super()
|
||||||
@ -32,4 +32,4 @@ define([ '../Ui' ], function (Ui) {
|
|||||||
Log.DEPENDENCIES = DEPENDENCIES
|
Log.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Log
|
return Log
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'Bastelstu.be/Chat/Message', 'Bastelstu.be/PromiseWrap/Ajax', 'Bastelstu.be/PromiseWrap/Ui/Confirmation' ], function (Message, Ajax, Confirmation) {
|
define([
|
||||||
"use strict";
|
'Bastelstu.be/Chat/Message',
|
||||||
|
'Bastelstu.be/PromiseWrap/Ajax',
|
||||||
|
'Bastelstu.be/PromiseWrap/Ui/Confirmation',
|
||||||
|
], function (Message, Ajax, Confirmation) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiMessageStream', 'Message' ]
|
const DEPENDENCIES = ['UiMessageStream', 'Message']
|
||||||
class Delete {
|
class Delete {
|
||||||
constructor(messageStream, message) {
|
constructor(messageStream, message) {
|
||||||
this.messageStream = messageStream
|
this.messageStream = messageStream
|
||||||
@ -26,19 +30,19 @@ define([ 'Bastelstu.be/Chat/Message', 'Bastelstu.be/PromiseWrap/Ajax', 'Bastelst
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindListener({ detail }) {
|
bindListener({ detail }) {
|
||||||
detail.forEach(item => {
|
detail.forEach((item) => {
|
||||||
if (!item) return
|
if (!item) return
|
||||||
|
|
||||||
const { node, message } = item
|
const { node, message } = item
|
||||||
const button = node.querySelector('.jsDeleteButton')
|
const button = node.querySelector('.jsDeleteButton')
|
||||||
if (!button) return
|
if (!button) return
|
||||||
|
|
||||||
button.addEventListener('click', async event => {
|
button.addEventListener('click', async (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
await Confirmation.show({
|
await Confirmation.show({
|
||||||
message: button.dataset.confirmMessageHtml,
|
message: button.dataset.confirmMessageHtml,
|
||||||
messageIsHtml: true
|
messageIsHtml: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.delete(message.messageID)
|
await this.delete(message.messageID)
|
||||||
@ -50,31 +54,31 @@ define([ 'Bastelstu.be/Chat/Message', 'Bastelstu.be/PromiseWrap/Ajax', 'Bastelst
|
|||||||
|
|
||||||
async delete(messageID) {
|
async delete(messageID) {
|
||||||
{
|
{
|
||||||
const payload = { objectIDs: [ messageID ] }
|
const payload = { objectIDs: [messageID] }
|
||||||
|
|
||||||
await Ajax.api(this, payload)
|
await Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const objectType = 'be.bastelstu.chat.messageType.tombstone'
|
const objectType = 'be.bastelstu.chat.messageType.tombstone'
|
||||||
const payload = { messageID
|
const payload = { messageID, userID: null }
|
||||||
, userID: null
|
|
||||||
}
|
|
||||||
const message = this.Message.instance({ objectType, payload })
|
const message = this.Message.instance({ objectType, payload })
|
||||||
message.getMessageType().render(message)
|
message.getMessageType().render(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return {
|
||||||
, ignoreError: true
|
silent: true,
|
||||||
, data: { className: 'chat\\data\\message\\MessageAction'
|
ignoreError: true,
|
||||||
, actionName: 'trash'
|
data: {
|
||||||
}
|
className: 'chat\\data\\message\\MessageAction',
|
||||||
}
|
actionName: 'trash',
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Delete.DEPENDENCIES = DEPENDENCIES
|
Delete.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Delete
|
return Delete
|
||||||
});
|
})
|
||||||
|
@ -11,29 +11,39 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../Helper'
|
define([
|
||||||
, 'WoltLabSuite/Core/Date/Util'
|
'../Helper',
|
||||||
, 'WoltLabSuite/Core/Dom/Change/Listener'
|
'WoltLabSuite/Core/Date/Util',
|
||||||
, 'WoltLabSuite/Core/Language'
|
'WoltLabSuite/Core/Dom/Change/Listener',
|
||||||
, 'WoltLabSuite/Core/User'
|
'WoltLabSuite/Core/Language',
|
||||||
, 'WoltLabSuite/Core/Dom/Traverse'
|
'WoltLabSuite/Core/User',
|
||||||
, '../DataStructure/EventEmitter'
|
'WoltLabSuite/Core/Dom/Traverse',
|
||||||
, '../DataStructure/RedBlackTree/Tree'
|
'../DataStructure/EventEmitter',
|
||||||
], function (Helper, DateUtil, DomChangeListener, Language, User, DOMTraverse, EventEmitter, Tree) {
|
'../DataStructure/RedBlackTree/Tree',
|
||||||
"use strict";
|
], function (
|
||||||
|
Helper,
|
||||||
|
DateUtil,
|
||||||
|
DomChangeListener,
|
||||||
|
Language,
|
||||||
|
User,
|
||||||
|
DOMTraverse,
|
||||||
|
EventEmitter,
|
||||||
|
Tree
|
||||||
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const enableAutoscroll = Symbol('enableAutoscroll')
|
const enableAutoscroll = Symbol('enableAutoscroll')
|
||||||
|
|
||||||
const DEPENDENCIES = [ ]
|
const DEPENDENCIES = []
|
||||||
class MessageStream {
|
class MessageStream {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.stream = elById('chatMessageStream')
|
this.stream = elById('chatMessageStream')
|
||||||
this.scrollContainer = elBySel('.scrollContainer', this.stream)
|
this.scrollContainer = elBySel('.scrollContainer', this.stream)
|
||||||
|
|
||||||
this[enableAutoscroll] = true
|
this[enableAutoscroll] = true
|
||||||
this.lastScrollPosition = undefined
|
this.lastScrollPosition = undefined
|
||||||
this.nodeMap = new WeakMap()
|
this.nodeMap = new WeakMap()
|
||||||
this.positions = new Tree()
|
this.positions = new Tree()
|
||||||
}
|
}
|
||||||
|
|
||||||
get enableAutoscroll() {
|
get enableAutoscroll() {
|
||||||
@ -50,7 +60,11 @@ define([ '../Helper'
|
|||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.scrollContainer.addEventListener('copy', this.onCopy.bind(this))
|
this.scrollContainer.addEventListener('copy', this.onCopy.bind(this))
|
||||||
this.scrollContainer.addEventListener('scroll', Helper.throttle(this.onScroll, 100, this), { passive: true })
|
this.scrollContainer.addEventListener(
|
||||||
|
'scroll',
|
||||||
|
Helper.throttle(this.onScroll, 100, this),
|
||||||
|
{ passive: true }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getDateMarker(date) {
|
getDateMarker(date) {
|
||||||
@ -69,123 +83,131 @@ define([ '../Helper'
|
|||||||
}
|
}
|
||||||
|
|
||||||
ingest(messages) {
|
ingest(messages) {
|
||||||
let scrollTopBefore = this.enableAutoscroll ? 0 : this.scrollContainer.scrollTop
|
let scrollTopBefore = this.enableAutoscroll
|
||||||
|
? 0
|
||||||
|
: this.scrollContainer.scrollTop
|
||||||
let prependedHeight = 0
|
let prependedHeight = 0
|
||||||
|
|
||||||
const ul = elBySel('ul', this.scrollContainer)
|
const ul = elBySel('ul', this.scrollContainer)
|
||||||
const first = ul.firstElementChild
|
const first = ul.firstElementChild
|
||||||
|
|
||||||
const ingested = messages.map((function (item) {
|
const ingested = messages.map(
|
||||||
let currentScrollHeight = 0
|
function (item) {
|
||||||
|
let currentScrollHeight = 0
|
||||||
|
|
||||||
const li = elCreate('li')
|
const li = elCreate('li')
|
||||||
|
|
||||||
// Allow messages types to not render a messages
|
// Allow messages types to not render a messages
|
||||||
// This can be used for status messages like ChatUpdate
|
// This can be used for status messages like ChatUpdate
|
||||||
let fragment
|
let fragment
|
||||||
if ((fragment = item.getMessageType().render(item)) === false) return
|
if ((fragment = item.getMessageType().render(item)) === false) return
|
||||||
|
|
||||||
if (fragment.querySelector(`.userMention[data-user-id="${User.userId}"]`)) li.classList.add('mentioned')
|
if (
|
||||||
|
fragment.querySelector(
|
||||||
|
`.userMention[data-user-id="${User.userId}"]`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
li.classList.add('mentioned')
|
||||||
|
|
||||||
li.appendChild(fragment)
|
li.appendChild(fragment)
|
||||||
|
|
||||||
li.classList.add('chatMessageBoundary')
|
li.classList.add('chatMessageBoundary')
|
||||||
li.setAttribute('id', `message-${item.messageID}`)
|
li.setAttribute('id', `message-${item.messageID}`)
|
||||||
li.dataset.objectType = item.objectType
|
li.dataset.objectType = item.objectType
|
||||||
li.dataset.userId = item.userID
|
li.dataset.userId = item.userID
|
||||||
if (item.isOwnMessage()) li.classList.add('own')
|
if (item.isOwnMessage()) li.classList.add('own')
|
||||||
if (item.isDeleted) li.classList.add('tombstone')
|
if (item.isDeleted) li.classList.add('tombstone')
|
||||||
|
|
||||||
const position = this.positions.insert(item.messageID)
|
const position = this.positions.insert(item.messageID)
|
||||||
if (position[1] !== undefined) {
|
if (position[1] !== undefined) {
|
||||||
const sibling = elById(`message-${position[1]}`)
|
const sibling = elById(`message-${position[1]}`)
|
||||||
if (!sibling) throw new Error('Unreachable')
|
if (!sibling) throw new Error('Unreachable')
|
||||||
|
|
||||||
let nodeBefore, nodeAfter
|
let nodeBefore, nodeAfter
|
||||||
let dateMarkerBetween = false
|
let dateMarkerBetween = false
|
||||||
if (position[0] === 'LEFT') {
|
if (position[0] === 'LEFT') {
|
||||||
nodeAfter = sibling
|
nodeAfter = sibling
|
||||||
nodeBefore = sibling.previousElementSibling
|
|
||||||
|
|
||||||
if (nodeBefore && nodeBefore.classList.contains('dateMarker')) {
|
|
||||||
elRemove(nodeBefore)
|
|
||||||
nodeBefore = sibling.previousElementSibling
|
nodeBefore = sibling.previousElementSibling
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (position[0] === 'RIGHT') {
|
|
||||||
nodeBefore = sibling
|
|
||||||
nodeAfter = sibling.nextElementSibling
|
|
||||||
|
|
||||||
if (nodeAfter && nodeAfter.classList.contains('dateMarker')) {
|
if (nodeBefore && nodeBefore.classList.contains('dateMarker')) {
|
||||||
elRemove(nodeAfter)
|
elRemove(nodeBefore)
|
||||||
|
nodeBefore = sibling.previousElementSibling
|
||||||
|
}
|
||||||
|
} else if (position[0] === 'RIGHT') {
|
||||||
|
nodeBefore = sibling
|
||||||
nodeAfter = sibling.nextElementSibling
|
nodeAfter = sibling.nextElementSibling
|
||||||
|
|
||||||
|
if (nodeAfter && nodeAfter.classList.contains('dateMarker')) {
|
||||||
|
elRemove(nodeAfter)
|
||||||
|
nodeAfter = sibling.nextElementSibling
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Unreachable')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Unreachable')
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageBefore = this.nodeMap.get(nodeBefore)
|
const messageBefore = this.nodeMap.get(nodeBefore)
|
||||||
if (nodeBefore && !messageBefore) throw new Error('Unreachable')
|
if (nodeBefore && !messageBefore) throw new Error('Unreachable')
|
||||||
const messageAfter = this.nodeMap.get(nodeAfter)
|
const messageAfter = this.nodeMap.get(nodeAfter)
|
||||||
if (nodeAfter && !messageAfter) throw new Error('Unreachable')
|
if (nodeAfter && !messageAfter) throw new Error('Unreachable')
|
||||||
|
|
||||||
if (!this.enableAutoscroll && nodeAfter) currentScrollHeight = this.scrollContainer.scrollHeight
|
if (!this.enableAutoscroll && nodeAfter)
|
||||||
|
currentScrollHeight = this.scrollContainer.scrollHeight
|
||||||
|
|
||||||
let context = nodeAfter
|
let context = nodeAfter
|
||||||
if (nodeAfter) nodeAfter.classList.remove('first')
|
if (nodeAfter) nodeAfter.classList.remove('first')
|
||||||
if (messageBefore) {
|
if (messageBefore) {
|
||||||
if (this.onDifferentDays(messageBefore.date, item.date)) {
|
if (this.onDifferentDays(messageBefore.date, item.date)) {
|
||||||
const dateMarker = this.getDateMarker(item.date)
|
const dateMarker = this.getDateMarker(item.date)
|
||||||
ul.insertBefore(dateMarker, nodeAfter)
|
ul.insertBefore(dateMarker, nodeAfter)
|
||||||
|
li.classList.add('first')
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
messageBefore.objectType !== item.objectType ||
|
||||||
|
!item.getMessageType().joinable(messageBefore, item)
|
||||||
|
) {
|
||||||
|
li.classList.add('first')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
li.classList.add('first')
|
li.classList.add('first')
|
||||||
}
|
}
|
||||||
else {
|
if (messageAfter) {
|
||||||
if (messageBefore.objectType !== item.objectType || !item.getMessageType().joinable(messageBefore, item)) {
|
if (this.onDifferentDays(messageAfter.date, item.date)) {
|
||||||
li.classList.add('first')
|
const dateMarker = this.getDateMarker(messageAfter.date)
|
||||||
}
|
ul.insertBefore(dateMarker, nodeAfter)
|
||||||
}
|
context = dateMarker
|
||||||
}
|
|
||||||
else {
|
|
||||||
li.classList.add('first')
|
|
||||||
}
|
|
||||||
if (messageAfter) {
|
|
||||||
if (this.onDifferentDays(messageAfter.date, item.date)) {
|
|
||||||
const dateMarker = this.getDateMarker(messageAfter.date)
|
|
||||||
ul.insertBefore(dateMarker, nodeAfter)
|
|
||||||
context = dateMarker
|
|
||||||
nodeAfter.classList.add('first')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (messageAfter.objectType !== item.objectType || !item.getMessageType().joinable(item, messageAfter)) {
|
|
||||||
nodeAfter.classList.add('first')
|
nodeAfter.classList.add('first')
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
messageAfter.objectType !== item.objectType ||
|
||||||
|
!item.getMessageType().joinable(item, messageAfter)
|
||||||
|
) {
|
||||||
|
nodeAfter.classList.add('first')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.insertBefore(li, context)
|
||||||
|
|
||||||
|
if (!this.enableAutoscroll && nodeAfter) {
|
||||||
|
prependedHeight +=
|
||||||
|
this.scrollContainer.scrollHeight - currentScrollHeight
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
li.classList.add('first')
|
||||||
|
ul.insertBefore(li, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.insertBefore(li, context);
|
this.nodeMap.set(li, item)
|
||||||
|
|
||||||
if (!this.enableAutoscroll && nodeAfter) {
|
return { node: li, message: item }
|
||||||
prependedHeight += this.scrollContainer.scrollHeight - currentScrollHeight
|
}.bind(this)
|
||||||
}
|
)
|
||||||
}
|
|
||||||
else {
|
|
||||||
li.classList.add('first')
|
|
||||||
ul.insertBefore(li, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodeMap.set(li, item)
|
if (ingested.some((item) => item != null)) {
|
||||||
|
|
||||||
return { node: li
|
|
||||||
, message: item
|
|
||||||
}
|
|
||||||
}).bind(this));
|
|
||||||
|
|
||||||
if (ingested.some(item => item != null)) {
|
|
||||||
if (this.enableAutoscroll) {
|
if (this.enableAutoscroll) {
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.stream.classList.add('activity')
|
this.stream.classList.add('activity')
|
||||||
this.scrollContainer.scrollTop = scrollTopBefore + prependedHeight
|
this.scrollContainer.scrollTop = scrollTopBefore + prependedHeight
|
||||||
}
|
}
|
||||||
@ -208,8 +230,11 @@ define([ '../Helper'
|
|||||||
|
|
||||||
let direction = 'down'
|
let direction = 'down'
|
||||||
|
|
||||||
if (this.lastScrollPosition != null && scrollTop < this.lastScrollPosition) {
|
if (
|
||||||
direction = 'up'
|
this.lastScrollPosition != null &&
|
||||||
|
scrollTop < this.lastScrollPosition
|
||||||
|
) {
|
||||||
|
direction = 'up'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (direction === 'up') {
|
if (direction === 'up') {
|
||||||
@ -219,12 +244,10 @@ define([ '../Helper'
|
|||||||
|
|
||||||
if (distanceFromTop <= 7) {
|
if (distanceFromTop <= 7) {
|
||||||
this.emit('reachedTop')
|
this.emit('reachedTop')
|
||||||
}
|
} else if (distanceFromTop <= 300) {
|
||||||
else if (distanceFromTop <= 300) {
|
|
||||||
this.emit('nearTop')
|
this.emit('nearTop')
|
||||||
}
|
}
|
||||||
}
|
} else if (direction === 'down') {
|
||||||
else if (direction === 'down') {
|
|
||||||
if (distanceFromTop > 7) {
|
if (distanceFromTop > 7) {
|
||||||
this.emit('scrollDown')
|
this.emit('scrollDown')
|
||||||
}
|
}
|
||||||
@ -232,8 +255,7 @@ define([ '../Helper'
|
|||||||
if (distanceFromBottom <= 7) {
|
if (distanceFromBottom <= 7) {
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
this.emit('reachedBottom')
|
this.emit('reachedBottom')
|
||||||
}
|
} else if (distanceFromBottom <= 300) {
|
||||||
else if (distanceFromBottom <= 300) {
|
|
||||||
this.emit('nearBottom')
|
this.emit('nearBottom')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,16 +272,19 @@ define([ '../Helper'
|
|||||||
// Get the first and last node in the selection
|
// Get the first and last node in the selection
|
||||||
let originalStart, start, end, originalEnd
|
let originalStart, start, end, originalEnd
|
||||||
start = originalStart = selection.getRangeAt(0).startContainer
|
start = originalStart = selection.getRangeAt(0).startContainer
|
||||||
end = originalEnd = selection.getRangeAt(selection.rangeCount - 1).endContainer
|
end = originalEnd = selection.getRangeAt(selection.rangeCount - 1)
|
||||||
|
.endContainer
|
||||||
|
|
||||||
const startOffset = selection.getRangeAt(0).startOffset
|
const startOffset = selection.getRangeAt(0).startOffset
|
||||||
const endOffset = selection.getRangeAt(selection.rangeCount - 1).endOffset
|
const endOffset = selection.getRangeAt(selection.rangeCount - 1).endOffset
|
||||||
|
|
||||||
// The Traverse module needs nodes of the Element type, the selected elements could be of type Text
|
// The Traverse module needs nodes of the Element type, the selected elements could be of type Text
|
||||||
while (!(start instanceof Element) && start.parentNode) start = start.parentNode
|
while (!(start instanceof Element) && start.parentNode)
|
||||||
|
start = start.parentNode
|
||||||
while (!(end instanceof Element) && end.parentNode) end = end.parentNode
|
while (!(end instanceof Element) && end.parentNode) end = end.parentNode
|
||||||
|
|
||||||
if (!start || !end) throw new Error('Unexpected error, no element nodes in selection')
|
if (!start || !end)
|
||||||
|
throw new Error('Unexpected error, no element nodes in selection')
|
||||||
|
|
||||||
// Try to find the starting li element in the selection
|
// Try to find the starting li element in the selection
|
||||||
if (!start.id || start.id.indexOf('message-') !== 0) {
|
if (!start.id || start.id.indexOf('message-') !== 0) {
|
||||||
@ -272,7 +297,10 @@ define([ '../Helper'
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do not select a message if we selected only a new line
|
// Do not select a message if we selected only a new line
|
||||||
if (originalStart instanceof Text && originalStart.textContent.substring(startOffset) === "") {
|
if (
|
||||||
|
originalStart instanceof Text &&
|
||||||
|
originalStart.textContent.substring(startOffset) === ''
|
||||||
|
) {
|
||||||
start = DOMTraverse.next(start)
|
start = DOMTraverse.next(start)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,15 +317,14 @@ define([ '../Helper'
|
|||||||
end = DOMTraverse.prev(end)
|
end = DOMTraverse.prev(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = [ ]
|
const elements = []
|
||||||
let next = start
|
let next = start
|
||||||
|
|
||||||
do {
|
do {
|
||||||
elements.push(next)
|
elements.push(next)
|
||||||
|
|
||||||
if (next === end) break
|
if (next === end) break
|
||||||
}
|
} while ((next = DOMTraverse.next(next)))
|
||||||
while (next = DOMTraverse.next(next))
|
|
||||||
|
|
||||||
// Only apply our custom formatting when selecting multiple or whole messages
|
// Only apply our custom formatting when selecting multiple or whole messages
|
||||||
if (elements.length === 1) {
|
if (elements.length === 1) {
|
||||||
@ -305,37 +332,59 @@ define([ '../Helper'
|
|||||||
range.setStart(originalStart, startOffset)
|
range.setStart(originalStart, startOffset)
|
||||||
range.setEnd(originalEnd, endOffset)
|
range.setEnd(originalEnd, endOffset)
|
||||||
|
|
||||||
if (!Helper.rangeSpansTextContent(range, start.querySelector('.chatMessage'))) return
|
if (
|
||||||
|
!Helper.rangeSpansTextContent(
|
||||||
|
range,
|
||||||
|
start.querySelector('.chatMessage')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
event.clipboardData.setData('text/plain', elements.map((el, index, arr) => {
|
event.clipboardData.setData(
|
||||||
const message = this.nodeMap.get(el)
|
'text/plain',
|
||||||
|
elements
|
||||||
|
.map((el, index, arr) => {
|
||||||
|
const message = this.nodeMap.get(el)
|
||||||
|
|
||||||
if (el.classList.contains('dateMarker')) return `== ${el.textContent.trim()} ==`
|
if (el.classList.contains('dateMarker'))
|
||||||
|
return `== ${el.textContent.trim()} ==`
|
||||||
|
|
||||||
if (!message) return
|
if (!message) return
|
||||||
|
|
||||||
if (el.classList.contains('tombstone')) {
|
if (el.classList.contains('tombstone')) {
|
||||||
return `[${message.formattedTime}] ${Language.get('chat.messageType.be.bastelstu.chat.messageType.tombstone.message')}`
|
return `[${message.formattedTime}] ${Language.get(
|
||||||
}
|
'chat.messageType.be.bastelstu.chat.messageType.tombstone.message'
|
||||||
|
)}`
|
||||||
|
}
|
||||||
|
|
||||||
const elem = elBySel('.chatMessage', el)
|
const elem = elBySel('.chatMessage', el)
|
||||||
|
|
||||||
let body
|
let body
|
||||||
if (typeof (body = message.getMessageType().renderPlainText(message)) === 'undefined' || body === false) {
|
if (
|
||||||
body = Helper.getTextContent(elem).replace(/\t+/g, '\t') // collapse multiple tabs
|
typeof (body = message
|
||||||
.replace(/ +/g, ' ') // collapse multiple spaces
|
.getMessageType()
|
||||||
.replace(/([\t ]*\n){2,}/g, '\n') // collapse line consisting of tabs, spaces and newlines
|
.renderPlainText(message)) === 'undefined' ||
|
||||||
.replace(/^[\t ]+|[\t ]+$/gm, '') // remove leading and trailing whitespace per line
|
body === false
|
||||||
}
|
) {
|
||||||
|
body = Helper.getTextContent(elem)
|
||||||
|
.replace(/\t+/g, '\t') // collapse multiple tabs
|
||||||
|
.replace(/ +/g, ' ') // collapse multiple spaces
|
||||||
|
.replace(/([\t ]*\n){2,}/g, '\n') // collapse line consisting of tabs, spaces and newlines
|
||||||
|
.replace(/^[\t ]+|[\t ]+$/gm, '') // remove leading and trailing whitespace per line
|
||||||
|
}
|
||||||
|
|
||||||
return `[${message.formattedTime}] <${message.username}> ${body.trim()}`
|
return `[${message.formattedTime}] <${
|
||||||
}).filter(x => x).join('\n'))
|
message.username
|
||||||
|
}> ${body.trim()}`
|
||||||
|
})
|
||||||
|
.filter((x) => x)
|
||||||
|
.join('\n')
|
||||||
|
)
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
console.error('Unable to use the clipboard API')
|
console.error('Unable to use the clipboard API')
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
@ -345,4 +394,4 @@ define([ '../Helper'
|
|||||||
MessageStream.DEPENDENCIES = DEPENDENCIES
|
MessageStream.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return MessageStream
|
return MessageStream
|
||||||
});
|
})
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Ui/Screen' ], function (UiScreen) {
|
define(['WoltLabSuite/Core/Ui/Screen'], function (UiScreen) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const initialized = Symbol('initialized')
|
const initialized = Symbol('initialized')
|
||||||
|
|
||||||
@ -22,10 +22,11 @@ define([ 'WoltLabSuite/Core/Ui/Screen' ], function (UiScreen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
UiScreen.on('screen-md-down', { match: this.enable.bind(this)
|
UiScreen.on('screen-md-down', {
|
||||||
, unmatch: this.disable.bind(this)
|
match: this.enable.bind(this),
|
||||||
, setup: this.init.bind(this)
|
unmatch: this.disable.bind(this),
|
||||||
})
|
setup: this.init.bind(this),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@ -36,19 +37,15 @@ define([ 'WoltLabSuite/Core/Ui/Screen' ], function (UiScreen) {
|
|||||||
this.initQuickSettings()
|
this.initQuickSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {}
|
||||||
|
|
||||||
}
|
disable() {}
|
||||||
|
|
||||||
disable() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
initQuickSettings() {
|
initQuickSettings() {
|
||||||
const navigation = elBySel('#chatQuickSettingsNavigation > ul')
|
const navigation = elBySel('#chatQuickSettingsNavigation > ul')
|
||||||
const quickSettings = elById('chatQuickSettings')
|
const quickSettings = elById('chatQuickSettings')
|
||||||
|
|
||||||
navigation.addEventListener(WCF_CLICK_EVENT, event => {
|
navigation.addEventListener(WCF_CLICK_EVENT, (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
// mimic dropdown behavior
|
// mimic dropdown behavior
|
||||||
@ -57,7 +54,7 @@ define([ 'WoltLabSuite/Core/Ui/Screen' ], function (UiScreen) {
|
|||||||
}, 10)
|
}, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
quickSettings.addEventListener(WCF_CLICK_EVENT, event => {
|
quickSettings.addEventListener(WCF_CLICK_EVENT, (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
@ -67,4 +64,4 @@ define([ 'WoltLabSuite/Core/Ui/Screen' ], function (UiScreen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Mobile
|
return Mobile
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
define(['WoltLabSuite/Core/Language'], function (Language) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore' ]
|
const DEPENDENCIES = ['ProfileStore']
|
||||||
class Notification {
|
class Notification {
|
||||||
constructor(profileStore) {
|
constructor(profileStore) {
|
||||||
this.profileStore = profileStore
|
this.profileStore = profileStore
|
||||||
@ -27,11 +27,14 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange.bind(this))
|
document.addEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
this.onVisibilitychange.bind(this)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get systemSupported() {
|
get systemSupported() {
|
||||||
return "Notification" in window
|
return 'Notification' in window
|
||||||
}
|
}
|
||||||
|
|
||||||
get systemDenied() {
|
get systemDenied() {
|
||||||
@ -40,7 +43,10 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
|
|
||||||
get systemGranted() {
|
get systemGranted() {
|
||||||
if (this.systemDenied) {
|
if (this.systemDenied) {
|
||||||
console.warn('[Notification]', 'System Notifications: permission denied')
|
console.warn(
|
||||||
|
'[Notification]',
|
||||||
|
'System Notifications: permission denied'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.Notification.permission === 'granted'
|
return window.Notification.permission === 'granted'
|
||||||
@ -57,7 +63,7 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
|
|
||||||
ingest(messages) {
|
ingest(messages) {
|
||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
messages.forEach(message => {
|
messages.forEach((message) => {
|
||||||
const body = message.getMessageType().renderPlainText(message)
|
const body = message.getMessageType().renderPlainText(message)
|
||||||
|
|
||||||
if (body === false) return
|
if (body === false) return
|
||||||
@ -70,10 +76,7 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
// The user information is guaranteed to be cached at this point
|
// The user information is guaranteed to be cached at this point
|
||||||
const user = this.profileStore.get(message.userID)
|
const user = this.profileStore.get(message.userID)
|
||||||
const title = Language.get('chat.notification.title', { message })
|
const title = Language.get('chat.notification.title', { message })
|
||||||
const options = { body
|
const options = { body, icon: user.imageUrl, badge: user.imageUrl }
|
||||||
, icon: user.imageUrl
|
|
||||||
, badge: user.imageUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
const notification = new window.Notification(title, options)
|
const notification = new window.Notification(title, options)
|
||||||
|
|
||||||
@ -88,14 +91,14 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
updateBrowserTitle() {
|
updateBrowserTitle() {
|
||||||
if (this.unread > 0) {
|
if (this.unread > 0) {
|
||||||
document.title = `(${this.unread}) ${this.browserTitle}`
|
document.title = `(${this.unread}) ${this.browserTitle}`
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
document.title = this.browserTitle
|
document.title = this.browserTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enableSystemNotifications() {
|
enableSystemNotifications() {
|
||||||
if (!this.systemSupported) return Promise.reject(new Error('Notifications are not supported'))
|
if (!this.systemSupported)
|
||||||
|
return Promise.reject(new Error('Notifications are not supported'))
|
||||||
|
|
||||||
if (this.systemGranted) {
|
if (this.systemGranted) {
|
||||||
this.systemEnabled = true
|
this.systemEnabled = true
|
||||||
@ -104,13 +107,12 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
window.Notification.requestPermission(permission => {
|
window.Notification.requestPermission((permission) => {
|
||||||
this.systemEnabled = permission === 'granted'
|
this.systemEnabled = permission === 'granted'
|
||||||
|
|
||||||
if (this.systemEnabled) {
|
if (this.systemEnabled) {
|
||||||
resolve()
|
resolve()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
reject(new Error(permission))
|
reject(new Error(permission))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -124,4 +126,4 @@ define([ 'WoltLabSuite/Core/Language' ], function (Language) {
|
|||||||
Notification.DEPENDENCIES = DEPENDENCIES
|
Notification.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Notification
|
return Notification
|
||||||
});
|
})
|
||||||
|
@ -11,28 +11,34 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiMessageStream' ]
|
const DEPENDENCIES = ['UiMessageStream']
|
||||||
class ReadMarker {
|
class ReadMarker {
|
||||||
constructor(messageStream) {
|
constructor(messageStream) {
|
||||||
this.messageStream = messageStream
|
this.messageStream = messageStream
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange.bind(this))
|
document.addEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
this.onVisibilitychange.bind(this)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibilitychange() {
|
onVisibilitychange() {
|
||||||
if (document.hidden) {
|
if (document.hidden) {
|
||||||
const ul = elBySel('ul', this.messageStream.stream)
|
const ul = elBySel('ul', this.messageStream.stream)
|
||||||
let lc = ul.lastElementChild
|
let lc = ul.lastElementChild
|
||||||
|
|
||||||
// delete previous markers
|
// delete previous markers
|
||||||
Array.prototype.forEach.call(document.querySelectorAll('.readMarker'), marker => {
|
Array.prototype.forEach.call(
|
||||||
marker.classList.remove('readMarker')
|
document.querySelectorAll('.readMarker'),
|
||||||
})
|
(marker) => {
|
||||||
|
marker.classList.remove('readMarker')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (lc) {
|
if (lc) {
|
||||||
lc.classList.add('readMarker')
|
lc.classList.add('readMarker')
|
||||||
@ -43,4 +49,4 @@ define([ ], function () {
|
|||||||
ReadMarker.DEPENDENCIES = DEPENDENCIES
|
ReadMarker.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return ReadMarker
|
return ReadMarker
|
||||||
});
|
})
|
||||||
|
@ -11,23 +11,27 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiSettingsButton' ]
|
const DEPENDENCIES = ['UiSettingsButton']
|
||||||
class Settings {
|
class Settings {
|
||||||
constructor(modules) {
|
constructor(modules) {
|
||||||
this.modules = modules
|
this.modules = modules
|
||||||
this.buttons = Array.from(elBySelAll('#chatQuickSettingsNavigation .button[data-module]'))
|
this.buttons = Array.from(
|
||||||
|
elBySelAll('#chatQuickSettingsNavigation .button[data-module]')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.buttons.forEach(element => {
|
this.buttons.forEach((element) => {
|
||||||
this.modules[element.dataset.module.replace(/\./g, '-')].instance(element).bootstrap()
|
this.modules[element.dataset.module.replace(/\./g, '-')]
|
||||||
|
.instance(element)
|
||||||
|
.bootstrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Settings.DEPENDENCIES = DEPENDENCIES
|
Settings.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Settings
|
return Settings
|
||||||
});
|
})
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './ToggleButton' ], function (ToggleButton) {
|
define(['./ToggleButton'], function (ToggleButton) {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiMessageStream' ].concat(ToggleButton.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['UiMessageStream'].concat(
|
||||||
|
ToggleButton.DEPENDENCIES || []
|
||||||
|
)
|
||||||
class AutoscrollButton extends ToggleButton {
|
class AutoscrollButton extends ToggleButton {
|
||||||
constructor(element, messageStream, ...superDeps) {
|
constructor(element, messageStream, ...superDeps) {
|
||||||
super(element, true, undefined, ...superDeps)
|
super(element, true, undefined, ...superDeps)
|
||||||
@ -40,4 +42,4 @@ define([ './ToggleButton' ], function (ToggleButton) {
|
|||||||
AutoscrollButton.DEPENDENCIES = DEPENDENCIES
|
AutoscrollButton.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return AutoscrollButton
|
return AutoscrollButton
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ ]
|
const DEPENDENCIES = []
|
||||||
class Button {
|
class Button {
|
||||||
constructor(element) {
|
constructor(element) {
|
||||||
if (!element || !element instanceof Element) throw new Error('No DOM element provided')
|
if (!element || !element instanceof Element)
|
||||||
|
throw new Error('No DOM element provided')
|
||||||
|
|
||||||
this.element = element
|
this.element = element
|
||||||
}
|
}
|
||||||
@ -33,4 +34,4 @@ define([ ], function () {
|
|||||||
Button.DEPENDENCIES = DEPENDENCIES
|
Button.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return Button
|
return Button
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,17 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './ToggleButton' ], function (ToggleButton) {
|
define(['./ToggleButton'], function (ToggleButton) {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
class FullscreenButton extends ToggleButton {
|
class FullscreenButton extends ToggleButton {
|
||||||
constructor(element, ...superDeps) {
|
constructor(element, ...superDeps) {
|
||||||
super(element, false, 'Bastelstu.be/Chat/Ui/Settings/FullscreenButton', ...superDeps)
|
super(
|
||||||
|
element,
|
||||||
|
false,
|
||||||
|
'Bastelstu.be/Chat/Ui/Settings/FullscreenButton',
|
||||||
|
...superDeps
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
@ -31,4 +36,4 @@ define([ './ToggleButton' ], function (ToggleButton) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return FullscreenButton
|
return FullscreenButton
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,20 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './ToggleButton' ], function (ToggleButton) {
|
define(['./ToggleButton'], function (ToggleButton) {
|
||||||
'use strict';
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiNotification' ].concat(ToggleButton.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['UiNotification'].concat(
|
||||||
|
ToggleButton.DEPENDENCIES || []
|
||||||
|
)
|
||||||
class NotificationsButton extends ToggleButton {
|
class NotificationsButton extends ToggleButton {
|
||||||
constructor(element, notification, ...superDeps) {
|
constructor(element, notification, ...superDeps) {
|
||||||
super(element, false, 'Bastelstu.be/Chat/Ui/Settings/NotificationsButton', ...superDeps)
|
super(
|
||||||
|
element,
|
||||||
|
false,
|
||||||
|
'Bastelstu.be/Chat/Ui/Settings/NotificationsButton',
|
||||||
|
...superDeps
|
||||||
|
)
|
||||||
|
|
||||||
this.notification = notification
|
this.notification = notification
|
||||||
}
|
}
|
||||||
@ -26,14 +33,17 @@ define([ './ToggleButton' ], function (ToggleButton) {
|
|||||||
super.bootstrap()
|
super.bootstrap()
|
||||||
|
|
||||||
// Hide the button if notifications are not supported or the permission has been denied
|
// Hide the button if notifications are not supported or the permission has been denied
|
||||||
if (!this.notification.systemSupported || this.notification.systemDenied) {
|
if (
|
||||||
|
!this.notification.systemSupported ||
|
||||||
|
this.notification.systemDenied
|
||||||
|
) {
|
||||||
elRemove(this.element.closest('li'))
|
elRemove(this.element.closest('li'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
super.enable()
|
super.enable()
|
||||||
this.notification.enableSystemNotifications().catch(error => {
|
this.notification.enableSystemNotifications().catch((error) => {
|
||||||
this.disable()
|
this.disable()
|
||||||
|
|
||||||
if (this.notification.systemDenied) elRemove(this.element)
|
if (this.notification.systemDenied) elRemove(this.element)
|
||||||
@ -48,4 +58,4 @@ define([ './ToggleButton' ], function (ToggleButton) {
|
|||||||
NotificationsButton.DEPENDENCIES = DEPENDENCIES
|
NotificationsButton.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return NotificationsButton
|
return NotificationsButton
|
||||||
});
|
})
|
||||||
|
@ -11,12 +11,13 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './ToggleButton'
|
define(['./ToggleButton', 'WoltLabSuite/Core/Ui/Screen'], function (
|
||||||
, 'WoltLabSuite/Core/Ui/Screen'
|
ToggleButton,
|
||||||
], function (ToggleButton, UiScreen) {
|
UiScreen
|
||||||
'use strict';
|
) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ].concat(ToggleButton.DEPENDENCIES || [ ])
|
const DEPENDENCIES = ['UiInput'].concat(ToggleButton.DEPENDENCIES || [])
|
||||||
class SmiliesButton extends ToggleButton {
|
class SmiliesButton extends ToggleButton {
|
||||||
constructor(element, input, ...superDeps) {
|
constructor(element, input, ...superDeps) {
|
||||||
super(element, false, undefined, ...superDeps)
|
super(element, false, undefined, ...superDeps)
|
||||||
@ -25,7 +26,7 @@ define([ './ToggleButton'
|
|||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.container = elById('smileyPickerContainer')
|
this.container = elById('smileyPickerContainer')
|
||||||
|
|
||||||
// Remove this button if smileys are disabled
|
// Remove this button if smileys are disabled
|
||||||
if (!this.container) {
|
if (!this.container) {
|
||||||
@ -37,7 +38,11 @@ define([ './ToggleButton'
|
|||||||
// Initialize the smiley picker tab menu
|
// Initialize the smiley picker tab menu
|
||||||
$('.messageTabMenu').messageTabMenu()
|
$('.messageTabMenu').messageTabMenu()
|
||||||
|
|
||||||
$('#smilies-text').on('mousedown', '.jsSmiley', this.insertSmiley.bind(this))
|
$('#smilies-text').on(
|
||||||
|
'mousedown',
|
||||||
|
'.jsSmiley',
|
||||||
|
this.insertSmiley.bind(this)
|
||||||
|
)
|
||||||
this.closeButton.addEventListener('mousedown', this.disable.bind(this))
|
this.closeButton.addEventListener('mousedown', this.disable.bind(this))
|
||||||
|
|
||||||
// Start in desktop mode
|
// Start in desktop mode
|
||||||
@ -48,9 +53,9 @@ define([ './ToggleButton'
|
|||||||
|
|
||||||
// Setup media queries
|
// Setup media queries
|
||||||
UiScreen.on('screen-md-down', {
|
UiScreen.on('screen-md-down', {
|
||||||
match: this.enableMobile.bind(this),
|
match: this.enableMobile.bind(this),
|
||||||
unmatch: this.disableMobile.bind(this),
|
unmatch: this.disableMobile.bind(this),
|
||||||
setup: this.setupMobile.bind(this)
|
setup: this.setupMobile.bind(this),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +68,23 @@ define([ './ToggleButton'
|
|||||||
*/
|
*/
|
||||||
setupMobile() {
|
setupMobile() {
|
||||||
this.shadowToggleButton = document.createElement('span')
|
this.shadowToggleButton = document.createElement('span')
|
||||||
this.shadowToggleButton.classList.add('smiliesToggleMobileButton', 'button', 'small')
|
this.shadowToggleButton.classList.add(
|
||||||
this.shadowToggleButton.innerHTML = '<span class="icon icon24 fa-smile-o"></span>'
|
'smiliesToggleMobileButton',
|
||||||
this.shadowToggleButton.addEventListener('mousedown', this.onClick.bind(this))
|
'button',
|
||||||
|
'small'
|
||||||
|
)
|
||||||
|
this.shadowToggleButton.innerHTML =
|
||||||
|
'<span class="icon icon24 fa-smile-o"></span>'
|
||||||
|
this.shadowToggleButton.addEventListener(
|
||||||
|
'mousedown',
|
||||||
|
this.onClick.bind(this)
|
||||||
|
)
|
||||||
|
|
||||||
const shadowContainer = elBySel('#chatInputContainer > div')
|
const shadowContainer = elBySel('#chatInputContainer > div')
|
||||||
shadowContainer.insertBefore(this.shadowToggleButton, shadowContainer.firstChild)
|
shadowContainer.insertBefore(
|
||||||
|
this.shadowToggleButton,
|
||||||
|
shadowContainer.firstChild
|
||||||
|
)
|
||||||
|
|
||||||
this.enableMobile()
|
this.enableMobile()
|
||||||
}
|
}
|
||||||
@ -156,4 +172,4 @@ define([ './ToggleButton'
|
|||||||
SmiliesButton.DEPENDENCIES = DEPENDENCIES
|
SmiliesButton.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return SmiliesButton
|
return SmiliesButton
|
||||||
});
|
})
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Button'
|
define([
|
||||||
, '../../LocalStorage'
|
'./Button',
|
||||||
, '../../DataStructure/EventEmitter'
|
'../../LocalStorage',
|
||||||
], function (Button, LocalStorage, EventEmitter) {
|
'../../DataStructure/EventEmitter',
|
||||||
'use strict';
|
], function (Button, LocalStorage, EventEmitter) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ ].concat(Button.DEPENDENCIES || [ ])
|
const DEPENDENCIES = [].concat(Button.DEPENDENCIES || [])
|
||||||
class ToggleButton extends Button {
|
class ToggleButton extends Button {
|
||||||
constructor(element, defaultState, storageKey, ...superDeps) {
|
constructor(element, defaultState, storageKey, ...superDeps) {
|
||||||
super(element, ...superDeps)
|
super(element, ...superDeps)
|
||||||
@ -38,8 +39,7 @@ define([ './Button'
|
|||||||
|
|
||||||
if (this.defaultState) {
|
if (this.defaultState) {
|
||||||
this.enable()
|
this.enable()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.disable()
|
this.disable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,8 +69,7 @@ define([ './Button'
|
|||||||
|
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this.disable()
|
this.disable()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.enable()
|
this.enable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,4 +78,4 @@ define([ './Button'
|
|||||||
ToggleButton.DEPENDENCIES = DEPENDENCIES
|
ToggleButton.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return ToggleButton
|
return ToggleButton
|
||||||
});
|
})
|
||||||
|
@ -11,18 +11,21 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Traverse' ], function (Traverse) {
|
define(['WoltLabSuite/Core/Dom/Traverse'], function (Traverse) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Topic {
|
class Topic {
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
elBySelAll('.chatRoomTopic', document, function (element) {
|
elBySelAll('.chatRoomTopic', document, function (element) {
|
||||||
elBySel('.jsDismissRoomTopicButton', element).addEventListener('click', function (event) {
|
elBySel('.jsDismissRoomTopicButton', element).addEventListener(
|
||||||
elRemove(element)
|
'click',
|
||||||
})
|
function (event) {
|
||||||
|
elRemove(element)
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Topic
|
return Topic
|
||||||
});
|
})
|
||||||
|
@ -11,18 +11,23 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Traverse'
|
define([
|
||||||
, 'WoltLabSuite/Core/Dom/Util'
|
'WoltLabSuite/Core/Dom/Traverse',
|
||||||
, 'WoltLabSuite/Core/Ui/Dropdown/Simple'
|
'WoltLabSuite/Core/Dom/Util',
|
||||||
], function (DomTraverse, DomUtil, SimpleDropdown) {
|
'WoltLabSuite/Core/Ui/Dropdown/Simple',
|
||||||
"use strict";
|
], function (DomTraverse, DomUtil, SimpleDropdown) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'ProfileStore', 'Template.UserListDropdownMenuItems', 'bottle' ]
|
const DEPENDENCIES = [
|
||||||
|
'ProfileStore',
|
||||||
|
'Template.UserListDropdownMenuItems',
|
||||||
|
'bottle',
|
||||||
|
]
|
||||||
class UserActionDropdownHandler {
|
class UserActionDropdownHandler {
|
||||||
constructor(profiles, dropdownTemplate, bottle) {
|
constructor(profiles, dropdownTemplate, bottle) {
|
||||||
this.profiles = profiles
|
this.profiles = profiles
|
||||||
this.dropdownTemplate = dropdownTemplate
|
this.dropdownTemplate = dropdownTemplate
|
||||||
this.bottle = bottle
|
this.bottle = bottle
|
||||||
|
|
||||||
this.container = elById('main')
|
this.container = elById('main')
|
||||||
}
|
}
|
||||||
@ -32,7 +37,15 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClick(event) {
|
onClick(event) {
|
||||||
const userElement = event.target.classList.contains('jsUserActionDropdown') ? event.target : DomTraverse.parentByClass(event.target, 'jsUserActionDropdown', this.container)
|
const userElement = event.target.classList.contains(
|
||||||
|
'jsUserActionDropdown'
|
||||||
|
)
|
||||||
|
? event.target
|
||||||
|
: DomTraverse.parentByClass(
|
||||||
|
event.target,
|
||||||
|
'jsUserActionDropdown',
|
||||||
|
this.container
|
||||||
|
)
|
||||||
|
|
||||||
if (!userElement) return
|
if (!userElement) return
|
||||||
|
|
||||||
@ -46,23 +59,37 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
|
|
||||||
// Note: We would usually use firstElementChild here, but this
|
// Note: We would usually use firstElementChild here, but this
|
||||||
// is not supported in Safari and Edge
|
// is not supported in Safari and Edge
|
||||||
const dropdown = DomUtil.createFragmentFromHtml(this.dropdownTemplate.fetch({ user })).querySelector('*')
|
const dropdown = DomUtil.createFragmentFromHtml(
|
||||||
|
this.dropdownTemplate.fetch({ user })
|
||||||
|
).querySelector('*')
|
||||||
|
|
||||||
Array.from(elBySelAll('[data-module]', dropdown)).forEach(element => {
|
Array.from(elBySelAll('[data-module]', dropdown)).forEach((element) => {
|
||||||
const moduleName = element.dataset.module
|
const moduleName = element.dataset.module
|
||||||
let userAction
|
let userAction
|
||||||
if (!this.bottle.container.UserAction || (userAction = this.bottle.container.UserAction[`${moduleName.replace(/\./g, '-')}`]) == null) {
|
if (
|
||||||
this.bottle.factory(`UserAction.${moduleName.replace(/\./g, '-')}`, _ => {
|
!this.bottle.container.UserAction ||
|
||||||
const UserAction = require(moduleName)
|
(userAction = this.bottle.container.UserAction[
|
||||||
const deps = this.bottle.digest(UserAction.DEPENDENCIES || [])
|
`${moduleName.replace(/\./g, '-')}`
|
||||||
|
]) == null
|
||||||
|
) {
|
||||||
|
this.bottle.factory(
|
||||||
|
`UserAction.${moduleName.replace(/\./g, '-')}`,
|
||||||
|
(_) => {
|
||||||
|
const UserAction = require(moduleName)
|
||||||
|
const deps = this.bottle.digest(UserAction.DEPENDENCIES || [])
|
||||||
|
|
||||||
return new UserAction(...deps)
|
return new UserAction(...deps)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
userAction = this.bottle.container.UserAction[`${moduleName.replace(/\./g, '-')}`]
|
userAction = this.bottle.container.UserAction[
|
||||||
|
`${moduleName.replace(/\./g, '-')}`
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
element.addEventListener(WCF_CLICK_EVENT, (event) => userAction.onClick(user, event))
|
element.addEventListener(WCF_CLICK_EVENT, (event) =>
|
||||||
|
userAction.onClick(user, event)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
SimpleDropdown.initFragment(userElement, dropdown)
|
SimpleDropdown.initFragment(userElement, dropdown)
|
||||||
@ -77,4 +104,4 @@ define([ 'WoltLabSuite/Core/Dom/Traverse'
|
|||||||
UserActionDropdownHandler.DEPENDENCIES = DEPENDENCIES
|
UserActionDropdownHandler.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return UserActionDropdownHandler
|
return UserActionDropdownHandler
|
||||||
});
|
})
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
class Action {
|
class Action {
|
||||||
constructor() { }
|
constructor() {}
|
||||||
|
|
||||||
onClick(userID, event) { }
|
onClick(userID, event) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Action
|
return Action
|
||||||
});
|
})
|
||||||
|
@ -11,17 +11,15 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../../console'
|
define(['../../console', './Action'], function (console, Action) {
|
||||||
, './Action'
|
'use strict'
|
||||||
], function (console, Action) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ]
|
const DEPENDENCIES = ['UiInput']
|
||||||
class BanAction extends Action {
|
class BanAction extends Action {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.input = input
|
this.input = input
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(user, event) {
|
onClick(user, event) {
|
||||||
@ -35,7 +33,7 @@ define([ '../../console'
|
|||||||
|
|
||||||
this.input.insertText(command, { append: false, prepend: true })
|
this.input.insertText(command, { append: false, prepend: true })
|
||||||
this.input.focus()
|
this.input.focus()
|
||||||
setTimeout(_ => {
|
setTimeout((_) => {
|
||||||
this.input.emit('autocomplete')
|
this.input.emit('autocomplete')
|
||||||
}, 1)
|
}, 1)
|
||||||
}
|
}
|
||||||
@ -43,4 +41,4 @@ define([ '../../console'
|
|||||||
BanAction.DEPENDENCIES = DEPENDENCIES
|
BanAction.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return BanAction
|
return BanAction
|
||||||
});
|
})
|
||||||
|
@ -11,17 +11,15 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ '../../console'
|
define(['../../console', './Action'], function (console, Action) {
|
||||||
, './Action'
|
'use strict'
|
||||||
], function (console, Action) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ]
|
const DEPENDENCIES = ['UiInput']
|
||||||
class MuteAction extends Action {
|
class MuteAction extends Action {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.input = input
|
this.input = input
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(user, event) {
|
onClick(user, event) {
|
||||||
@ -35,7 +33,7 @@ define([ '../../console'
|
|||||||
|
|
||||||
this.input.insertText(command, { append: false, prepend: true })
|
this.input.insertText(command, { append: false, prepend: true })
|
||||||
this.input.focus()
|
this.input.focus()
|
||||||
setTimeout(_ => {
|
setTimeout((_) => {
|
||||||
this.input.emit('autocomplete')
|
this.input.emit('autocomplete')
|
||||||
}, 1)
|
}, 1)
|
||||||
}
|
}
|
||||||
@ -43,4 +41,4 @@ define([ '../../console'
|
|||||||
MuteAction.DEPENDENCIES = DEPENDENCIES
|
MuteAction.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return MuteAction
|
return MuteAction
|
||||||
});
|
})
|
||||||
|
@ -11,15 +11,15 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ './Action', '../../console' ], function (Action, console) {
|
define(['./Action', '../../console'], function (Action, console) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiInput' ]
|
const DEPENDENCIES = ['UiInput']
|
||||||
class WhisperAction extends Action {
|
class WhisperAction extends Action {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.input = input
|
this.input = input
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(user, event) {
|
onClick(user, event) {
|
||||||
@ -38,4 +38,4 @@ define([ './Action', '../../console' ], function (Action, console) {
|
|||||||
WhisperAction.DEPENDENCIES = DEPENDENCIES
|
WhisperAction.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return WhisperAction
|
return WhisperAction
|
||||||
});
|
})
|
||||||
|
@ -11,28 +11,26 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/Dom/Util' ], function (DomUtil) {
|
define(['WoltLabSuite/Core/Dom/Util'], function (DomUtil) {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'Template.UserList' ]
|
const DEPENDENCIES = ['Template.UserList']
|
||||||
class UserList {
|
class UserList {
|
||||||
constructor(userListTemplate) {
|
constructor(userListTemplate) {
|
||||||
this.userListTemplate = userListTemplate
|
this.userListTemplate = userListTemplate
|
||||||
this.chatUserList = elById('chatUserList')
|
this.chatUserList = elById('chatUserList')
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
render(users) {
|
render(users) {
|
||||||
users.sort((a, b) => a.username.localeCompare(b.username))
|
users.sort((a, b) => a.username.localeCompare(b.username))
|
||||||
const html = this.userListTemplate.fetch({ users })
|
const html = this.userListTemplate.fetch({ users })
|
||||||
const fragment = DomUtil.createFragmentFromHtml(html)
|
const fragment = DomUtil.createFragmentFromHtml(html)
|
||||||
|
|
||||||
// Replace the current user list with the new one
|
// Replace the current user list with the new one
|
||||||
const currentList = elBySel('#chatUserList > .boxContent > ul')
|
const currentList = elBySel('#chatUserList > .boxContent > ul')
|
||||||
const parentNode = currentList.parentNode
|
const parentNode = currentList.parentNode
|
||||||
parentNode.removeChild(currentList)
|
parentNode.removeChild(currentList)
|
||||||
parentNode.appendChild(fragment)
|
parentNode.appendChild(fragment)
|
||||||
}
|
}
|
||||||
@ -40,4 +38,4 @@ define([ 'WoltLabSuite/Core/Dom/Util' ], function (DomUtil) {
|
|||||||
UserList.DEPENDENCIES = DEPENDENCIES
|
UserList.DEPENDENCIES = DEPENDENCIES
|
||||||
|
|
||||||
return UserList
|
return UserList
|
||||||
});
|
})
|
||||||
|
@ -11,11 +11,12 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ 'WoltLabSuite/Core/User'
|
define([
|
||||||
, 'WoltLabSuite/Core/StringUtil'
|
'WoltLabSuite/Core/User',
|
||||||
, './Helper'
|
'WoltLabSuite/Core/StringUtil',
|
||||||
], function (CoreUser, StringUtil, Helper) {
|
'./Helper',
|
||||||
"use strict";
|
], function (CoreUser, StringUtil, Helper) {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
const u = Symbol('user')
|
const u = Symbol('user')
|
||||||
|
|
||||||
@ -25,15 +26,16 @@ define([ 'WoltLabSuite/Core/User'
|
|||||||
class User {
|
class User {
|
||||||
constructor(user) {
|
constructor(user) {
|
||||||
this[u] = Helper.deepFreeze(user)
|
this[u] = Helper.deepFreeze(user)
|
||||||
|
|
||||||
Object.getOwnPropertyNames(this[u]).forEach(key => {
|
Object.getOwnPropertyNames(this[u]).forEach((key) => {
|
||||||
if (this[key]) {
|
if (this[key]) {
|
||||||
throw new Error('Attempting to override existing property')
|
throw new Error('Attempting to override existing property')
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(this, key, { value: this[u][key]
|
Object.defineProperty(this, key, {
|
||||||
, enumerable: true
|
value: this[u][key],
|
||||||
})
|
enumerable: true,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,28 +44,36 @@ define([ 'WoltLabSuite/Core/User'
|
|||||||
if (this.color1 === null && this.color2 === null) return this.username
|
if (this.color1 === null && this.color2 === null) return this.username
|
||||||
|
|
||||||
// Single color
|
// Single color
|
||||||
if (this.color1 === this.color2) return `<span style="color: ${Helper.intToRGBHex(this.color1)};">${StringUtil.escapeHTML(this.username)}</span>`
|
if (this.color1 === this.color2)
|
||||||
|
return `<span style="color: ${Helper.intToRGBHex(
|
||||||
|
this.color1
|
||||||
|
)};">${StringUtil.escapeHTML(this.username)}</span>`
|
||||||
|
|
||||||
// Gradient
|
// Gradient
|
||||||
const r1 = (this.color1 >> 16) & 0xFF
|
const r1 = (this.color1 >> 16) & 0xff
|
||||||
const r2 = (this.color2 >> 16) & 0xFF
|
const r2 = (this.color2 >> 16) & 0xff
|
||||||
const g1 = (this.color1 >> 8) & 0xFF
|
const g1 = (this.color1 >> 8) & 0xff
|
||||||
const g2 = (this.color2 >> 8) & 0xFF
|
const g2 = (this.color2 >> 8) & 0xff
|
||||||
const b1 = this.color1 & 0xFF
|
const b1 = this.color1 & 0xff
|
||||||
const b2 = this.color2 & 0xFF
|
const b2 = this.color2 & 0xff
|
||||||
|
|
||||||
const steps = this.username.length - 1
|
const steps = this.username.length - 1
|
||||||
const r = (r1 - r2) / steps
|
const r = (r1 - r2) / steps
|
||||||
const g = (g1 - g2) / steps
|
const g = (g1 - g2) / steps
|
||||||
const b = (b1 - b2) / steps
|
const b = (b1 - b2) / steps
|
||||||
|
|
||||||
return this[u].username.split('').map((letter, index) => {
|
return this[u].username
|
||||||
const R = Math.round(r1 - index * r)
|
.split('')
|
||||||
const G = Math.round(g1 - index * g)
|
.map((letter, index) => {
|
||||||
const B = Math.round(b1 - index * b)
|
const R = Math.round(r1 - index * r)
|
||||||
|
const G = Math.round(g1 - index * g)
|
||||||
|
const B = Math.round(b1 - index * b)
|
||||||
|
|
||||||
return `<span style="color: rgb(${R}, ${G}, ${B})">${StringUtil.escapeHTML(letter)}</span>`
|
return `<span style="color: rgb(${R}, ${G}, ${B})">${StringUtil.escapeHTML(
|
||||||
}).join('')
|
letter
|
||||||
|
)}</span>`
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
get self() {
|
get self() {
|
||||||
@ -71,11 +81,7 @@ define([ 'WoltLabSuite/Core/User'
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getGuest(username) {
|
static getGuest(username) {
|
||||||
const payload = { username
|
const payload = { username, userID: null, color1: null, color2: null }
|
||||||
, userID: null
|
|
||||||
, color1: null
|
|
||||||
, color2: null
|
|
||||||
}
|
|
||||||
|
|
||||||
return new User(payload)
|
return new User(payload)
|
||||||
}
|
}
|
||||||
@ -90,4 +96,4 @@ define([ 'WoltLabSuite/Core/User'
|
|||||||
}
|
}
|
||||||
|
|
||||||
return User
|
return User
|
||||||
});
|
})
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
* or later of the General Public License.
|
* or later of the General Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define([ ], function () {
|
define([], function () {
|
||||||
"use strict";
|
'use strict'
|
||||||
|
|
||||||
const start = Date.now()
|
const start = Date.now()
|
||||||
let last = start
|
let last = start
|
||||||
|
|
||||||
const group = function () {
|
const group = function () {
|
||||||
if (window.console.group) window.console.group()
|
if (window.console.group) window.console.group()
|
||||||
@ -46,13 +46,12 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const debug = function (handler, ...args) {
|
const debug = function (handler, ...args) {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const time = [ (now - start), `\t+${(now - last)}ms\t` ]
|
const time = [now - start, `\t+${now - last}ms\t`]
|
||||||
|
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
println('debug', ...time, `[${handler}]\t`, ...args)
|
println('debug', ...time, `[${handler}]\t`, ...args)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
println('debug', ...time, handler)
|
println('debug', ...time, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ define([ ], function () {
|
|||||||
|
|
||||||
const debugException = function (error) {
|
const debugException = function (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
let message = `[${error.name}] „${error.message}“ in ${error.fileName} on line ${error.lineNumber}\n`
|
let message = `[${error.name}] „${error.message}“ in ${error.fileName} on line ${error.lineNumber}\n`
|
||||||
|
|
||||||
if (error.stack) {
|
if (error.stack) {
|
||||||
message += 'Stacktrace:\n'
|
message += 'Stacktrace:\n'
|
||||||
@ -69,8 +68,7 @@ define([ ], function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println('error', message)
|
println('error', message)
|
||||||
}
|
} else if (error.code && error.message) {
|
||||||
else if (error.code && error.message) {
|
|
||||||
debugAjaxException(error)
|
debugAjaxException(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,20 +77,21 @@ define([ ], function () {
|
|||||||
groupCollapsed()
|
groupCollapsed()
|
||||||
let details = `[${error.code}] ${error.message}`
|
let details = `[${error.code}] ${error.message}`
|
||||||
|
|
||||||
const br2nl = (string) => string.split('\n')
|
const br2nl = (string) =>
|
||||||
.map(line => line.replace(/<br\s*\/?>$/i, ''))
|
string
|
||||||
.join('\n')
|
.split('\n')
|
||||||
|
.map((line) => line.replace(/<br\s*\/?>$/i, ''))
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
if (error.stacktrace) {
|
if (error.stacktrace) {
|
||||||
details += `\nStacktrace:\n${br2nl(error.stacktrace)}`
|
details += `\nStacktrace:\n${br2nl(error.stacktrace)}`
|
||||||
}
|
} else if (error.exceptionID) {
|
||||||
else if (error.exceptionID) {
|
|
||||||
details += `\nException ID: ${error.exceptionID}`
|
details += `\nException ID: ${error.exceptionID}`
|
||||||
}
|
}
|
||||||
|
|
||||||
println('debug', details)
|
println('debug', details)
|
||||||
|
|
||||||
error.previous.forEach(previous => {
|
error.previous.forEach((previous) => {
|
||||||
let details = ''
|
let details = ''
|
||||||
|
|
||||||
group()
|
group()
|
||||||
@ -103,17 +102,18 @@ define([ ], function () {
|
|||||||
println('debug', details)
|
println('debug', details)
|
||||||
})
|
})
|
||||||
|
|
||||||
error.previous.forEach(_ => groupEnd())
|
error.previous.forEach((_) => groupEnd())
|
||||||
groupEnd()
|
groupEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
return { log
|
return {
|
||||||
, warn
|
log,
|
||||||
, error
|
warn,
|
||||||
, debug
|
error,
|
||||||
, debugException
|
debug,
|
||||||
, group
|
debugException,
|
||||||
, groupCollapsed
|
group,
|
||||||
, groupEnd
|
groupCollapsed,
|
||||||
}
|
groupEnd,
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user