@fiduswriter/bibliography-manager 0.1.0

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 (105) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +20 -0
  3. package/dist/database/index.js +172 -0
  4. package/dist/database/index.js.map +1 -0
  5. package/dist/database/server_connector.js +47 -0
  6. package/dist/database/server_connector.js.map +1 -0
  7. package/dist/export/index.js +23 -0
  8. package/dist/export/index.js.map +1 -0
  9. package/dist/form/cats.js +22 -0
  10. package/dist/form/cats.js.map +1 -0
  11. package/dist/form/fields/date.js +28 -0
  12. package/dist/form/fields/date.js.map +1 -0
  13. package/dist/form/fields/key.js +100 -0
  14. package/dist/form/fields/key.js.map +1 -0
  15. package/dist/form/fields/key_list.js +37 -0
  16. package/dist/form/fields/key_list.js.map +1 -0
  17. package/dist/form/fields/literal.js +43 -0
  18. package/dist/form/fields/literal.js.map +1 -0
  19. package/dist/form/fields/literal_list.js +36 -0
  20. package/dist/form/fields/literal_list.js.map +1 -0
  21. package/dist/form/fields/literal_long.js +42 -0
  22. package/dist/form/fields/literal_long.js.map +1 -0
  23. package/dist/form/fields/name.js +121 -0
  24. package/dist/form/fields/name.js.map +1 -0
  25. package/dist/form/fields/name_list.js +36 -0
  26. package/dist/form/fields/name_list.js.map +1 -0
  27. package/dist/form/fields/range.js +89 -0
  28. package/dist/form/fields/range.js.map +1 -0
  29. package/dist/form/fields/range_list.js +36 -0
  30. package/dist/form/fields/range_list.js.map +1 -0
  31. package/dist/form/fields/tag_list.js +30 -0
  32. package/dist/form/fields/tag_list.js.map +1 -0
  33. package/dist/form/fields/title.js +46 -0
  34. package/dist/form/fields/title.js.map +1 -0
  35. package/dist/form/fields/uri.js +40 -0
  36. package/dist/form/fields/uri.js.map +1 -0
  37. package/dist/form/fields/verbatim.js +19 -0
  38. package/dist/form/fields/verbatim.js.map +1 -0
  39. package/dist/form/index.js +344 -0
  40. package/dist/form/index.js.map +1 -0
  41. package/dist/form/strings.js +89 -0
  42. package/dist/form/strings.js.map +1 -0
  43. package/dist/form/templates.js +59 -0
  44. package/dist/form/templates.js.map +1 -0
  45. package/dist/import/bibliography_import.js +83 -0
  46. package/dist/import/bibliography_import.js.map +1 -0
  47. package/dist/import/dialog.js +71 -0
  48. package/dist/import/dialog.js.map +1 -0
  49. package/dist/import/index.js +3 -0
  50. package/dist/import/index.js.map +1 -0
  51. package/dist/import/templates.js +12 -0
  52. package/dist/import/templates.js.map +1 -0
  53. package/dist/index.js +6 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/overview/index.js +453 -0
  56. package/dist/overview/index.js.map +1 -0
  57. package/dist/overview/menu.js +91 -0
  58. package/dist/overview/menu.js.map +1 -0
  59. package/dist/overview/templates.js +24 -0
  60. package/dist/overview/templates.js.map +1 -0
  61. package/dist/plugins/bibliography_overview/index.js +2 -0
  62. package/dist/plugins/bibliography_overview/index.js.map +1 -0
  63. package/dist/schema/literal.js +24 -0
  64. package/dist/schema/literal.js.map +1 -0
  65. package/dist/schema/literal_long.js +40 -0
  66. package/dist/schema/literal_long.js.map +1 -0
  67. package/dist/schema/title.js +31 -0
  68. package/dist/schema/title.js.map +1 -0
  69. package/dist/tools.js +53 -0
  70. package/dist/tools.js.map +1 -0
  71. package/package.json +55 -0
  72. package/src/database/index.js +234 -0
  73. package/src/database/server_connector.js +57 -0
  74. package/src/export/index.js +27 -0
  75. package/src/form/cats.js +24 -0
  76. package/src/form/fields/date.js +31 -0
  77. package/src/form/fields/key.js +113 -0
  78. package/src/form/fields/key_list.js +45 -0
  79. package/src/form/fields/literal.js +48 -0
  80. package/src/form/fields/literal_list.js +42 -0
  81. package/src/form/fields/literal_long.js +47 -0
  82. package/src/form/fields/name.js +152 -0
  83. package/src/form/fields/name_list.js +42 -0
  84. package/src/form/fields/range.js +104 -0
  85. package/src/form/fields/range_list.js +42 -0
  86. package/src/form/fields/tag_list.js +33 -0
  87. package/src/form/fields/title.js +51 -0
  88. package/src/form/fields/uri.js +43 -0
  89. package/src/form/fields/verbatim.js +21 -0
  90. package/src/form/index.js +424 -0
  91. package/src/form/strings.js +122 -0
  92. package/src/form/templates.js +70 -0
  93. package/src/import/bibliography_import.js +104 -0
  94. package/src/import/dialog.js +95 -0
  95. package/src/import/index.js +2 -0
  96. package/src/import/templates.js +12 -0
  97. package/src/index.js +5 -0
  98. package/src/overview/index.js +548 -0
  99. package/src/overview/menu.js +106 -0
  100. package/src/overview/templates.js +30 -0
  101. package/src/plugins/bibliography_overview/index.js +1 -0
  102. package/src/schema/literal.js +34 -0
  103. package/src/schema/literal_long.js +50 -0
  104. package/src/schema/title.js +42 -0
  105. package/src/tools.js +58 -0
@@ -0,0 +1,548 @@
1
+ import fixUTF8 from "fix-utf8"
2
+
3
+ import {
4
+ Dialog,
5
+ OverviewDataTable,
6
+ OverviewMenuView,
7
+ addAlert,
8
+ ensureCSS,
9
+ escapeText,
10
+ findTarget,
11
+ isActivationEvent,
12
+ setDocTitle,
13
+ whenReady
14
+ } from "fwtoolkit"
15
+ import {baseBodyTemplate, FeedbackTab, SiteMenu} from "@fiduswriter/common"
16
+ import {plugins} from "../../../plugins/bibliography_overview"
17
+ import {getBibTypeTitle} from "../form/strings"
18
+ import {litToText, nameToText} from "../tools"
19
+ import {bulkMenuModel, menuModel} from "./menu"
20
+ import {editCategoriesTemplate} from "./templates"
21
+
22
+ export class BibliographyOverview {
23
+ constructor({app, user}) {
24
+ this.app = app
25
+ this.user = user
26
+
27
+ this.lastSort = {column: 0, dir: "asc"}
28
+ }
29
+
30
+ /** Bind the init function to doc loading.
31
+ * @function bind
32
+ */
33
+ init() {
34
+ return whenReady().then(() => {
35
+ this.render()
36
+ const smenu = new SiteMenu(this.app, "bibliography")
37
+ smenu.init()
38
+ this.menu = new OverviewMenuView(this, menuModel)
39
+ this.menu.init()
40
+ this.setBibCategoryList(this.app.bibDB.cats)
41
+ this.initTable(Object.keys(this.app.bibDB.db))
42
+ // Reset scroll position to top to prevent Safari from auto-scrolling
43
+ // to the focused table element, which would hide the header/menu
44
+ window.scrollTo(0, 0)
45
+ this.activatePlugins()
46
+ this.bindEvents()
47
+ })
48
+ }
49
+
50
+ render() {
51
+ this.dom = document.createElement("body")
52
+ this.dom.innerHTML = baseBodyTemplate({
53
+ contents: "",
54
+ user: this.user,
55
+ hasOverview: true,
56
+ app: this.app
57
+ })
58
+ document.body = this.dom
59
+ ensureCSS([
60
+ staticUrl("css/bibliography.css"),
61
+ staticUrl("css/prosemirror.css"),
62
+ staticUrl("css/inline_tools.css")
63
+ ])
64
+ setDocTitle(gettext("Bibliography Manager"), this.app)
65
+ const feedbackTab = new FeedbackTab()
66
+ feedbackTab.init()
67
+ }
68
+
69
+ onResize() {
70
+ if (!this.table) {
71
+ return
72
+ }
73
+ this.initTable(Object.keys(this.app.bibDB.db))
74
+ }
75
+
76
+ /* Initialize the overview table */
77
+ initTable(ids) {
78
+ if (this.overviewTable) {
79
+ this.overviewTable.destroy()
80
+ this.overviewTable = null
81
+ }
82
+ this.table = null
83
+ this.dtBulk = null
84
+
85
+ const contentsEl = this.dom.querySelector(".fw-contents")
86
+ contentsEl.innerHTML = ""
87
+
88
+ const hiddenCols = [0]
89
+
90
+ if (window.innerWidth < 500) {
91
+ hiddenCols.push(1)
92
+ if (window.innerWidth < 450) {
93
+ hiddenCols.push(3)
94
+ }
95
+ }
96
+
97
+ this.overviewTable = new OverviewDataTable({
98
+ dom: contentsEl,
99
+ classes: ["fw-data-table", "fw-large"],
100
+ columns: [
101
+ {
102
+ select: 0,
103
+ type: "number"
104
+ },
105
+ {
106
+ select: 1,
107
+ type: "boolean",
108
+ sortable: false
109
+ },
110
+ {
111
+ select: hiddenCols,
112
+ hidden: true
113
+ },
114
+ {
115
+ select: 6,
116
+ sortable: false
117
+ },
118
+ {
119
+ select: [this.lastSort.column],
120
+ sort: this.lastSort.dir
121
+ }
122
+ ],
123
+ data: ids.map(id => this.createTableRow(id)),
124
+ idColumn: 0,
125
+ checkboxColumn: 1,
126
+ bulkMenu: bulkMenuModel(),
127
+ bulkMenuPage: this,
128
+ searchable: true,
129
+ scrollY: `${Math.max(window.innerHeight - 360, 100)}px`,
130
+ tabIndex: 1,
131
+ labels: {
132
+ noRows: gettext("No sources registered"),
133
+ noResults: gettext("No sources found") // Message shown when there are no search results
134
+ },
135
+ headings: [
136
+ "",
137
+ "",
138
+ gettext("Title"),
139
+ gettext("Sourcetype"),
140
+ gettext("Author"),
141
+ gettext("Published"),
142
+ ""
143
+ ],
144
+ template: (options, _dom) =>
145
+ `<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>`,
146
+ rowRender: (row, tr, _index) => {
147
+ const id = row.cells[0].data
148
+ const inputNode = {
149
+ nodeName: "input",
150
+ attributes: {
151
+ type: "checkbox",
152
+ class: "entry-select fw-check",
153
+ "data-id": id,
154
+ id: `bib-${id}`
155
+ }
156
+ }
157
+ if (row.cells[1].data) {
158
+ inputNode.attributes.checked = true
159
+ }
160
+ tr.childNodes[0].childNodes = [
161
+ inputNode,
162
+ {
163
+ nodeName: "label",
164
+ attributes: {
165
+ for: `bib-${id}`
166
+ }
167
+ }
168
+ ]
169
+ },
170
+ onEnter: (row, _event) => {
171
+ if (this.getSelected().length > 0) {
172
+ return
173
+ }
174
+ const rowIndex = this.table.data.data.indexOf(row)
175
+ const editButton = this.table.dom.querySelector(
176
+ `tr[data-index="${rowIndex}"] span.edit-bib`
177
+ )
178
+ if (editButton) {
179
+ editButton.click()
180
+ }
181
+ },
182
+ onDelete: row => {
183
+ const bibId = row.cells[0].data
184
+ this.deleteBibEntryDialog([bibId])
185
+ }
186
+ })
187
+ this.overviewTable.init()
188
+ this.table = this.overviewTable.table
189
+ this.table.id = "bibliography"
190
+ this.dtBulk = this.overviewTable.dtBulk
191
+
192
+ this.table.on("datatable.sort", (column, dir) => {
193
+ this.lastSort = {column, dir}
194
+ })
195
+
196
+ this.table.dom.focus()
197
+ }
198
+
199
+ /** Adds a list of bibliography categories to current list of bibliography categories.
200
+ * @function setBibCategoryList
201
+ * @param newBibCategories The new categories which will be added to the existing ones.
202
+ */
203
+ setBibCategoryList(bibCategories) {
204
+ const catSelector = this.menu.model.content.find(
205
+ menuItem => menuItem.id === "cat_selector"
206
+ )
207
+ catSelector.content = catSelector.content.filter(
208
+ cat => cat.type !== "category"
209
+ )
210
+
211
+ catSelector.content = catSelector.content.concat(
212
+ bibCategories.map(cat => ({
213
+ title: cat.category_title,
214
+ type: "category",
215
+ action: _overview => {
216
+ const trs = this.dom.querySelectorAll(
217
+ "#bibliography > tbody > tr"
218
+ )
219
+ trs.forEach(tr => {
220
+ if (
221
+ tr
222
+ .querySelector(".fw-data-table-title")
223
+ .classList.contains(`cat_${cat.id}`)
224
+ ) {
225
+ tr.style.display = ""
226
+ } else {
227
+ tr.style.display = "none"
228
+ }
229
+ })
230
+ }
231
+ }))
232
+ )
233
+ this.menu.update()
234
+ }
235
+
236
+ /** This takes a list of new bib entries and adds them to BibDB and the bibliography table
237
+ * @function updateTable
238
+ */
239
+ updateTable(ids) {
240
+ // Remove items that already exist
241
+ this.removeTableRows(ids)
242
+ this.table.insert({data: ids.map(id => this.createTableRow(id))})
243
+ // Redo last sort
244
+ this.table.columns.sort(this.lastSort.column, this.lastSort.dir)
245
+ }
246
+
247
+ createTableRow(id) {
248
+ const bibInfo = this.app.bibDB.db[id]
249
+ const bibauthors = bibInfo.fields.author || bibInfo.fields.editor
250
+ const cats = bibInfo.cats.map(cat => `cat_${cat}`)
251
+ return [
252
+ id,
253
+ false, // checkbox
254
+ `<span class="fw-data-table-title ${cats.join(" ")}">
255
+ <i class="fa fa-book"></i>
256
+ <span class="edit-bib fw-link-text fw-searchable" data-id="${id}">
257
+ ${bibInfo.fields.title?.length ? escapeText(litToText(bibInfo.fields.title)) : gettext("Untitled")}
258
+ </span>
259
+ ${bibInfo.entry_key ? `<small class="bib-entry-key">${escapeText(bibInfo.entry_key)}</small>` : ""}
260
+ </span>`, // title
261
+ getBibTypeTitle(bibInfo.bib_type), // sourcetype
262
+ bibauthors ? nameToText(bibauthors) : "", // author
263
+ `<span class="fw-date">${bibInfo.fields.date ? bibInfo.fields.date.replace("/", " ") : ""}</span>`, // published,
264
+ `<span class="delete-bib fw-link-text" data-id="${id}"><i class="fa fa-trash-alt"> </i></span>` // delete icon
265
+ ]
266
+ }
267
+
268
+ removeTableRows(ids) {
269
+ const existingRows = this.table.data.data
270
+ .map((row, index) => {
271
+ const id = row.cells[0].data
272
+ if (ids.includes(id)) {
273
+ return index
274
+ } else {
275
+ return false
276
+ }
277
+ })
278
+ .filter(rowIndex => rowIndex !== false)
279
+
280
+ if (existingRows.length) {
281
+ this.table.rows.remove(existingRows)
282
+ }
283
+ }
284
+
285
+ /** Opens a dialog for editing categories.
286
+ * @function editCategoriesDialog
287
+ */
288
+ editCategoriesDialog() {
289
+ if (this.app.isOffline()) {
290
+ addAlert(
291
+ "info",
292
+ gettext(
293
+ "You are currently offline. Please try again when you are back online."
294
+ )
295
+ )
296
+ return
297
+ }
298
+ const buttons = [
299
+ {
300
+ text: gettext("Submit"),
301
+ classes: "fw-dark",
302
+ click: () => {
303
+ const cats = {ids: [], titles: []}
304
+ this.dom
305
+ .querySelectorAll("#edit-categories .category-form")
306
+ .forEach(el => {
307
+ const title = el.value.trim()
308
+ if (title.length) {
309
+ cats.ids.push(
310
+ Number.parseInt(
311
+ el.getAttribute("data-id") || 0
312
+ )
313
+ )
314
+ cats.titles.push(title)
315
+ }
316
+ })
317
+ if (this.app.isOffline()) {
318
+ addAlert(
319
+ "info",
320
+ gettext(
321
+ "You are currently offline. Please try again when you are back online."
322
+ )
323
+ )
324
+ } else {
325
+ this.saveCategories(cats)
326
+ }
327
+ dialog.close()
328
+ }
329
+ },
330
+ {
331
+ type: "cancel"
332
+ }
333
+ ]
334
+
335
+ const dialog = new Dialog({
336
+ id: "edit-categories",
337
+ width: 350,
338
+ height: 350,
339
+ title: gettext("Edit Categories"),
340
+ body: editCategoriesTemplate({
341
+ categories: this.app.bibDB.cats
342
+ }),
343
+ buttons
344
+ })
345
+ dialog.open()
346
+ }
347
+
348
+ /** Dialog to confirm deletion of bibliography items.
349
+ * @function deleteBibEntryDialog
350
+ * @param ids Ids of items that are to be deleted.
351
+ */
352
+ deleteBibEntryDialog(ids) {
353
+ const buttons = [
354
+ {
355
+ text: gettext("Delete"),
356
+ classes: "fw-dark",
357
+ click: () => {
358
+ this.deleteBibEntries(ids)
359
+ dialog.close()
360
+ }
361
+ },
362
+ {
363
+ type: "cancel"
364
+ }
365
+ ]
366
+
367
+ const dialog = new Dialog({
368
+ id: "confirmdeletion",
369
+ title: gettext("Confirm deletion"),
370
+ body: `<p>${gettext("Delete the bibliography item(s)")}?</p>`,
371
+ buttons,
372
+ icon: "exclamation-triangle"
373
+ })
374
+ dialog.open()
375
+ }
376
+
377
+ // get IDs of selected bib entries
378
+ getSelected() {
379
+ return Array.from(
380
+ this.dom.querySelectorAll(".entry-select:checked:not(:disabled)")
381
+ ).map(el => Number.parseInt(el.getAttribute("data-id")))
382
+ }
383
+
384
+ activatePlugins() {
385
+ if (this.plugins) {
386
+ // Plugins have been activated already
387
+ return
388
+ }
389
+ // Add plugins.
390
+ this.plugins = {}
391
+
392
+ return Promise.all(
393
+ plugins.map(([app, plugin]) => {
394
+ if (!this.app.settings.APPS.includes(app)) {
395
+ return Promise.resolve()
396
+ }
397
+ return Promise.all(
398
+ Object.values(plugin).map(pluginExport => {
399
+ if (typeof pluginExport === "function") {
400
+ this.plugins[pluginExport.name] = new pluginExport(
401
+ this
402
+ )
403
+ return (
404
+ this.plugins[pluginExport.name].init() ||
405
+ Promise.resolve()
406
+ )
407
+ }
408
+ return Promise.resolve()
409
+ })
410
+ )
411
+ })
412
+ )
413
+ }
414
+
415
+ /** Initialize the bibliography table and bind interactive parts.
416
+ * @function bibEvents
417
+ */
418
+ bindEvents() {
419
+ this.dom.addEventListener("click", event =>
420
+ this.handleActivation(event)
421
+ )
422
+ this.dom.addEventListener("keydown", event =>
423
+ this.handleActivation(event)
424
+ )
425
+
426
+ // Allow pasting of bibtex data.
427
+ this.dom.addEventListener("paste", event => {
428
+ if (event.target.nodeName === "INPUT") {
429
+ // We are inside of an input element, cancel.
430
+ return false
431
+ }
432
+ const text = event.clipboardData.getData("text")
433
+ return this.getBibtex(text)
434
+ })
435
+
436
+ // The two drag events are needed to allow dropping
437
+ this.dom.addEventListener("dragover", event => {
438
+ if (event.dataTransfer.types.includes("text/plain")) {
439
+ event.preventDefault()
440
+ }
441
+ })
442
+
443
+ this.dom.addEventListener("dragenter", event => {
444
+ if (event.dataTransfer.types.includes("text/plain")) {
445
+ event.preventDefault()
446
+ }
447
+ })
448
+
449
+ // Allow dropping of bibtex data
450
+ this.dom.addEventListener("drop", event => {
451
+ if (event.target.nodeName === "INPUT") {
452
+ // We are inside of an input element, cancel.
453
+ return false
454
+ }
455
+ const text = fixUTF8(event.dataTransfer.getData("text"))
456
+ return this.getBibtex(text)
457
+ })
458
+ }
459
+
460
+ handleActivation(event) {
461
+ if (!isActivationEvent(event)) {
462
+ return
463
+ }
464
+ const el = {}
465
+ switch (true) {
466
+ case findTarget(event, ".delete-bib", el): {
467
+ const bibId = Number.parseInt(el.target.dataset.id)
468
+ this.deleteBibEntryDialog([bibId])
469
+ break
470
+ }
471
+ case findTarget(event, ".edit-bib", el): {
472
+ const bibId = Number.parseInt(el.target.dataset.id)
473
+ import("../form").then(({BibEntryForm}) => {
474
+ const form = new BibEntryForm(
475
+ this.app.bibDB,
476
+ this.app,
477
+ bibId
478
+ )
479
+ form.init().then(idTranslations => {
480
+ const ids = idTranslations.map(idTrans => idTrans[1])
481
+ return this.updateTable(ids)
482
+ })
483
+ })
484
+ break
485
+ }
486
+ case findTarget(event, ".fw-add-input", el): {
487
+ const itemEl = el.target.closest(".fw-list-input")
488
+ if (!itemEl.nextElementSibling) {
489
+ itemEl.insertAdjacentHTML(
490
+ "afterend",
491
+ `<tr class="fw-list-input">
492
+ <td>
493
+ <input type="text" class="category-form">
494
+ <span class="fw-add-input icon-addremove" tabindex="0"></span>
495
+ </td>
496
+ </tr>`
497
+ )
498
+ } else {
499
+ itemEl.parentElement.removeChild(itemEl)
500
+ }
501
+ break
502
+ }
503
+ default:
504
+ break
505
+ }
506
+ }
507
+
508
+ // find bibtex in pasted or dropped data.
509
+ getBibtex(text) {
510
+ import("../import").then(({BibliographyImporter}) => {
511
+ const importer = new BibliographyImporter(
512
+ text,
513
+ this.app.bibDB,
514
+ newIds => this.updateTable(newIds),
515
+ false
516
+ )
517
+ importer.init()
518
+ })
519
+ return true
520
+ }
521
+
522
+ saveCategories(cats) {
523
+ this.app.bibDB
524
+ .saveCategories(cats)
525
+ .then(bibCats => this.setBibCategoryList(bibCats))
526
+ }
527
+
528
+ deleteBibEntries(ids) {
529
+ this.app.bibDB
530
+ .deleteBibEntries(ids)
531
+ .then(ids => this.removeTableRows(ids))
532
+ }
533
+
534
+ close() {
535
+ if (this.table) {
536
+ this.table.destroy()
537
+ this.table = null
538
+ }
539
+ if (this.dtBulk) {
540
+ this.dtBulk.destroy()
541
+ this.dtBulk = null
542
+ }
543
+ if (this.menu) {
544
+ this.menu.destroy()
545
+ this.menu = null
546
+ }
547
+ }
548
+ }
@@ -0,0 +1,106 @@
1
+ import {BibLatexFileExporter} from "../export"
2
+ import {BibliographyFileImportDialog} from "../import"
3
+
4
+ export const bulkMenuModel = () => ({
5
+ content: [
6
+ {
7
+ title: gettext("Delete selected"),
8
+ tooltip: gettext("Delete selected bibliography entries."),
9
+ action: overview => {
10
+ const ids = overview
11
+ .getSelected()
12
+ .map(id => Number.parseInt(id))
13
+ if (ids.length) {
14
+ overview.deleteBibEntryDialog(ids)
15
+ }
16
+ },
17
+ disabled: overview =>
18
+ !overview.getSelected().length || overview.app.isOffline()
19
+ },
20
+ {
21
+ title: gettext("Export selected"),
22
+ tooltip: gettext("Export selected bibliography entries."),
23
+ action: overview => {
24
+ const ids = overview.getSelected()
25
+ if (ids.length) {
26
+ const exporter = new BibLatexFileExporter(
27
+ overview.app.bibDB,
28
+ ids
29
+ )
30
+ exporter.init()
31
+ }
32
+ },
33
+ disabled: overview =>
34
+ !overview.getSelected().length || overview.app.isOffline()
35
+ }
36
+ ]
37
+ })
38
+
39
+ export const menuModel = () => ({
40
+ content: [
41
+ {
42
+ type: "dropdown",
43
+ id: "cat_selector",
44
+ keys: "Alt-c",
45
+ content: [
46
+ {
47
+ title: gettext("All categories"),
48
+ action: _overview => {
49
+ const trs = document.querySelectorAll(
50
+ "#bibliography > tbody > tr"
51
+ )
52
+ trs.forEach(tr => (tr.style.display = ""))
53
+ }
54
+ }
55
+ ],
56
+ order: 1
57
+ },
58
+ {
59
+ type: "text",
60
+ title: gettext("Edit categories"),
61
+ keys: "Alt-e",
62
+ action: overview => overview.editCategoriesDialog(),
63
+ order: 2
64
+ },
65
+ {
66
+ type: "text",
67
+ title: gettext("Register new source"),
68
+ keys: "Alt-n",
69
+ action: overview => {
70
+ import("../form").then(({BibEntryForm}) => {
71
+ const form = new BibEntryForm(
72
+ overview.app.bibDB,
73
+ overview.app
74
+ )
75
+ form.init().then(idTranslations => {
76
+ const ids = idTranslations.map(idTrans => idTrans[1])
77
+ return overview.updateTable(ids)
78
+ })
79
+ })
80
+ },
81
+ order: 3
82
+ },
83
+ {
84
+ type: "text",
85
+ title: gettext("Import bibliography"),
86
+ keys: "Alt-u",
87
+ action: overview => {
88
+ const fileImporter = new BibliographyFileImportDialog(
89
+ overview.app.bibDB,
90
+ ids => overview.updateTable(ids),
91
+ overview.app
92
+ )
93
+ fileImporter.init()
94
+ },
95
+ order: 4
96
+ },
97
+ {
98
+ type: "search",
99
+ icon: "search",
100
+ title: gettext("Search bibliography"),
101
+ keys: "Alt-s",
102
+ input: (overview, text) => overview.table.search(text),
103
+ order: 5
104
+ }
105
+ ]
106
+ })
@@ -0,0 +1,30 @@
1
+ import {escapeText} from "fwtoolkit"
2
+
3
+ /** A template for each category in the category list edit of the bibliography categories list. */
4
+ const categoryFormsTemplate = ({categories}) =>
5
+ `${categories
6
+ .map(
7
+ cat =>
8
+ `<tr id="categoryTr_${cat.id}" class="fw-list-input">
9
+ <td>
10
+ <input type="text" class="category-form" id="categoryTitle_${cat.id}"
11
+ value="${escapeText(cat.category_title)}" data-id="${cat.id}" />
12
+ <span class="fw-add-input icon-addremove" tabindex="0"></span>
13
+ </td>
14
+ </tr>`
15
+ )
16
+ .join("")}
17
+ <tr class="fw-list-input">
18
+ <td>
19
+ <input type="text" class="category-form" />
20
+ <span class="fw-add-input icon-addremove" tabindex="0"></span>
21
+ </td>
22
+ </tr>`
23
+
24
+ /** A template for the editing of bibliography categories list. */
25
+ export const editCategoriesTemplate = ({categories}) =>
26
+ `<table id="editCategoryList" class="fw-dialog-table">
27
+ <tbody>
28
+ ${categoryFormsTemplate({categories})}
29
+ </tbody>
30
+ </table>`
@@ -0,0 +1 @@
1
+ export const plugins = []