fwtoolkit 0.1.0-alpha.1 → 0.1.0-alpha.2

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/src/dialog.js ADDED
@@ -0,0 +1,484 @@
1
+ import {keyName} from "w3c-keyname"
2
+
3
+ import {findTarget} from "./basic"
4
+
5
+ const dialogTemplate = ({
6
+ id,
7
+ classes,
8
+ title,
9
+ height,
10
+ width,
11
+ icon,
12
+ buttons,
13
+ zIndex,
14
+ body,
15
+ scroll,
16
+ help,
17
+ canClose,
18
+ note,
19
+ blur
20
+ }) =>
21
+ `<div tabindex="-1" role="dialog"
22
+ class="ui-dialog ui-corner-all ui-widget ui-widget-content ui-front ui-dialog-buttons"
23
+ ${id ? `aria-describedby="${id}"` : ""} style="z-index: ${zIndex};">
24
+ <div class="ui-dialog-titlebar ui-corner-all ui-widget-header ui-helper-clearfix">
25
+ ${icon ? `<i class="fa fa-${icon}" aria-hidden="true"></i>` : ""}
26
+ <span id="ui-id-2" class="ui-dialog-title">${title}</span>
27
+ ${
28
+ help
29
+ ? `<button type="button" class="ui-button ui-corner-all ui-widget ui-button-icon-only ui-dialog-titlebar-help" title="${gettext("Help")}">
30
+ <span class="ui-button-icon ui-icon ui-icon-help"> </span>
31
+ <span class="ui-button-icon-space"> </span>
32
+ ${gettext("Help")}
33
+ </button>`
34
+ : ""
35
+ }
36
+ ${
37
+ canClose
38
+ ? `<button type="button" class="ui-button ui-corner-all ui-widget ui-button-icon-only ui-dialog-titlebar-close" title="${gettext("Close")}">
39
+ <span class="ui-button-icon ui-icon ui-icon-closethick"> </span>
40
+ <span class="ui-button-icon-space"> </span>
41
+ ${gettext("Close")}
42
+ </button>`
43
+ : ""
44
+ }
45
+
46
+ </div>
47
+ <div ${id ? `id="${id}"` : ""} class="ui-dialog-content ui-widget-content${classes ? ` ${classes}` : ""}${scroll ? " ui-scrollable" : ""}" style="width: ${width}; height: ${height};">
48
+ ${note.text ? `<div class="note-container">${noteTemplate(note)}</div>` : ""}
49
+ ${body}
50
+ </div>
51
+ <div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
52
+ <div class="ui-dialog-buttonset">${buttonsTemplate({buttons})}</div>
53
+ </div>
54
+ </div>
55
+ <div class="ui-widget-overlay ui-front${blur === false ? " no-blur" : ""}" style="z-index: ${zIndex - 1}"></div>`
56
+
57
+ const noteTemplate = note => {
58
+ return note.text
59
+ ? `<p class="noteEl ${note.display ? "" : "hide"}">${note.text}</p>`
60
+ : ""
61
+ }
62
+
63
+ const buttonsTemplate = ({buttons}) =>
64
+ buttons.map(button => buttonTemplate(button)).join("")
65
+
66
+ const buttonTemplate = ({
67
+ text,
68
+ classes,
69
+ icon,
70
+ dropdown
71
+ }) => `<button type="button" class="${classes ? classes : "fw-light"} fw-button ui-button ui-corner-all ui-widget">
72
+ ${icon ? `<i class="fa fa-${icon}" aria-hidden="true"></i>` : ""}
73
+ ${text}
74
+ ${dropdown ? '<i class="fa fa-caret-down" aria-hidden="true"></i>' : ""}
75
+ </button>`
76
+
77
+ const BUTTON_TYPES = {
78
+ close: {
79
+ text: gettext("Close"),
80
+ classes: "fw-orange",
81
+ click: dialog => () => dialog.close()
82
+ },
83
+ cancel: {
84
+ text: gettext("Cancel"),
85
+ classes: "fw-orange",
86
+ click: dialog => () => dialog.close()
87
+ },
88
+ ok: {
89
+ text: gettext("OK"),
90
+ classes: "fw-dark",
91
+ click: dialog => () => dialog.close()
92
+ }
93
+ }
94
+
95
+ export class Dialog {
96
+ constructor(options) {
97
+ this.id = options.id || false
98
+ this.classes = options.classes || false
99
+ this.title = options.title || ""
100
+ this.body = options.body || ""
101
+ this.restoreActiveElement = options.restoreActiveElement !== false // default is true
102
+ this.height = options.height ? `${options.height}px` : "auto"
103
+ this.width = options.width ? `${options.width}px` : "auto"
104
+ this.canClose = "canClose" in options ? options.canClose : true
105
+ this.help = "help" in options ? options.help : false
106
+ this.note = "note" in options ? options.note : {}
107
+ this.blur = "blur" in options ? options.blur : true
108
+ this.buttons = []
109
+ if (options.buttons) {
110
+ this.setButtons(options.buttons)
111
+ }
112
+ this.beforeClose = options.beforeClose || false
113
+ this.onClose = options.onClose || false
114
+ this.icon = options.icon || false
115
+ this.scroll = options.scroll || false
116
+ this.canEscape =
117
+ options.canEscape ??
118
+ options.buttons?.find(button =>
119
+ ["cancel", "close"].includes(button.type)
120
+ ) ??
121
+ false
122
+ this.dialogEl = false
123
+ this.backdropEl = false
124
+ this.dragging = false
125
+ this.hasBeenMoved = false
126
+ this.listeners = {}
127
+ this.fullScreen = options.fullScreen ? options.fullScreen : false
128
+ this.initialFocus = options.initialFocus || null
129
+
130
+ this.previousActiveElement = null // Store previously focused element
131
+ this.firstFocusableEl = null
132
+ this.lastFocusableEl = null
133
+ this.focusableEls = null
134
+ }
135
+
136
+ setButtons(buttons) {
137
+ this.buttons = buttons.map(button => ({
138
+ text: button.text
139
+ ? button.text
140
+ : button.type
141
+ ? BUTTON_TYPES[button.type].text
142
+ : "",
143
+ classes: button.classes
144
+ ? button.classes
145
+ : button.type
146
+ ? BUTTON_TYPES[button.type].classes
147
+ : false,
148
+ click: button.click
149
+ ? button.click
150
+ : button.type
151
+ ? BUTTON_TYPES[button.type].click(this)
152
+ : "",
153
+ icon: button.icon ? button.icon : false,
154
+ dropdown: button.dropdown ? true : false
155
+ }))
156
+ }
157
+
158
+ open() {
159
+ if (this.dialogEl) {
160
+ return
161
+ }
162
+
163
+ // Store currently focused element to restore later
164
+ this.previousActiveElement = this.restoreActiveElement
165
+ ? document.activeElement
166
+ : null
167
+
168
+ if (this.fullScreen) {
169
+ this.height = "85vh"
170
+ }
171
+
172
+ document.body.insertAdjacentHTML(
173
+ "beforeend",
174
+ dialogTemplate({
175
+ id: this.id,
176
+ classes: this.classes,
177
+ title: this.title,
178
+ height: this.height,
179
+ width: this.width,
180
+ icon: this.icon,
181
+ buttons: this.buttons,
182
+ zIndex: this.nextDialogZIndex(),
183
+ body: this.body,
184
+ scroll: this.scroll,
185
+ canClose: this.canClose,
186
+ help: this.help,
187
+ note: this.note,
188
+ blur: this.blur
189
+ })
190
+ )
191
+ this.backdropEl = document.body.lastElementChild
192
+ this.dialogEl = this.backdropEl.previousElementSibling
193
+ if (this.fullScreen) {
194
+ this.dialogEl.style.width = "98%"
195
+ this.dialogEl.style.height = "100%"
196
+ this.dialogEl.style.position = "fixed"
197
+ this.dialogEl.style.top = "0px"
198
+ } else {
199
+ this.centerDialog()
200
+ }
201
+
202
+ // Set dialog attributes for accessibility
203
+ this.dialogEl.setAttribute("role", "dialog")
204
+ this.dialogEl.setAttribute("aria-modal", "true")
205
+ if (this.title) {
206
+ this.dialogEl.setAttribute("aria-labelledby", "dialog-title")
207
+ this.dialogEl.querySelector(".ui-dialog-title").id = "dialog-title"
208
+ }
209
+
210
+ // Get all focusable elements
211
+ this.focusableEls = this.getFocusableElements()
212
+ this.firstFocusableEl = this.focusableEls[0]
213
+ this.lastFocusableEl = this.focusableEls[this.focusableEls.length - 1]
214
+
215
+ // Set initial focus to the most appropriate element
216
+ const initialFocusElement = this.getInitialFocusElement()
217
+ if (initialFocusElement) {
218
+ setTimeout(() => initialFocusElement.focus(), 0)
219
+ } else if (this.firstFocusableEl) {
220
+ this.firstFocusableEl.focus()
221
+ } else {
222
+ this.dialogEl.focus()
223
+ }
224
+
225
+ this.bind()
226
+ }
227
+
228
+ refreshButtons() {
229
+ this.dialogEl.querySelector(".ui-dialog-buttonset").innerHTML =
230
+ buttonsTemplate({buttons: this.buttons})
231
+ }
232
+
233
+ refreshNote() {
234
+ this.dialogEl.querySelector(".note-container").innerHTML = noteTemplate(
235
+ this.note
236
+ )
237
+ }
238
+
239
+ centerDialog() {
240
+ const totalWidth = window.innerWidth,
241
+ totalHeight = window.innerHeight,
242
+ dialogWidth = this.dialogEl.clientWidth,
243
+ dialogHeight = this.dialogEl.clientHeight,
244
+ scrollTopOffset = window.pageYOffset,
245
+ scrollLeftOffset = window.pageXOffset
246
+
247
+ this.dialogEl.style.top = `${(totalHeight - dialogHeight) / 2 + scrollTopOffset}px`
248
+ this.dialogEl.style.left = `${(totalWidth - dialogWidth) / 2 + scrollLeftOffset}px`
249
+ }
250
+
251
+ adjustDialogToScroll() {
252
+ this.dialogEl.style.top = `${Math.max(
253
+ Math.min(
254
+ this.dialogEl.offsetTop,
255
+ this.backdropEl.scrollHeight -
256
+ this.dialogEl.scrollHeight +
257
+ window.pageYOffset
258
+ ),
259
+ window.pageYOffset
260
+ )}px`
261
+ }
262
+
263
+ moveDialog(x, y) {
264
+ this.dialogEl.style.top = `${Math.min(
265
+ Math.max(y - this.dragging.y, 0),
266
+ this.backdropEl.scrollHeight -
267
+ this.dialogEl.scrollHeight +
268
+ window.pageYOffset
269
+ )}px`
270
+ this.dialogEl.style.left = `${Math.min(
271
+ Math.max(x - this.dragging.x, 0),
272
+ document.body.scrollWidth - this.dialogEl.scrollWidth
273
+ )}px`
274
+ this.hasBeenMoved = true
275
+ }
276
+
277
+ onScroll(_event) {
278
+ if (this.hasBeenMoved) {
279
+ // The dialog has been moved manually. We just adjust the position to make it stay in the view.
280
+ this.adjustDialogToScroll()
281
+ } else {
282
+ this.centerDialog()
283
+ }
284
+ }
285
+
286
+ onKeydown(event) {
287
+ let name = keyName(event)
288
+ if (event.altKey) {
289
+ name = "Alt-" + name
290
+ }
291
+ if (event.ctrlKey) {
292
+ name = "Ctrl-" + name
293
+ }
294
+ if (event.metaKey) {
295
+ name = "Meta-" + name
296
+ }
297
+ if (event.shiftKey) {
298
+ name = "Shift-" + name
299
+ }
300
+ if (name === "Escape" && this.canEscape) {
301
+ event.preventDefault()
302
+ this.close()
303
+ return
304
+ } else if (name === "Tab") {
305
+ if (document.activeElement === this.lastFocusableEl) {
306
+ event.preventDefault()
307
+ this.firstFocusableEl.focus()
308
+ }
309
+ } else if (name === "Shift-Tab") {
310
+ if (document.activeElement === this.firstFocusableEl) {
311
+ event.preventDefault()
312
+ this.lastFocusableEl.focus()
313
+ }
314
+ }
315
+ }
316
+
317
+ bind() {
318
+ this.listeners.onKeydown = event => this.onKeydown(event)
319
+ document.body.addEventListener("keydown", this.listeners.onKeydown)
320
+ this.dialogEl.addEventListener("click", event => {
321
+ const el = {}
322
+ switch (true) {
323
+ case findTarget(event, ".ui-dialog-buttonpane button", el): {
324
+ event.preventDefault()
325
+ let buttonNumber = 0
326
+ let seekItem = el.target
327
+ while (seekItem.previousElementSibling) {
328
+ buttonNumber++
329
+ seekItem = seekItem.previousElementSibling
330
+ }
331
+ this.buttons[buttonNumber].click(event)
332
+ break
333
+ }
334
+ case findTarget(event, ".ui-dialog-titlebar-close", el):
335
+ event.preventDefault()
336
+ this.close()
337
+ break
338
+ case findTarget(event, ".ui-dialog-titlebar-help", el):
339
+ event.preventDefault()
340
+ this.help()
341
+ break
342
+ default:
343
+ break
344
+ }
345
+ })
346
+ if (!this.fullScreen) {
347
+ this.listeners.onScroll = event => this.onScroll(event)
348
+ window.addEventListener("scroll", this.listeners.onScroll, false)
349
+ this.dialogEl.addEventListener("mousedown", event => {
350
+ const el = {}
351
+ switch (true) {
352
+ case findTarget(event, ".ui-dialog-titlebar", el):
353
+ this.dragging = {
354
+ x: event.clientX - this.dialogEl.offsetLeft,
355
+ y: event.clientY - this.dialogEl.offsetTop
356
+ }
357
+ break
358
+ default:
359
+ break
360
+ }
361
+ })
362
+ this.dialogEl.addEventListener("mouseup", event => {
363
+ const el = {}
364
+ switch (true) {
365
+ case findTarget(event, ".ui-dialog-titlebar", el):
366
+ this.dragging = false
367
+ break
368
+ default:
369
+ break
370
+ }
371
+ })
372
+ this.dialogEl.addEventListener("mousemove", event => {
373
+ if (!this.dragging) {
374
+ return
375
+ }
376
+ this.moveDialog(event.clientX, event.clientY)
377
+ })
378
+ }
379
+
380
+ // Prevent clicks outside dialog from moving focus outside
381
+ this.backdropEl.addEventListener("click", event => {
382
+ event.preventDefault()
383
+ if (this.canClose) {
384
+ this.close()
385
+ }
386
+ })
387
+
388
+ // Prevent focus from leaving dialog when clicking backdrop
389
+ this.backdropEl.addEventListener("mousedown", event => {
390
+ event.preventDefault()
391
+ })
392
+ }
393
+
394
+ nextDialogZIndex() {
395
+ let zIndex = 100
396
+ document
397
+ .querySelectorAll("div.ui-dialog")
398
+ .forEach(
399
+ dialogEl => (zIndex = Math.max(zIndex, dialogEl.style.zIndex))
400
+ )
401
+ zIndex += 2
402
+ document.body.style.setProperty("--highest-dialog-z-index", zIndex)
403
+ return zIndex
404
+ }
405
+
406
+ getFocusableElements() {
407
+ // Get all focusable elements
408
+ const focusableSelectors = [
409
+ "button:not([disabled])",
410
+ "[href]",
411
+ "input:not([disabled])",
412
+ "select:not([disabled])",
413
+ "textarea:not([disabled])",
414
+ '[tabindex]:not([tabindex="-1"])'
415
+ ].join(",")
416
+
417
+ const elements = Array.from(
418
+ this.dialogEl.querySelectorAll(focusableSelectors)
419
+ )
420
+
421
+ // Filter out hidden elements
422
+ return elements.filter(el => {
423
+ const style = window.getComputedStyle(el)
424
+ return style.display !== "none" && style.visibility !== "hidden"
425
+ })
426
+ }
427
+
428
+ getInitialFocusElement() {
429
+ if (this.initialFocus) {
430
+ const customFocusElement = this.dialogEl.querySelector(
431
+ this.initialFocus
432
+ )
433
+ if (customFocusElement) {
434
+ return customFocusElement
435
+ }
436
+ }
437
+ // Get all focusable elements
438
+ const elements = this.getFocusableElements()
439
+
440
+ // Try to find the most appropriate initial focus target
441
+ const priorityElements = [
442
+ // First try to find a text input
443
+ elements.find(el => el.tagName === "INPUT" && el.type === "text"),
444
+ // Then try to find the first button in the button pane
445
+ elements.find(el => el.closest(".ui-dialog-buttonpane")),
446
+ // Then try to find any input
447
+ elements.find(el => el.tagName === "INPUT"),
448
+ // Then try to find any button except close/help
449
+ elements.find(
450
+ el =>
451
+ el.tagName === "BUTTON" &&
452
+ !el.classList.contains("ui-dialog-titlebar-close") &&
453
+ !el.classList.contains("ui-dialog-titlebar-help")
454
+ )
455
+ ]
456
+
457
+ // Return the first element that exists
458
+ return priorityElements.find(el => el) || elements[0]
459
+ }
460
+
461
+ close() {
462
+ if (!this.dialogEl) {
463
+ return
464
+ }
465
+ if (!this.fullScreen) {
466
+ window.removeEventListener("scroll", this.listeners.onScroll, false)
467
+ }
468
+ document.body.removeEventListener("keydown", this.listeners.onKeydown)
469
+ if (this.beforeClose) {
470
+ this.beforeClose()
471
+ }
472
+ this.dialogEl.parentElement.removeChild(this.dialogEl)
473
+ this.backdropEl.parentElement.removeChild(this.backdropEl)
474
+
475
+ // Restore focus to previous element
476
+ if (this.previousActiveElement && this.previousActiveElement.focus) {
477
+ this.previousActiveElement.focus()
478
+ }
479
+
480
+ if (this.onClose) {
481
+ this.onClose()
482
+ }
483
+ }
484
+ }
package/src/events.js ADDED
@@ -0,0 +1,9 @@
1
+ export const isActivationEvent = event => {
2
+ if (event.type === "click") {
3
+ return true
4
+ }
5
+ if (event.type === "keydown") {
6
+ return event.key === "Enter" || event.key === " "
7
+ }
8
+ return false
9
+ }
@@ -0,0 +1,67 @@
1
+ import {escapeText} from "./basic"
2
+ import {Dialog} from "./dialog"
3
+ import {ensureCSS} from "./network"
4
+
5
+ const faqTemplate = ({escapedQuestions}) =>
6
+ `<div class="faq">
7
+ <ol class="faq-list">
8
+ ${escapedQuestions
9
+ .map(
10
+ question => `<li class="faq-item">
11
+ <div>
12
+ <div class="faq-question fw-button fw-light"><i class="fa-solid fa-plus-circle"></i>${question[0]}</div>
13
+ <div class="faq-answer" style="display: none;">${question[1]}</div>
14
+ </div>
15
+ </li>`
16
+ )
17
+ .join("")}
18
+ </ol>
19
+ </div>`
20
+
21
+ export class faqDialog {
22
+ constructor({title = "", questions = []}) {
23
+ ensureCSS(staticUrl("css/faq_dialog.css"))
24
+ const escapedQuestions = []
25
+
26
+ questions.forEach(q => {
27
+ const question = escapeText(q[0])
28
+ let answer
29
+ q[1] = escapeText(q[1])
30
+ if (q[q.length - 1].hasImage) {
31
+ answer = interpolate(...q.slice(1, q.length), true)
32
+ } else {
33
+ answer = q[1]
34
+ }
35
+ escapedQuestions.push([question, answer])
36
+ })
37
+
38
+ this.faqDialog = new Dialog({
39
+ title: title,
40
+ body: faqTemplate({escapedQuestions}),
41
+ height: 600,
42
+ width: 900,
43
+ buttons: []
44
+ })
45
+ }
46
+
47
+ open() {
48
+ this.faqDialog.open()
49
+ this.faqDialog.dialogEl
50
+ .querySelectorAll(".faq-question")
51
+ .forEach(element => {
52
+ element.addEventListener("click", () => {
53
+ const iconEle = element.firstElementChild
54
+ const answerEle = element.nextElementSibling
55
+ if (answerEle.style.display == "") {
56
+ iconEle.classList.remove("fa-minus-circle")
57
+ iconEle.classList.add("fa-plus-circle")
58
+ answerEle.style.display = "none"
59
+ } else if (answerEle.style.display == "none") {
60
+ iconEle.classList.remove("fa-plus-circle")
61
+ iconEle.classList.add("fa-minus-circle")
62
+ answerEle.style.display = ""
63
+ }
64
+ })
65
+ })
66
+ }
67
+ }
@@ -0,0 +1,142 @@
1
+ import {Dialog} from "../dialog"
2
+ import {FileSelector} from "./selector"
3
+ import {addAlert} from "../basic"
4
+ import {NewFolderDialog} from "./new_folder_dialog"
5
+ import {moveTemplate} from "./templates"
6
+ import {moveFile, shortFileTitle} from "./tools"
7
+ /**
8
+ * Functions for the document move dialog.
9
+ */
10
+
11
+ export class FileDialog {
12
+ constructor({
13
+ title = "", // Dialog title
14
+ movingFiles = [], // Array of all files that are to be moved.
15
+ allFiles = [], // Array of all existing files.
16
+ moveUrl = "", // URL to use for moving files
17
+ successMessage = "", // Message for success
18
+ errorMessage = "", // Message for failure
19
+ succcessCallback = (_file, _path) => {}, // Callback on success
20
+ fileIcon = "far fa-file-alt"
21
+ }) {
22
+ this.title = title
23
+ this.movingFiles = movingFiles
24
+ this.allFiles = allFiles
25
+ this.moveUrl = moveUrl
26
+ this.successMessage = successMessage
27
+ this.errorMessage = errorMessage
28
+ this.succcessCallback = succcessCallback
29
+ this.fileIcon = fileIcon
30
+
31
+ this.path = this.getPath()
32
+ this.fileSelector = false
33
+ }
34
+
35
+ getPath() {
36
+ if (this.movingFiles.length === 1) {
37
+ let path = this.movingFiles[0].path
38
+ if (path.endsWith("/")) {
39
+ path +=
40
+ this.movingFiles[0].title.replace(/\//g, "") ||
41
+ gettext("Untitled")
42
+ }
43
+ return path
44
+ }
45
+ // We are moving several files. We assume they are all in the same directory
46
+ // so we only need to take the file of the first file.
47
+ return this.movingFiles[0].path.split("/").slice(0, -1).join("/") + "/"
48
+ }
49
+
50
+ updatePathDir(path) {
51
+ const fileName = this.dialog.dialogEl
52
+ .querySelector("#path")
53
+ .value.split("/")
54
+ .pop()
55
+ this.dialog.dialogEl.querySelector("#path").value = path + fileName
56
+ }
57
+
58
+ init() {
59
+ this.dialog = new Dialog({
60
+ title: this.title,
61
+ id: "move-dialog",
62
+ width: 820,
63
+ height: 440,
64
+ body: moveTemplate({
65
+ path: this.path
66
+ }),
67
+ buttons: [
68
+ {
69
+ text: gettext("New folder"),
70
+ classes: "fw-dark",
71
+ click: () => {
72
+ const dialog = new NewFolderDialog(folderName => {
73
+ if (!this.fileSelector) {
74
+ return
75
+ }
76
+ this.fileSelector.addFolder(folderName)
77
+ })
78
+ dialog.open()
79
+ }
80
+ },
81
+ {type: "cancel"},
82
+ {
83
+ text: gettext("Submit"),
84
+ classes: "fw-dark",
85
+ click: () => {
86
+ //apply the current state to server
87
+ let path =
88
+ this.dialog.dialogEl.querySelector("#path").value
89
+ this.dialog.close()
90
+
91
+ if (path === this.path) {
92
+ // No change
93
+ return
94
+ }
95
+ if (this.movingFiles.length > 1) {
96
+ if (!path.endsWith("/")) {
97
+ path += "/"
98
+ }
99
+ this.movingFiles.forEach(doc => {
100
+ this.moveFile(
101
+ doc,
102
+ doc.path.endsWith("/")
103
+ ? path
104
+ : path + doc.path.split("/").pop()
105
+ )
106
+ })
107
+ } else {
108
+ this.moveFile(this.movingFiles[0], path)
109
+ }
110
+ }
111
+ }
112
+ ]
113
+ })
114
+ this.dialog.open()
115
+
116
+ this.fileSelector = new FileSelector({
117
+ dom: this.dialog.dialogEl.querySelector(".file-selector"),
118
+ files: this.allFiles,
119
+ showFiles: false,
120
+ selectDir: path => this.updatePathDir(path),
121
+ fileIcon: this.fileIcon
122
+ })
123
+ this.fileSelector.init()
124
+ }
125
+
126
+ moveFile(file, requestedPath) {
127
+ return moveFile(file.id, file.title, requestedPath, this.moveUrl)
128
+ .then(path => {
129
+ addAlert(
130
+ "success",
131
+ `${this.successMessage}: '${shortFileTitle(file.title, path)}'`
132
+ )
133
+ this.succcessCallback(file, path)
134
+ })
135
+ .catch(() => {
136
+ addAlert(
137
+ "error",
138
+ `${this.errorMessage}: '${shortFileTitle(file.title, file.path)}'`
139
+ )
140
+ })
141
+ }
142
+ }