@fiduswriter/books-document 0.1.27 → 0.1.29

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.
Files changed (32) hide show
  1. package/README.md +1 -1
  2. package/dist/ui/book_dialog/dialog.d.ts +98 -0
  3. package/dist/ui/book_dialog/dialog.d.ts.map +1 -0
  4. package/dist/ui/book_dialog/dialog.js +590 -0
  5. package/dist/ui/book_dialog/dialog.js.map +1 -0
  6. package/dist/ui/book_dialog/export.d.ts +81 -0
  7. package/dist/ui/book_dialog/export.d.ts.map +1 -0
  8. package/dist/ui/book_dialog/export.js +184 -0
  9. package/dist/ui/book_dialog/export.js.map +1 -0
  10. package/dist/ui/book_dialog/index.d.ts +38 -0
  11. package/dist/ui/book_dialog/index.d.ts.map +1 -0
  12. package/dist/ui/book_dialog/index.js +36 -0
  13. package/dist/ui/book_dialog/index.js.map +1 -0
  14. package/dist/ui/book_dialog/sanity_check.d.ts +22 -0
  15. package/dist/ui/book_dialog/sanity_check.d.ts.map +1 -0
  16. package/dist/ui/book_dialog/sanity_check.js +109 -0
  17. package/dist/ui/book_dialog/sanity_check.js.map +1 -0
  18. package/dist/ui/book_dialog/templates.d.ts +74 -0
  19. package/dist/ui/book_dialog/templates.d.ts.map +1 -0
  20. package/dist/ui/book_dialog/templates.js +526 -0
  21. package/dist/ui/book_dialog/templates.js.map +1 -0
  22. package/dist/ui/book_dialog/types.d.ts +165 -0
  23. package/dist/ui/book_dialog/types.d.ts.map +1 -0
  24. package/dist/ui/book_dialog/types.js +15 -0
  25. package/dist/ui/book_dialog/types.js.map +1 -0
  26. package/package.json +20 -16
  27. package/src/ui/book_dialog/dialog.ts +765 -0
  28. package/src/ui/book_dialog/export.ts +398 -0
  29. package/src/ui/book_dialog/index.ts +81 -0
  30. package/src/ui/book_dialog/sanity_check.ts +208 -0
  31. package/src/ui/book_dialog/templates.ts +705 -0
  32. package/src/ui/book_dialog/types.ts +180 -0
@@ -0,0 +1,705 @@
1
+ /**
2
+ * HTML templates of the book dialog.
3
+ *
4
+ * One function per dialog pane plus the tab strip that holds them. Field ids
5
+ * are fixed: the dialog reads them back in `./dialog.js` when saving.
6
+ */
7
+
8
+ import {LANGUAGES} from "@fiduswriter/document/schema/const"
9
+ import {escapeText, gettext, longFilePath} from "fwtoolkit"
10
+ import type {Book, BookStyle, Chapter, DocumentListEntry} from "../../types.js"
11
+ import type {BookDialogInfo, BookDialogPart} from "./types.js"
12
+
13
+ /*
14
+ * The `rights` field is a Django-backend notion. A book that has no access
15
+ * rights at all -- a local `.fidusbook` file, say -- is editable, so the
16
+ * templates only disable fields for an explicitly read-only book.
17
+ */
18
+ const isReadOnly = (book: Book): boolean => book.rights === "read"
19
+
20
+ /** A template for the basic info book template pane */
21
+ export const bookBasicInfoTemplate = ({book}: {book: Book}) =>
22
+ `<table class="fw-dialog-table">
23
+ <tbody>
24
+ <tr>
25
+ <th>
26
+ <h4 class="fw-tablerow-title">${gettext("Title")}</h4>
27
+ </th>
28
+ <td>
29
+ <input class="entry-form" type="text" id="book-title"
30
+ value="${escapeText(book.title)}"
31
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
32
+ >
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <th>
37
+ <h4 class="fw-tablerow-title">${gettext("Author")}</h4>
38
+ </th>
39
+ <td>
40
+ <input class="entry-form" type="text" id="book-metadata-author"
41
+ value="${escapeText(book.metadata.author ?? "")}"
42
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
43
+ >
44
+ </td>
45
+ </tr>
46
+ <tr>
47
+ <th>
48
+ <h4 class="fw-tablerow-title">${gettext("Subtitle")}</h4>
49
+ </th>
50
+ <td>
51
+ <input class="entry-form" type="text" id="book-metadata-subtitle"
52
+ value="${escapeText(book.metadata.subtitle ?? "")}"
53
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
54
+ >
55
+ </td>
56
+ </tr>
57
+ <tr>
58
+ <th>
59
+ <h4 class="fw-tablerow-title">${gettext("Version")}</h4>
60
+ </th>
61
+ <td>
62
+ <input class="entry-form" type="text" id="book-metadata-version"
63
+ value="${escapeText(book.metadata.version ?? "")}"
64
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
65
+ >
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <th>
70
+ <h4 class="fw-tablerow-title">${gettext("Language")}</h4>
71
+ </th>
72
+ <td>
73
+ <div class="fw-select-container">
74
+ <select class="entry-form dk fw-button fw-light fw-large" name="book-settings-language"
75
+ title="${gettext("Select language")}"
76
+ id="book-settings-language"
77
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
78
+ >
79
+ ${LANGUAGES.map(
80
+ language =>
81
+ `<option value="${language[0]}"
82
+ ${
83
+ language[0] ===
84
+ book.settings.language
85
+ ? "selected"
86
+ : ""
87
+ }
88
+ >
89
+ ${escapeText(language[1])}
90
+ </option>`
91
+ ).join("")}
92
+ </select>
93
+ <div class="fw-select-arrow fa fa-caret-down"></div>
94
+ </div>
95
+ </td>
96
+ </tr>
97
+ <tr>
98
+ <th>
99
+ <h4 class="fw-tablerow-title">${gettext("Publisher")}</h4>
100
+ </th>
101
+ <td>
102
+ <input class="entry-form" type="text" id="book-metadata-publisher"
103
+ value="${escapeText(book.metadata.publisher ?? "")}"
104
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
105
+ >
106
+ </td>
107
+ </tr>
108
+ <tr>
109
+ <th>
110
+ <h4 class="fw-tablerow-title">${gettext(
111
+ "Copyright notice"
112
+ )}</h4>
113
+ </th>
114
+ <td>
115
+ <input class="entry-form" type="text" id="book-metadata-copyright"
116
+ value="${escapeText(book.metadata.copyright ?? "")}"
117
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
118
+ >
119
+ </td>
120
+ </tr>
121
+ <tr>
122
+ <th>
123
+ <h4 class="fw-tablerow-title" title="${gettext(
124
+ "Comma separated keywords"
125
+ )}">
126
+ ${gettext("Keywords")}
127
+ </h4>
128
+ </th>
129
+ <td>
130
+ <input class="entry-form" type="text" id="book-metadata-keywords"
131
+ value="${book.metadata.keywords}"
132
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
133
+ >
134
+ </td>
135
+ </tr>
136
+ <tr>
137
+ <th>
138
+ <h4 class="fw-tablerow-title">${gettext("Description")}</h4>
139
+ </th>
140
+ <td>
141
+ <textarea class="entry-form" id="book-metadata-description" rows="4"
142
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
143
+ >${escapeText(book.metadata.description ?? "")}</textarea>
144
+ </td>
145
+ </tr>
146
+ <tr>
147
+ <th>
148
+ <h4 class="fw-tablerow-title">${gettext("ISBN")}</h4>
149
+ </th>
150
+ <td>
151
+ <input class="entry-form" type="text" id="book-metadata-isbn"
152
+ value="${escapeText(book.metadata.isbn ?? "")}"
153
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
154
+ >
155
+ </td>
156
+ </tr>
157
+ <tr>
158
+ <th>
159
+ <h4 class="fw-tablerow-title" title="${gettext("E.g.: 2024 or January 2024")}">
160
+ ${gettext("Publication date")}
161
+ </h4>
162
+ </th>
163
+ <td>
164
+ <input class="entry-form" type="text" id="book-metadata-publication-date"
165
+ value="${escapeText(book.metadata.publication_date ?? "")}"
166
+ placeholder="${gettext("E.g.: 2024")}"
167
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
168
+ >
169
+ </td>
170
+ </tr>
171
+ <tr>
172
+ <th>
173
+ <h4 class="fw-tablerow-title">${gettext("Series title")}</h4>
174
+ </th>
175
+ <td>
176
+ <input class="entry-form" type="text" id="book-metadata-series-title"
177
+ value="${escapeText(book.metadata.series_title ?? "")}"
178
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
179
+ >
180
+ </td>
181
+ </tr>
182
+ <tr>
183
+ <th>
184
+ <h4 class="fw-tablerow-title">${gettext("Series position")}</h4>
185
+ </th>
186
+ <td>
187
+ <input class="entry-form" type="text" id="book-metadata-series-position"
188
+ value="${escapeText(book.metadata.series_position ?? "")}"
189
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
190
+ >
191
+ </td>
192
+ </tr>
193
+ </tbody>
194
+ </table>`
195
+
196
+ /** A template for the citation style pane of the book dialog */
197
+ export const bookBibliographyDataTemplate = ({
198
+ book,
199
+ citationStyles
200
+ }: {
201
+ book: Book
202
+ citationStyles: Array<{id: string; title?: string}>
203
+ }) =>
204
+ `<table class="fw-dialog-table">
205
+ <tbody>
206
+ <tr>
207
+ <th>
208
+ <h4 class="fw-tablerow-title">${gettext("Citation style")}</h4>
209
+ </th>
210
+ <td>
211
+ <div class="fw-select-container">
212
+ <select class="entry-form dk fw-button fw-light fw-large" name="book-settings-citationstyle"
213
+ title="${gettext(
214
+ "Select citation style for the book"
215
+ )}"
216
+ id="book-settings-citationstyle"
217
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
218
+ >
219
+ ${Object.entries(citationStyles)
220
+ .map(
221
+ ([key, value]) =>
222
+ `<option value="${key}" ${
223
+ key === book.settings.citationstyle
224
+ ? "selected"
225
+ : ""
226
+ }>
227
+ ${value}
228
+ </option>`
229
+ )
230
+ .join("")}
231
+ </select>
232
+ <div class="fw-select-arrow fa fa-caret-down"></div>
233
+ </div>
234
+ </td>
235
+ </tr>
236
+ </tbody>
237
+ </table>`
238
+
239
+ const paperSizes = [
240
+ ["folio", gettext("Folio (15 x 12 inch)")],
241
+ ["quarto", gettext("Quarto (12 × 9 inch)")],
242
+ ["octavo", gettext("Octavo (9 x 6 inch)")],
243
+ ["a5", gettext("A5")],
244
+ ["a4", gettext("A4")]
245
+ ]
246
+
247
+ /** A template for the print related data pane of the book dialog */
248
+ export const bookPrintDataTemplate = ({
249
+ book,
250
+ bookStyleList
251
+ }: {
252
+ book: Book
253
+ bookStyleList: BookStyle[]
254
+ }) =>
255
+ `<table class="fw-dialog-table">
256
+ <tbody>
257
+ <tr>
258
+ <th>
259
+ <h4 class="fw-tablerow-title">${gettext("Book style")}</h4>
260
+ </th>
261
+ <td>
262
+ <div class="fw-select-container">
263
+ <select class="entry-form dk fw-button fw-light fw-large" name="book-settings-bookstyle"
264
+ title="${gettext(
265
+ "Select stylesheet for the book"
266
+ )}"
267
+ id="book-settings-bookstyle"
268
+ ${
269
+ isReadOnly(book) || !bookStyleList.length
270
+ ? 'disabled="disabled"'
271
+ : ""
272
+ }
273
+ >
274
+ ${bookStyleList
275
+ .map(
276
+ (bookStyle: BookStyle) =>
277
+ `<option value="${bookStyle.slug}"
278
+ ${
279
+ bookStyle.slug ===
280
+ book.settings.book_style
281
+ ? "selected"
282
+ : ""
283
+ }
284
+ >
285
+ ${escapeText((bookStyle.title as string) ?? "")}
286
+ </option>`
287
+ )
288
+ .join("")}
289
+ </select>
290
+ <div class="fw-select-arrow fa fa-caret-down"></div>
291
+ </div>
292
+ </td>
293
+ </tr>
294
+ <tr>
295
+ <th>
296
+ <h4 class="fw-tablerow-title">${gettext("Paper size")}</h4>
297
+ </th>
298
+ <td>
299
+ <div class="fw-select-container">
300
+ <select class="entry-form dk fw-button fw-light fw-large" name="book-settings-papersize"
301
+ title="${gettext(
302
+ "Select paper size for the book"
303
+ )}"
304
+ id="book-settings-papersize"
305
+ ${isReadOnly(book) ? 'disabled="disabled"' : ""}
306
+ >
307
+ ${paperSizes
308
+ .map(
309
+ size =>
310
+ `<option value="${size[0]}" ${
311
+ size[0] ===
312
+ book.settings.papersize
313
+ ? "selected"
314
+ : ""
315
+ }>
316
+ ${size[1]}
317
+ </option>`
318
+ )
319
+ .join("")}
320
+ </select>
321
+ <div class="fw-select-arrow fa fa-caret-down"></div>
322
+ </div>
323
+ </td>
324
+ </tr>
325
+ </tbody>
326
+ </table>`
327
+
328
+ /** A template for the cover image input on the epub pane of the book dialog. */
329
+ export const bookEpubDataCoverTemplate = ({
330
+ book,
331
+ imageDB
332
+ }: {
333
+ book: Book
334
+ imageDB: BookDialogInfo["imageDB"]
335
+ }) =>
336
+ `<th class="figure-preview-row">
337
+ <h4 class="fw-tablerow-title">${gettext("Cover image")}</h4>
338
+ </th>
339
+ <td>
340
+ ${
341
+ book.cover_image
342
+ ? `<div class="img" style="background-image: url(${
343
+ imageDB.db[book.cover_image].image
344
+ });" title="${gettext("Cover image")}"></div>`
345
+ : ""
346
+ }
347
+ </td>
348
+ ${
349
+ isReadOnly(book)
350
+ ? ""
351
+ : `<td class="figure-preview-row">
352
+ <button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
353
+ ui-corner-all ui-button-text-only fw-button fw-dark"
354
+ id="select-cover-image-button" role="button" aria-disabled="false" title="${gettext(
355
+ "Select a cover image"
356
+ )}">
357
+ <span class="ui-button-text">${gettext("Select Image")}</span>
358
+ </button>
359
+ ${
360
+ book.cover_image
361
+ ? `<button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
362
+ ui-corner-all ui-button-text-only fw-button fw-orange"
363
+ id="remove-cover-image-button" role="button" aria-disabled="false" title="${gettext(
364
+ "Remove cover image"
365
+ )}">
366
+ <span class="ui-button-text">${gettext("Remove Image")}</span>
367
+ </button>`
368
+ : ""
369
+ }
370
+ </td>`
371
+ }`
372
+
373
+ /** A template for the epub related data pane of the book dialog */
374
+ export const bookEpubDataTemplate = ({
375
+ book,
376
+ imageDB
377
+ }: {
378
+ book: Book
379
+ imageDB: BookDialogInfo["imageDB"]
380
+ }) =>
381
+ `<table class="fw-dialog-table fw-media-uploader">
382
+ <tbody>
383
+ <tr id="cover-preview-row">
384
+ ${bookEpubDataCoverTemplate({
385
+ book,
386
+ imageDB
387
+ })}
388
+ </tr>
389
+ </tbody>
390
+ </table>`
391
+
392
+ /** A template for the ODT template row of the book dialog. */
393
+ export const bookODTDataRowTemplate = ({book}: {book: Book}) =>
394
+ `<th class="odt-file-row">
395
+ <h4 class="fw-tablerow-title">${gettext("ODT Template")}</h4>
396
+ </th>
397
+ <td>
398
+ ${
399
+ book.odt_template
400
+ ? `<a href="${book.odt_template}" id="odt-template-file-url" download>${escapeText(
401
+ book.odt_template
402
+ )}</a>`
403
+ : ""
404
+ }
405
+ </td>
406
+ ${
407
+ isReadOnly(book)
408
+ ? ""
409
+ : `<td class="odt-template-row">
410
+ <button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
411
+ ui-corner-all ui-button-text-only fw-button fw-dark"
412
+ id="select-odt-template" role="button" aria-disabled="false"
413
+ title="${gettext("Select an ODT template file")}">
414
+ <span class="ui-button-text">${gettext(
415
+ "Select ODT template file"
416
+ )}</span>
417
+ </button>
418
+ <input type="file" id="input-odt-template" accept=".odt" class="fw-media-file-input">
419
+ ${
420
+ book.odt_template
421
+ ? `<button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
422
+ ui-corner-all ui-button-text-only fw-button fw-orange"
423
+ id="remove-odt-template-button" role="button" aria-disabled="false"
424
+ title="${gettext("Remove ODT template file")}">
425
+ <span class="ui-button-text">
426
+ ${gettext("Remove ODT template file")}
427
+ </span>
428
+ </button>`
429
+ : ""
430
+ }
431
+ </td>`
432
+ }`
433
+
434
+ /** A template for the ODT related data pane of the book dialog */
435
+ export const bookODTDataTemplate = ({book}: {book: Book}) =>
436
+ `<table class="fw-dialog-table">
437
+ <tbody>
438
+ <tr id="odt-template-row">
439
+ ${bookODTDataRowTemplate({book})}
440
+ </tr>
441
+ </tbody>
442
+ </table>`
443
+
444
+ /** A template for the DOCX template row of the book dialog. */
445
+ export const bookDOCXDataRowTemplate = ({book}: {book: Book}) =>
446
+ `<th class="docx-file-row">
447
+ <h4 class="fw-tablerow-title">${gettext("DOCX Template")}</h4>
448
+ </th>
449
+ <td>
450
+ ${
451
+ book.docx_template
452
+ ? `<a href="${book.docx_template}" id="docx-template-file-url" download>${escapeText(
453
+ book.docx_template
454
+ )}</a>`
455
+ : ""
456
+ }
457
+ </td>
458
+ ${
459
+ isReadOnly(book)
460
+ ? ""
461
+ : `<td class="docx-template-row">
462
+ <button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
463
+ ui-corner-all ui-button-text-only fw-button fw-dark"
464
+ id="select-docx-template" role="button" aria-disabled="false"
465
+ title="${gettext("Select an DOCX template file")}">
466
+ <span class="ui-button-text">${gettext(
467
+ "Select DOCX template file"
468
+ )}</span>
469
+ </button>
470
+ <input type="file" id="input-docx-template" accept=".docx" class="fw-media-file-input">
471
+ ${
472
+ book.docx_template
473
+ ? `<button type="button" class="fw-dialog-titlebar-button ui-widget ui-state-default
474
+ ui-corner-all ui-button-text-only fw-button fw-orange"
475
+ id="remove-docx-template-button" role="button" aria-disabled="false"
476
+ title="${gettext("Remove DOCX template file")}">
477
+ <span class="ui-button-text">
478
+ ${gettext("Remove DOCX template file")}
479
+ </span>
480
+ </button>`
481
+ : ""
482
+ }
483
+ </td>`
484
+ }`
485
+
486
+ /** A template for the DOCX related data pane of the book dialog */
487
+ export const bookDOCXDataTemplate = ({book}: {book: Book}) =>
488
+ `<table class="fw-dialog-table">
489
+ <tbody>
490
+ <tr id="docx-template-row">
491
+ ${bookDOCXDataRowTemplate({book})}
492
+ </tr>
493
+ </tbody>
494
+ </table>`
495
+
496
+ /** A template for the sanity check pane of the book dialog */
497
+ export const bookSanityCheckTemplate = () =>
498
+ `<table class="fw-dialog-table">
499
+ <tbody>
500
+ <tr>
501
+ <th>
502
+ <button type="button" id="perform-sanity-check-button" class="fw-dialog-titlebar-button fw-button fw-dark">${gettext(
503
+ "Perform sanity check"
504
+ )}</button>
505
+ </th>
506
+ <td id="sanity-check-output"></td>
507
+ </tr>
508
+ </tbody>
509
+ </table>`
510
+
511
+ /** A template for the book dialog. */
512
+ export const bookDialogTemplate = ({
513
+ title,
514
+ bookInfo,
515
+ dialogParts
516
+ }: {
517
+ title: string
518
+ bookInfo: BookDialogInfo
519
+ dialogParts: BookDialogPart[]
520
+ }) =>
521
+ `<div id="book-dialog" title="${title}">
522
+ <div id="bookoptions-tab">
523
+ <ul class="fw-tabs-nav">
524
+ ${dialogParts
525
+ .map(
526
+ (part, index) =>
527
+ `<li class="fw-tab-link ${index === 0 ? "fw-current-tab" : ""}">
528
+ <a href="#optionTab${index}" class="fw-tab-link-inner" title="${escapeText(
529
+ part.description
530
+ )}">
531
+ ${escapeText(part.title)}
532
+ </a>
533
+ </li>`
534
+ )
535
+ .join("")}
536
+ </ul>
537
+ ${dialogParts
538
+ .map(
539
+ (part, index) =>
540
+ `<div class="tab-content fw-tabs-panel" id="optionTab${index}" title="${escapeText(
541
+ part.description
542
+ )}">
543
+ ${part.template(bookInfo)}
544
+ </div>`
545
+ )
546
+ .join("")}
547
+ </div>
548
+ </div>`
549
+
550
+ /** A template for the chapter pane of the book dialog. */
551
+ export const bookDialogChaptersTemplate = ({
552
+ book,
553
+ documentList
554
+ }: {
555
+ book: Book
556
+ documentList: DocumentListEntry[]
557
+ }) => {
558
+ const hasEncryptedChapters = book.chapters.some(
559
+ chapter => documentList.find(doc => doc.id === chapter.text)?.e2ee
560
+ )
561
+ return `${
562
+ isReadOnly(book)
563
+ ? ""
564
+ : `<div class="fw-ar-container">
565
+ <h3 class="fw-green-title">${gettext("My documents")}</h3>
566
+ <div id="book-document-list"></div>
567
+ </div>
568
+ <span id="add-chapter" class="fw-button fw-large fw-square fw-light fw-ar-button">
569
+ <i class="fas fa-caret-right"></i>
570
+ </span>`
571
+ }
572
+ <div class="fw-ar-container">
573
+ <h3 class="fw-green-title">${gettext("Book chapters")}</h3>
574
+ <table class="fw-data-table">
575
+ <thead class="fw-data-table-header">
576
+ <tr>
577
+ <th colspan="2">${gettext("Title")}</th>
578
+ <th colspan="2">${gettext("Sort")}</th>
579
+ ${isReadOnly(book) ? "" : "<th>&emsp;</th>"}
580
+ </tr>
581
+ </thead>
582
+ <tbody class="fw-data-table-body fw-small" id="book-chapter-list">
583
+ ${bookChapterListTemplate({
584
+ book,
585
+ documentList
586
+ })}
587
+ </tbody>
588
+ </table>
589
+ ${
590
+ hasEncryptedChapters
591
+ ? `<p class="fw-note e2ee-chapter-notice">
592
+ <i class="fas fa-lock"></i>
593
+ ${gettext("This book contains encrypted chapters. A personal passphrase is required to export or run a sanity check on this book.")}
594
+ </p>`
595
+ : ""
596
+ }
597
+ </div>`
598
+ }
599
+
600
+ /** A template for the chapter list on the chapter pane the book dialog. */
601
+ export const bookChapterListTemplate = ({
602
+ book,
603
+ documentList
604
+ }: {
605
+ book: Book
606
+ documentList: DocumentListEntry[]
607
+ }) => {
608
+ let partCounter = 1
609
+ return book.chapters
610
+ .slice()
611
+ .sort((a: Chapter, b: Chapter) => a.number - b.number)
612
+ .map((chapter: Chapter, index: number, array: Chapter[]) => {
613
+ const doc = documentList.find(doc => doc.id === chapter.text)
614
+ return `<tr
615
+ ${doc ? "" : 'class="noaccess"'}
616
+ >
617
+ <td width="222" data-id="${
618
+ chapter.text
619
+ }" class="fw-checkable-td" ${
620
+ doc
621
+ ? `title="${longFilePath(
622
+ doc.title ?? "",
623
+ doc.path ?? ""
624
+ )}"`
625
+ : ""
626
+ }>
627
+ <span class="fw-inline">
628
+ ${doc ? "" : '<i class="fas fa-minus-circle"></i>'}
629
+ ${doc && doc.e2ee ? `<i class="fas fa-lock e2ee-chapter-icon" title="${gettext("Encrypted document")}"></i>` : ""}
630
+ ${
631
+ (chapter.part ?? "").length
632
+ ? `<b class="part">
633
+ ${partCounter++}. ${gettext("Book part")}:
634
+ ${escapeText(chapter.part ?? "")}
635
+ </b>
636
+ <br>`
637
+ : ""
638
+ }
639
+ ${chapter.number}
640
+ ${
641
+ doc
642
+ ? doc.title
643
+ ? doc.title
644
+ : gettext("Untitled")
645
+ : gettext("No access")
646
+ }
647
+ </span>
648
+ </td>
649
+ ${
650
+ isReadOnly(book)
651
+ ? ""
652
+ : `<td width="30" data-id="${chapter.text}" class="edit-chapter">
653
+ <i class="fas fa-pencil-alt fw-link-text"></i>
654
+ </td>
655
+ ${
656
+ index === 0
657
+ ? '<td width="10"></td>'
658
+ : `<td width="10" class="book-sort-up" data-id="${chapter.text}">
659
+ <i class="fas fa-sort-up"></i>
660
+ </td>`
661
+ }
662
+ ${
663
+ index + 1 === array.length
664
+ ? '<td width="10"></td>'
665
+ : `<td width="10" class="book-sort-down" data-id="${chapter.text}">
666
+ <i class="fas fa-sort-down"></i>
667
+ </td>`
668
+ }
669
+ <td width="50" align="center">
670
+ <span class="delete-chapter fw-inline" data-id="${
671
+ chapter.text
672
+ }">
673
+ <i class="fas fa-trash-alt fw-link-text"></i>
674
+ </span>
675
+ </td>`
676
+ }${
677
+ isReadOnly(book)
678
+ ? `<td width="30"></td>
679
+ <td width="10"></td>
680
+ <td width="10"></td>
681
+ <td width="50"></td>`
682
+ : ""
683
+ }
684
+ </tr>`
685
+ })
686
+ .join("")
687
+ }
688
+
689
+ /** A template for the chapter dialog for books */
690
+ export const bookChapterDialogTemplate = ({chapter}: {chapter: Chapter}) =>
691
+ `<table class="fw-dialog-table">
692
+ <tr>
693
+ <th>
694
+ <h4 title="${gettext(
695
+ "If this chapter starts a part of the book, specify the title of that part here"
696
+ )}">
697
+ ${gettext("Book part title")}
698
+ </h4>
699
+ </th>
700
+ <td>
701
+ <input type="text" id="book-chapter-part"
702
+ value="${escapeText(chapter.part ?? "")}">
703
+ </td>
704
+ </tr>
705
+ </table>`