fwtoolkit 0.1.0-alpha.6 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/alerts.css +7 -0
- package/css/dialog.css +62 -0
- package/css/overview_menu.css +7 -0
- package/dist/basic.d.ts +49 -36
- package/dist/basic.d.ts.map +1 -1
- package/dist/basic.js +58 -39
- package/dist/basic.js.map +1 -1
- package/dist/blob.d.ts +1 -1
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +0 -1
- package/dist/blob.js.map +1 -1
- package/dist/content_menu.d.ts +63 -20
- package/dist/content_menu.d.ts.map +1 -1
- package/dist/content_menu.js +23 -20
- package/dist/content_menu.js.map +1 -1
- package/dist/datatable_bulk.d.ts +34 -6
- package/dist/datatable_bulk.d.ts.map +1 -1
- package/dist/datatable_bulk.js +4 -5
- package/dist/datatable_bulk.js.map +1 -1
- package/dist/dialog.d.ts +82 -7
- package/dist/dialog.d.ts.map +1 -1
- package/dist/dialog.js +21 -16
- package/dist/dialog.js.map +1 -1
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +3 -3
- package/dist/events.js.map +1 -1
- package/dist/faq_dialog.d.ts +13 -4
- package/dist/faq_dialog.d.ts.map +1 -1
- package/dist/faq_dialog.js +4 -2
- package/dist/faq_dialog.js.map +1 -1
- package/dist/file/dialog.d.ts +33 -13
- package/dist/file/dialog.d.ts.map +1 -1
- package/dist/file/dialog.js +6 -8
- package/dist/file/dialog.js.map +1 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +0 -1
- package/dist/file/index.js.map +1 -1
- package/dist/file/new_folder_dialog.d.ts +5 -2
- package/dist/file/new_folder_dialog.d.ts.map +1 -1
- package/dist/file/new_folder_dialog.js +1 -2
- package/dist/file/new_folder_dialog.js.map +1 -1
- package/dist/file/selector.d.ts +47 -14
- package/dist/file/selector.d.ts.map +1 -1
- package/dist/file/selector.js +11 -9
- package/dist/file/selector.js.map +1 -1
- package/dist/file/templates.d.ts +1 -1
- package/dist/file/templates.d.ts.map +1 -1
- package/dist/file/templates.js +0 -1
- package/dist/file/templates.js.map +1 -1
- package/dist/file/tools.d.ts +4 -4
- package/dist/file/tools.d.ts.map +1 -1
- package/dist/file/tools.js +4 -4
- package/dist/file/tools.js.map +1 -1
- package/dist/focus.d.ts +1 -1
- package/dist/focus.d.ts.map +1 -1
- package/dist/focus.js +5 -5
- package/dist/focus.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/network.d.ts +19 -7
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +15 -12
- package/dist/network.js.map +1 -1
- package/dist/overview_menu.d.ts +77 -18
- package/dist/overview_menu.d.ts.map +1 -1
- package/dist/overview_menu.js +54 -35
- package/dist/overview_menu.js.map +1 -1
- package/dist/settings.d.ts +7 -2
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -1
- package/dist/settings.js.map +1 -1
- package/dist/user.d.ts +8 -2
- package/dist/user.d.ts.map +1 -1
- package/dist/user.js +1 -2
- package/dist/user.js.map +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +1 -2
- package/dist/worker.js.map +1 -1
- package/dist/ws.d.ts +59 -25
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +19 -15
- package/dist/ws.js.map +1 -1
- package/package.json +2 -1
- package/src/basic.ts +136 -69
- package/src/blob.ts +1 -2
- package/src/content_menu.ts +127 -44
- package/src/datatable_bulk.ts +72 -35
- package/src/dialog.ts +156 -61
- package/src/diff-dom.d.ts +16 -0
- package/src/events.ts +3 -3
- package/src/faq_dialog.ts +25 -11
- package/src/file/dialog.ts +48 -14
- package/src/file/index.ts +0 -1
- package/src/file/new_folder_dialog.ts +7 -5
- package/src/file/selector.ts +86 -36
- package/src/file/templates.ts +2 -3
- package/src/file/tools.ts +17 -8
- package/src/focus.ts +11 -13
- package/src/global.d.ts +11 -4
- package/src/index.ts +0 -3
- package/src/network.ts +58 -20
- package/src/overview_menu.ts +183 -109
- package/src/settings.ts +9 -4
- package/src/user.ts +10 -5
- package/src/w3c-keyname.d.ts +3 -0
- package/src/worker.ts +1 -2
- package/src/ws.ts +115 -50
- package/css/ui_dialogs.css +0 -144
- package/dist/templates.d.ts +0 -7
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -43
- package/dist/templates.js.map +0 -1
- package/dist/user_util.d.ts +0 -7
- package/dist/user_util.d.ts.map +0 -1
- package/dist/user_util.js +0 -19
- package/dist/user_util.js.map +0 -1
- package/src/templates.ts +0 -43
- package/src/user_util.ts +0 -17
package/src/settings.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export interface Settings {
|
|
2
|
+
apiUrl: (url: string) => string
|
|
3
|
+
getCsrfToken: () => string
|
|
4
|
+
[key: string]: unknown
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let _settings: Settings | null = null
|
|
3
8
|
|
|
4
|
-
export function initSettings(rawSettings) {
|
|
9
|
+
export function initSettings(rawSettings: Settings): void {
|
|
5
10
|
if (_settings) {
|
|
6
11
|
throw new Error("Settings already initialized")
|
|
7
12
|
}
|
|
@@ -9,7 +14,7 @@ export function initSettings(rawSettings) {
|
|
|
9
14
|
_settings = Object.freeze({...rawSettings})
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
export function getSettings() {
|
|
17
|
+
export function getSettings(): Settings {
|
|
13
18
|
if (!_settings) {
|
|
14
19
|
throw new Error(
|
|
15
20
|
"App settings not initialized. Call initSettings() first."
|
package/src/user.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import {post} from "./network.js"
|
|
3
2
|
|
|
4
|
-
export const setLanguage = (_config, language) =>
|
|
3
|
+
export const setLanguage = (_config: unknown, language: string): Promise<unknown> =>
|
|
5
4
|
post("/api/i18n/setlang/", {language}).then(() => {
|
|
6
5
|
// We delete the network cache as this contains the JS
|
|
7
6
|
// translations.
|
|
@@ -13,9 +12,9 @@ export const setLanguage = (_config, language) =>
|
|
|
13
12
|
})
|
|
14
13
|
})
|
|
15
14
|
|
|
16
|
-
const COLOR_CACHE = {}
|
|
15
|
+
const COLOR_CACHE: Record<string, string> = {}
|
|
17
16
|
|
|
18
|
-
const userColor = string => {
|
|
17
|
+
const userColor = (string: string): string => {
|
|
19
18
|
// Source https://gist.github.com/0x263b/2bdd90886c2036a1ad5bcf06d6e6fb37
|
|
20
19
|
if (string.length === 0) {
|
|
21
20
|
return "rgb(0,0,0)"
|
|
@@ -35,8 +34,14 @@ const userColor = string => {
|
|
|
35
34
|
return COLOR_CACHE[string]
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
interface AvatarUser {
|
|
38
|
+
username?: string
|
|
39
|
+
name?: string
|
|
40
|
+
avatar?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
/** A template for the default round avatar view. */
|
|
39
|
-
export const avatarTemplate = ({user}) => {
|
|
44
|
+
export const avatarTemplate = ({user}: {user: AvatarUser}): string => {
|
|
40
45
|
const name = user.username || user.name || "A"
|
|
41
46
|
if (user.avatar) {
|
|
42
47
|
return `<img class="fw-avatar" src="${user.avatar}" alt="${name}">`
|
package/src/worker.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
/* allows cross domain web workers */
|
|
3
2
|
/* Taken from https://benohead.com/cross-domain-cross-browser-web-workers/ */
|
|
4
|
-
export const makeWorker = workerUrl => {
|
|
3
|
+
export const makeWorker = (workerUrl: string): Worker => {
|
|
5
4
|
const a = document.createElement("a")
|
|
6
5
|
a.href = workerUrl // turn into absolute URL if needed.
|
|
7
6
|
const blob = new Blob([`importScripts("${a.href}")`], {
|
package/src/ws.ts
CHANGED
|
@@ -1,7 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
export interface WebSocketMessage {
|
|
2
|
+
type?: string
|
|
3
|
+
s?: number
|
|
4
|
+
c?: number
|
|
5
|
+
base?: string
|
|
6
|
+
from?: number
|
|
7
|
+
[key: string]: unknown
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type GetMessage = () => WebSocketMessage | false
|
|
11
|
+
|
|
12
|
+
export interface WebSocketConnectorOptions {
|
|
13
|
+
base?: string
|
|
14
|
+
path?: string
|
|
15
|
+
appLoaded?: () => boolean
|
|
16
|
+
anythingToSend?: () => boolean
|
|
17
|
+
messagesElement?: () => HTMLElement | false | null
|
|
18
|
+
initialMessage?: () => WebSocketMessage
|
|
19
|
+
resubScribed?: () => void
|
|
20
|
+
restartMessage?: () => WebSocketMessage
|
|
21
|
+
warningNotAllSent?: string
|
|
22
|
+
infoDisconnected?: string
|
|
23
|
+
receiveData?: (data: WebSocketMessage) => void
|
|
24
|
+
failedAuth?: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MessageTracker {
|
|
28
|
+
server: number
|
|
29
|
+
client: number
|
|
30
|
+
lastTen: WebSocketMessage[]
|
|
31
|
+
}
|
|
32
|
+
|
|
2
33
|
/* Sets up communicating with server (retrieving document, saving, collaboration, etc.).
|
|
3
34
|
*/
|
|
4
35
|
export class WebSocketConnector {
|
|
36
|
+
base: string
|
|
37
|
+
path: string
|
|
38
|
+
appLoaded: () => boolean
|
|
39
|
+
anythingToSend: () => boolean
|
|
40
|
+
messagesElement: () => HTMLElement | false | null
|
|
41
|
+
initialMessage: () => WebSocketMessage
|
|
42
|
+
resubScribed: () => void
|
|
43
|
+
restartMessage: () => WebSocketMessage
|
|
44
|
+
warningNotAllSent: string
|
|
45
|
+
infoDisconnected: string
|
|
46
|
+
receiveData: (data: WebSocketMessage) => void
|
|
47
|
+
failedAuth: () => void
|
|
48
|
+
|
|
49
|
+
messages: MessageTracker
|
|
50
|
+
messagesToSend: GetMessage[]
|
|
51
|
+
oldMessages: GetMessage[]
|
|
52
|
+
|
|
53
|
+
online: boolean
|
|
54
|
+
connected: boolean
|
|
55
|
+
connectionCount: number
|
|
56
|
+
recentlySent: boolean
|
|
57
|
+
listeners: Record<string, (event: Event) => void>
|
|
58
|
+
|
|
59
|
+
ws: WebSocket | undefined
|
|
60
|
+
|
|
61
|
+
//heartbeat
|
|
62
|
+
pingTimer: number | false
|
|
63
|
+
pongTimer: number | false
|
|
64
|
+
|
|
5
65
|
constructor({
|
|
6
66
|
base = "", // needs to be specified
|
|
7
67
|
path = "", // needs to be specified
|
|
@@ -13,11 +73,11 @@ export class WebSocketConnector {
|
|
|
13
73
|
restartMessage = () => ({type: "restart"}), // Too many messages have been lost and we need to restart
|
|
14
74
|
warningNotAllSent = gettext("Warning! Some data is unsaved"), // Info to show while disconnected WITH unsaved data
|
|
15
75
|
infoDisconnected = gettext("Disconnected. Attempting to reconnect..."), // Info to show while disconnected WITHOUT unsaved data
|
|
16
|
-
receiveData =
|
|
76
|
+
receiveData = () => {},
|
|
17
77
|
failedAuth = () => {
|
|
18
78
|
window.location.href = "/"
|
|
19
79
|
}
|
|
20
|
-
}) {
|
|
80
|
+
}: WebSocketConnectorOptions = {}) {
|
|
21
81
|
this.base = base
|
|
22
82
|
this.path = path
|
|
23
83
|
this.appLoaded = appLoaded
|
|
@@ -48,31 +108,32 @@ export class WebSocketConnector {
|
|
|
48
108
|
//heartbeat
|
|
49
109
|
this.pingTimer = false
|
|
50
110
|
this.pongTimer = false
|
|
111
|
+
|
|
112
|
+
this.messages = {
|
|
113
|
+
server: 0,
|
|
114
|
+
client: 0,
|
|
115
|
+
lastTen: []
|
|
116
|
+
}
|
|
51
117
|
}
|
|
52
118
|
|
|
53
|
-
init() {
|
|
119
|
+
init(): void {
|
|
54
120
|
this.createWSConnection()
|
|
55
121
|
|
|
56
122
|
// Close the socket manually for now when the connection is lost. Sometimes the socket isn't closed on disconnection.
|
|
57
|
-
this.listeners.onOffline =
|
|
123
|
+
this.listeners.onOffline = () => this.ws!.close()
|
|
58
124
|
window.addEventListener("offline", this.listeners.onOffline)
|
|
59
125
|
}
|
|
60
126
|
|
|
61
|
-
goOffline() {
|
|
127
|
+
goOffline(): void {
|
|
62
128
|
// Simulate offline mode due to lack of ways of doing this in Chrome/Firefox
|
|
63
129
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1421357
|
|
64
130
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=423246
|
|
65
131
|
this.online = false
|
|
66
132
|
this.connected = false
|
|
67
|
-
this.ws
|
|
133
|
+
this.ws!.close()
|
|
68
134
|
}
|
|
69
135
|
|
|
70
|
-
|
|
71
|
-
// Reconnect from offline mode
|
|
72
|
-
this.online = true
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
close() {
|
|
136
|
+
close(): void {
|
|
76
137
|
if (this.ws) {
|
|
77
138
|
this.ws.onclose = () => {}
|
|
78
139
|
this.ws.close()
|
|
@@ -80,14 +141,14 @@ export class WebSocketConnector {
|
|
|
80
141
|
window.removeEventListener("offline", this.listeners.onOffline)
|
|
81
142
|
}
|
|
82
143
|
|
|
83
|
-
createWSConnection() {
|
|
144
|
+
createWSConnection(): void {
|
|
84
145
|
// Messages object used to ensure that data is received in right order.
|
|
85
146
|
this.messages = {
|
|
86
147
|
server: 0,
|
|
87
148
|
client: 0,
|
|
88
149
|
lastTen: []
|
|
89
150
|
}
|
|
90
|
-
let url
|
|
151
|
+
let url: string
|
|
91
152
|
if (this.online) {
|
|
92
153
|
if (this.base.startsWith("/")) {
|
|
93
154
|
url = this.base + this.path
|
|
@@ -113,7 +174,7 @@ export class WebSocketConnector {
|
|
|
113
174
|
this.ws.onclose = () => this.onclose()
|
|
114
175
|
}
|
|
115
176
|
|
|
116
|
-
waitForWS() {
|
|
177
|
+
waitForWS(): Promise<void> {
|
|
117
178
|
return new Promise((resolve, reject) => {
|
|
118
179
|
const checkState = () => {
|
|
119
180
|
if (!this.ws) {
|
|
@@ -138,22 +199,22 @@ export class WebSocketConnector {
|
|
|
138
199
|
})
|
|
139
200
|
}
|
|
140
201
|
|
|
141
|
-
onmessage(event) {
|
|
142
|
-
const data = JSON.parse(event.data)
|
|
202
|
+
onmessage(event: MessageEvent): void {
|
|
203
|
+
const data = JSON.parse(event.data) as WebSocketMessage
|
|
143
204
|
const expectedServer = this.messages.server + 1
|
|
144
205
|
if (data.type === "request_resend") {
|
|
145
|
-
this.resend_messages(data.from)
|
|
206
|
+
this.resend_messages(data.from!)
|
|
146
207
|
} else if (data.type === "pong") {
|
|
147
208
|
this.heartbeat()
|
|
148
|
-
} else if (data.s < expectedServer) {
|
|
209
|
+
} else if ((data.s as number) < expectedServer) {
|
|
149
210
|
// Receive a message already received at least once. Ignore.
|
|
150
211
|
return
|
|
151
|
-
} else if (data.s > expectedServer) {
|
|
212
|
+
} else if ((data.s as number) > expectedServer) {
|
|
152
213
|
// Messages from the server have been lost.
|
|
153
214
|
// Request resend.
|
|
154
215
|
this.waitForWS()
|
|
155
216
|
.then(() =>
|
|
156
|
-
this.ws
|
|
217
|
+
this.ws!.send(
|
|
157
218
|
JSON.stringify({
|
|
158
219
|
type: "request_resend",
|
|
159
220
|
from: this.messages.server
|
|
@@ -167,27 +228,27 @@ export class WebSocketConnector {
|
|
|
167
228
|
this.messages.server = expectedServer
|
|
168
229
|
if (data.c === this.messages.client) {
|
|
169
230
|
this.receive(data)
|
|
170
|
-
} else if (data.c < this.messages.client) {
|
|
231
|
+
} else if ((data.c as number) < this.messages.client) {
|
|
171
232
|
// We have received all server messages, but the server seems
|
|
172
233
|
// to have missed some of the client's messages. They could
|
|
173
234
|
// have been sent simultaneously.
|
|
174
235
|
// The server wins over the client in this case.
|
|
175
236
|
this.waitForWS().then(() => {
|
|
176
|
-
const clientDifference = this.messages.client - data.c
|
|
177
|
-
this.messages.client = data.c
|
|
237
|
+
const clientDifference = this.messages.client - (data.c as number)
|
|
238
|
+
this.messages.client = data.c as number
|
|
178
239
|
if (clientDifference > this.messages.lastTen.length) {
|
|
179
240
|
// We cannot fix the situation
|
|
180
241
|
this.send(this.restartMessage)
|
|
181
242
|
return
|
|
182
243
|
}
|
|
183
|
-
this.messages
|
|
244
|
+
this.messages.lastTen
|
|
184
245
|
.slice(0 - clientDifference)
|
|
185
246
|
.forEach(data => {
|
|
186
247
|
this.messages.client += 1
|
|
187
248
|
data.c = this.messages.client
|
|
188
249
|
data.s = this.messages.server
|
|
189
250
|
|
|
190
|
-
this.ws
|
|
251
|
+
this.ws!.send(JSON.stringify(data))
|
|
191
252
|
})
|
|
192
253
|
this.receive(data)
|
|
193
254
|
})
|
|
@@ -195,7 +256,7 @@ export class WebSocketConnector {
|
|
|
195
256
|
}
|
|
196
257
|
}
|
|
197
258
|
|
|
198
|
-
onclose() {
|
|
259
|
+
onclose(): void {
|
|
199
260
|
this.connected = false
|
|
200
261
|
window.setTimeout(() => {
|
|
201
262
|
this.createWSConnection()
|
|
@@ -215,7 +276,7 @@ export class WebSocketConnector {
|
|
|
215
276
|
}
|
|
216
277
|
}
|
|
217
278
|
|
|
218
|
-
open() {
|
|
279
|
+
open(): void {
|
|
219
280
|
const messagesElement = this.messagesElement()
|
|
220
281
|
if (messagesElement) {
|
|
221
282
|
messagesElement.innerHTML = ""
|
|
@@ -230,19 +291,19 @@ export class WebSocketConnector {
|
|
|
230
291
|
this.send(() => message)
|
|
231
292
|
}
|
|
232
293
|
|
|
233
|
-
subscribed() {
|
|
294
|
+
subscribed(): void {
|
|
234
295
|
if (this.connectionCount > 1) {
|
|
235
296
|
this.resubScribed()
|
|
236
297
|
}
|
|
237
298
|
while (this.oldMessages.length > 0) {
|
|
238
|
-
this.send(this.oldMessages.shift())
|
|
299
|
+
this.send(this.oldMessages.shift()!)
|
|
239
300
|
}
|
|
240
301
|
}
|
|
241
302
|
|
|
242
303
|
/** Sends data to server or keeps it in a list if currently offline. */
|
|
243
|
-
send(getData, timer = 80) {
|
|
244
|
-
if (this.connected && this.ws
|
|
245
|
-
this.ws
|
|
304
|
+
send(getData: GetMessage, timer = 80): void {
|
|
305
|
+
if (this.connected && this.ws!.readyState !== this.ws!.OPEN) {
|
|
306
|
+
this.ws!.close()
|
|
246
307
|
return
|
|
247
308
|
}
|
|
248
309
|
if (this.connected && !this.recentlySent) {
|
|
@@ -255,11 +316,11 @@ export class WebSocketConnector {
|
|
|
255
316
|
data.c = this.messages.client
|
|
256
317
|
data.s = this.messages.server
|
|
257
318
|
this.messages.lastTen.push(data)
|
|
258
|
-
this.messages.lastTen = this.messages
|
|
319
|
+
this.messages.lastTen = this.messages.lastTen.slice(-10)
|
|
259
320
|
|
|
260
321
|
this.waitForWS()
|
|
261
322
|
.then(() => {
|
|
262
|
-
this.ws
|
|
323
|
+
this.ws!.send(JSON.stringify(data))
|
|
263
324
|
this.setRecentlySentTimer(timer)
|
|
264
325
|
})
|
|
265
326
|
.catch(() => {
|
|
@@ -275,20 +336,20 @@ export class WebSocketConnector {
|
|
|
275
336
|
}
|
|
276
337
|
}
|
|
277
338
|
|
|
278
|
-
setRecentlySentTimer(timer) {
|
|
339
|
+
setRecentlySentTimer(timer: number): void {
|
|
279
340
|
this.recentlySent = true
|
|
280
341
|
window.setTimeout(() => {
|
|
281
342
|
this.recentlySent = false
|
|
282
343
|
const oldMessages = this.messagesToSend
|
|
283
344
|
this.messagesToSend = []
|
|
284
345
|
while (oldMessages.length > 0) {
|
|
285
|
-
const getData = oldMessages.shift()
|
|
346
|
+
const getData = oldMessages.shift()!
|
|
286
347
|
this.send(getData, Math.min(timer * 1.2, 10000))
|
|
287
348
|
}
|
|
288
349
|
}, timer)
|
|
289
350
|
}
|
|
290
351
|
|
|
291
|
-
resend_messages(from) {
|
|
352
|
+
resend_messages(from: number): Promise<void> {
|
|
292
353
|
return this.waitForWS()
|
|
293
354
|
.then(() => {
|
|
294
355
|
const toSend = this.messages.client - from
|
|
@@ -302,7 +363,7 @@ export class WebSocketConnector {
|
|
|
302
363
|
this.messages.client += 1
|
|
303
364
|
data.c = this.messages.client
|
|
304
365
|
data.s = this.messages.server
|
|
305
|
-
this.ws
|
|
366
|
+
this.ws!.send(JSON.stringify(data))
|
|
306
367
|
})
|
|
307
368
|
})
|
|
308
369
|
.catch(() => {
|
|
@@ -311,10 +372,10 @@ export class WebSocketConnector {
|
|
|
311
372
|
})
|
|
312
373
|
}
|
|
313
374
|
|
|
314
|
-
receive(data) {
|
|
375
|
+
receive(data: WebSocketMessage): void {
|
|
315
376
|
switch (data.type) {
|
|
316
377
|
case "redirect":
|
|
317
|
-
this.base = data.base
|
|
378
|
+
this.base = data.base as string
|
|
318
379
|
break
|
|
319
380
|
case "welcome":
|
|
320
381
|
this.open()
|
|
@@ -332,15 +393,19 @@ export class WebSocketConnector {
|
|
|
332
393
|
}
|
|
333
394
|
}
|
|
334
395
|
|
|
335
|
-
heartbeat() {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
396
|
+
heartbeat(): void {
|
|
397
|
+
if (this.pingTimer !== false) {
|
|
398
|
+
clearTimeout(this.pingTimer)
|
|
399
|
+
}
|
|
400
|
+
if (this.pongTimer !== false) {
|
|
401
|
+
clearTimeout(this.pongTimer)
|
|
402
|
+
}
|
|
403
|
+
this.pingTimer = window.setTimeout(() => {
|
|
339
404
|
// Don't send ping if WebSocket is not open
|
|
340
|
-
if (this.ws
|
|
341
|
-
this.ws
|
|
342
|
-
this.pongTimer = setTimeout(() => {
|
|
343
|
-
this.listeners.onOffline()
|
|
405
|
+
if (this.ws!.readyState === this.ws!.OPEN) {
|
|
406
|
+
this.ws!.send('{"type": "ping"}')
|
|
407
|
+
this.pongTimer = window.setTimeout(() => {
|
|
408
|
+
this.listeners.onOffline(new Event("offline"))
|
|
344
409
|
}, 10000)
|
|
345
410
|
}
|
|
346
411
|
}, 60000)
|
package/css/ui_dialogs.css
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
.ui-widget-overlay {
|
|
2
|
-
position: fixed;
|
|
3
|
-
left: 0;
|
|
4
|
-
top: 0;
|
|
5
|
-
width: 100%;
|
|
6
|
-
height: 100%;
|
|
7
|
-
background-color: rgb(var(--overlay-color));
|
|
8
|
-
z-index: 100;
|
|
9
|
-
backdrop-filter: blur(2px);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.ui-widget-overlay.no-blur {
|
|
13
|
-
backdrop-filter: none;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.ui-dialog {
|
|
17
|
-
position: absolute;
|
|
18
|
-
padding: 19px;
|
|
19
|
-
background-color: var(--cs-light-background);
|
|
20
|
-
border: solid 1px var(--cs-light-border);
|
|
21
|
-
box-shadow: 0 0 10px var(--cs-light-box-shadow);
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
border-radius: 4px;
|
|
24
|
-
font-size: 14px;
|
|
25
|
-
outline: 0;
|
|
26
|
-
z-index: 1000; /* Ensure dialog stays on top */
|
|
27
|
-
user-select: none; /* Prevent interaction with background content */
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.ui-dialog-titlebar {
|
|
31
|
-
position: relative;
|
|
32
|
-
padding: 0;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.ui-dialog-title {
|
|
36
|
-
font-size: 16px;
|
|
37
|
-
font-weight: 700;
|
|
38
|
-
text-transform: none;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.ui-dialog-buttonpane {
|
|
42
|
-
padding-top: 20px;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.ui-button {
|
|
46
|
-
font-size: 14px;
|
|
47
|
-
line-height: 1em;
|
|
48
|
-
border: none;
|
|
49
|
-
background: none;
|
|
50
|
-
padding: 0;
|
|
51
|
-
margin: 0;
|
|
52
|
-
cursor: pointer;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.ui-dialog-titlebar-close,
|
|
56
|
-
.ui-dialog-titlebar-help {
|
|
57
|
-
position: absolute;
|
|
58
|
-
right: 0.3em;
|
|
59
|
-
top: 50%;
|
|
60
|
-
width: 20px;
|
|
61
|
-
margin: -10px 0 0;
|
|
62
|
-
padding: 1px;
|
|
63
|
-
height: 20px;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.ui-button-icon-only {
|
|
67
|
-
width: 2em;
|
|
68
|
-
box-sizing: border-box;
|
|
69
|
-
text-indent: -9999px;
|
|
70
|
-
white-space: nowrap;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.ui-button-icon-only .ui-icon {
|
|
74
|
-
position: absolute;
|
|
75
|
-
top: 50%;
|
|
76
|
-
left: 50%;
|
|
77
|
-
margin-top: -8px;
|
|
78
|
-
margin-left: -8px;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.ui-button-icon-only .ui-icon::before {
|
|
82
|
-
text-indent: 5019px;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/* Style for the close button specifically */
|
|
86
|
-
.ui-dialog-titlebar-close {
|
|
87
|
-
order: 99; /* Push to end of flex container */
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.ui-dialog-titlebar-close:hover,
|
|
91
|
-
.ui-dialog-titlebar-help:hover {
|
|
92
|
-
color: var(--cs-dark-hover);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.ui-icon-closethick::before {
|
|
96
|
-
width: 0.7em;
|
|
97
|
-
height: 1em;
|
|
98
|
-
padding-top: 5px;
|
|
99
|
-
content: "\f00d";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.ui-icon-help::before {
|
|
103
|
-
width: 0.7em;
|
|
104
|
-
height: 1em;
|
|
105
|
-
padding-top: 5px;
|
|
106
|
-
content: "\f128";
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.ui-dialog-content {
|
|
110
|
-
position: relative;
|
|
111
|
-
margin-top: 24px;
|
|
112
|
-
overflow: auto;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.ui-dialog-buttonset {
|
|
116
|
-
position: relative;
|
|
117
|
-
text-align: right;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.ui-dialog-buttonset .fw-button {
|
|
121
|
-
margin-left: 6px;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/* Make focus visible on all focusable elements */
|
|
125
|
-
.ui-dialog *:focus {
|
|
126
|
-
outline: 2px solid var(--cs-dark-background);
|
|
127
|
-
outline-offset: 2px;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/* Style the title bar buttons specifically */
|
|
131
|
-
.ui-dialog-titlebar .ui-button-icon-only {
|
|
132
|
-
position: absolute;
|
|
133
|
-
top: 50%;
|
|
134
|
-
transform: translateY(-50%);
|
|
135
|
-
padding: 8px;
|
|
136
|
-
background: transparent;
|
|
137
|
-
border: none;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.ui-dialog-titlebar .ui-button-icon-only:focus {
|
|
141
|
-
outline: 2px solid var(--cs-dark-text);
|
|
142
|
-
outline-offset: 2px;
|
|
143
|
-
border-radius: 4px;
|
|
144
|
-
}
|
package/dist/templates.d.ts
DELETED
package/dist/templates.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,GAAI;;;;;CAAkC,WAsC5D,CAAA"}
|
package/dist/templates.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
import { avatarTemplate } from "./user.js";
|
|
3
|
-
import { filterPrimaryEmail } from "./user_util.js";
|
|
4
|
-
export const baseBodyTemplate = ({ user, contents, hasOverview, app }) => `
|
|
5
|
-
<div id="wait">
|
|
6
|
-
<i class="fa fa-spinner fa-pulse"></i>
|
|
7
|
-
</div>
|
|
8
|
-
<header class="fw-header" role="banner">
|
|
9
|
-
<div class="fw-container">
|
|
10
|
-
<a href="${app && app.routes[""].app === "document" ? "/" : "/documents/"}">
|
|
11
|
-
<h1 class="fw-logo">
|
|
12
|
-
<span class="fw-logo-text"></span>
|
|
13
|
-
<img src="${staticUrl("svg/icon.svg")}" alt="Logo" />
|
|
14
|
-
</h1>
|
|
15
|
-
</a>
|
|
16
|
-
<nav id="header-nav" role="navigation" aria-label="${gettext("Site navigation")}"></nav>
|
|
17
|
-
<div id="user-preferences" class="fw-user-preferences fw-header-text">
|
|
18
|
-
<div id="preferences-btn" class="fw-button">
|
|
19
|
-
${avatarTemplate({ user })}
|
|
20
|
-
</div>
|
|
21
|
-
<div id="user-preferences-pulldown" class="fw-pulldown fw-right">
|
|
22
|
-
<div data-value="profile">
|
|
23
|
-
<span class='fw-avatar-card'>
|
|
24
|
-
<span class='fw-avatar-card-avatar'>${avatarTemplate({ user })}</span>
|
|
25
|
-
<span class='fw-avatar-card-name'>
|
|
26
|
-
${user.username}
|
|
27
|
-
<span class='fw-avatar-card-email'>${filterPrimaryEmail(user.emails)}</span>
|
|
28
|
-
</span>
|
|
29
|
-
</span>
|
|
30
|
-
</div>
|
|
31
|
-
<div data-value="contacts">${gettext("Contacts")}</div>
|
|
32
|
-
<div data-value="logout">${gettext("Log out")}</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div><!-- end user preference -->
|
|
35
|
-
</div><!-- end container -->
|
|
36
|
-
</header>
|
|
37
|
-
<div class="fw-contents-outer">
|
|
38
|
-
${hasOverview ? '<div class="fw-overview-menu-wrapper"><ul id="fw-overview-menu"></ul></div>' : ""}
|
|
39
|
-
<div class="fw-contents">
|
|
40
|
-
${contents}
|
|
41
|
-
</div>
|
|
42
|
-
</div>`;
|
|
43
|
-
//# sourceMappingURL=templates.js.map
|
package/dist/templates.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAA;AACxC,OAAO,EAAC,kBAAkB,EAAC,MAAM,gBAAgB,CAAA;AAEjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAC,EAAE,EAAE,CAAC;;;;;;mBAMrD,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa;;;4BAGrD,SAAS,CAAC,cAAc,CAAC;;;6DAGQ,OAAO,CAAC,iBAAiB,CAAC;;;kBAGrE,cAAc,CAAC,EAAC,IAAI,EAAC,CAAC;;;;;8DAKsB,cAAc,CAAC,EAAC,IAAI,EAAC,CAAC;;8BAEtD,IAAI,CAAC,QAAQ;iEACsB,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;;;;6CAInD,OAAO,CAAC,UAAU,CAAC;2CACrB,OAAO,CAAC,SAAS,CAAC;;;;;;MAMvD,WAAW,CAAC,CAAC,CAAC,6EAA6E,CAAC,CAAC,CAAC,EAAE;;UAE5F,QAAQ;;OAEX,CAAA"}
|
package/dist/user_util.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/** Creates a dropdown box.
|
|
2
|
-
* @param btn The button to open and close the dropdown box.
|
|
3
|
-
* @param box The node containing the contents of the dropdown box.
|
|
4
|
-
* @param preopen An optional function to be called before opening the dropdown box. Used to position dropdown box.
|
|
5
|
-
*/
|
|
6
|
-
export declare const filterPrimaryEmail: (emails: any) => any;
|
|
7
|
-
//# sourceMappingURL=user_util.d.ts.map
|
package/dist/user_util.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user_util.d.ts","sourceRoot":"","sources":["../src/user_util.ts"],"names":[],"mappings":"AACA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAG,WAAM,QAUvC,CAAA"}
|
package/dist/user_util.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/** Creates a dropdown box.
|
|
3
|
-
* @param btn The button to open and close the dropdown box.
|
|
4
|
-
* @param box The node containing the contents of the dropdown box.
|
|
5
|
-
* @param preopen An optional function to be called before opening the dropdown box. Used to position dropdown box.
|
|
6
|
-
*/
|
|
7
|
-
export const filterPrimaryEmail = emails => {
|
|
8
|
-
const primaryEmails = emails.filter(email => email.primary);
|
|
9
|
-
if (!primaryEmails.length) {
|
|
10
|
-
if (emails.length) {
|
|
11
|
-
return emails[0].address;
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
return "";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return primaryEmails[0].address;
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=user_util.js.map
|
package/dist/user_util.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user_util.js","sourceRoot":"","sources":["../src/user_util.ts"],"names":[],"mappings":"AAAA,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,EAAE;IACvC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC3D,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;QACxB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QAC5B,CAAC;aAAM,CAAC;YACJ,OAAO,EAAE,CAAA;QACb,CAAC;IACL,CAAC;IACD,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;AACnC,CAAC,CAAA"}
|