@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,42 @@
1
+ import {InputList} from "fwtoolkit"
2
+ import {RangeFieldForm} from "./range"
3
+
4
+ export class RangeListForm {
5
+ constructor(dom, initialValue = [[]]) {
6
+ this.dom = dom
7
+ this.fieldHandlers = new WeakMap()
8
+ this.inputList = new InputList({
9
+ dom,
10
+ initialValues: initialValue,
11
+ emptyValue: [],
12
+ renderItem: value => ({
13
+ html: `<div class="range-field"></div>`,
14
+ bind: el => {
15
+ const fieldHandler = new RangeFieldForm(
16
+ el.firstElementChild,
17
+ value
18
+ )
19
+ fieldHandler.init()
20
+ this.fieldHandlers.set(el, fieldHandler)
21
+ }
22
+ }),
23
+ getValue: el => this.fieldHandlers.get(el).value
24
+ })
25
+ }
26
+
27
+ init() {
28
+ // InputList is already rendered by the constructor.
29
+ }
30
+
31
+ get value() {
32
+ const formValue = this.inputList.values.filter(value => value !== false)
33
+ if (formValue.length === 0) {
34
+ return false
35
+ }
36
+ return formValue
37
+ }
38
+
39
+ check() {
40
+ return true
41
+ }
42
+ }
@@ -0,0 +1,33 @@
1
+ import TokenField from "tokenfield"
2
+
3
+ export class TagListForm {
4
+ constructor(dom, initialValue = []) {
5
+ this.dom = dom
6
+ this.initialValue = initialValue
7
+ }
8
+
9
+ init() {
10
+ this.dom.innerHTML = '<input class="tags" type="text">'
11
+ this.tokenInput = new TokenField({
12
+ el: this.dom.querySelector(".tags"),
13
+ setItems: this.initialValue.map((key, index) => {
14
+ return {id: index, name: key}
15
+ }),
16
+ keys: {
17
+ 188: "delimiter"
18
+ }
19
+ })
20
+ }
21
+
22
+ get value() {
23
+ const formValue = this.tokenInput.getItems().map(item => {
24
+ return item.name
25
+ })
26
+ // If the form has not been filled out, don't consider this form
27
+ return formValue.length > 0 ? formValue : false
28
+ }
29
+
30
+ check() {
31
+ return true
32
+ }
33
+ }
@@ -0,0 +1,51 @@
1
+ import {toggleMark} from "prosemirror-commands"
2
+
3
+ import {InlineInput, icon} from "fwtoolkit"
4
+
5
+ import {titleSchema} from "../../schema/title"
6
+
7
+ export class TitleFieldForm {
8
+ constructor(dom, initialValue) {
9
+ this.inlineInput = new InlineInput(dom, {
10
+ schema: titleSchema,
11
+ nodeType: "literal",
12
+ initialValue,
13
+ tools: [
14
+ {
15
+ command: toggleMark(titleSchema.marks.strong),
16
+ dom: icon("strong", gettext("Strong"))
17
+ },
18
+ {
19
+ command: toggleMark(titleSchema.marks.em),
20
+ dom: icon("em", gettext("Emphasis"))
21
+ },
22
+ {
23
+ command: toggleMark(titleSchema.marks.smallcaps),
24
+ dom: icon("smallcaps", gettext("Small caps"))
25
+ },
26
+ {
27
+ command: toggleMark(titleSchema.marks.sub),
28
+ dom: icon("sub", gettext("Subscript₊"))
29
+ },
30
+ {
31
+ command: toggleMark(titleSchema.marks.sup),
32
+ dom: icon("sup", gettext("Supscript²"))
33
+ },
34
+ {
35
+ command: toggleMark(titleSchema.marks.nocase),
36
+ dom: icon("nocase", gettext("CasE ProTecT"))
37
+ }
38
+ ]
39
+ })
40
+ }
41
+
42
+ init() {}
43
+
44
+ get value() {
45
+ return this.inlineInput.value
46
+ }
47
+
48
+ check() {
49
+ return this.inlineInput.check()
50
+ }
51
+ }
@@ -0,0 +1,43 @@
1
+ // Simple regex to check for a basic URL structure
2
+ const simpleUrlRegex = /^(https?:\/\/)?[\w.-]+\.[a-z]{2,}(\/\S*)?$/i
3
+
4
+ function isValidUrl(string) {
5
+ if (simpleUrlRegex.test(string)) {
6
+ try {
7
+ new URL(string)
8
+ return true
9
+ } catch (_e) {
10
+ return false
11
+ }
12
+ }
13
+ return false
14
+ }
15
+
16
+ export class URIFieldForm {
17
+ constructor(dom, initialValue = "", placeHolder = "") {
18
+ this.dom = dom
19
+ this.initialValue = initialValue
20
+ this.placeHolder = placeHolder
21
+ }
22
+
23
+ init() {
24
+ this.dom.innerHTML = `<input class="uri" type="text" value="${this.initialValue}" placeholder="${this.placeHolder}">`
25
+ }
26
+
27
+ get value() {
28
+ const formValue = this.dom.querySelector("input.uri").value
29
+ // If the form has not been filled out, don't consider this form
30
+ return formValue.length > 0 ? formValue : false
31
+ }
32
+
33
+ check() {
34
+ const formValue = this.value
35
+ if (formValue) {
36
+ if (!isValidUrl(formValue)) {
37
+ this.dom.classList.add("fw-fomt-error")
38
+ return false
39
+ }
40
+ }
41
+ return true
42
+ }
43
+ }
@@ -0,0 +1,21 @@
1
+ export class VerbatimFieldForm {
2
+ constructor(dom, initialValue = "", placeHolder = "") {
3
+ this.dom = dom
4
+ this.initialValue = initialValue
5
+ this.placeHolder = placeHolder
6
+ }
7
+
8
+ init() {
9
+ this.dom.innerHTML = `<input class="verbatim" type="text" value="${this.initialValue}" placeholder="${this.placeHolder}">`
10
+ }
11
+
12
+ get value() {
13
+ const formValue = this.dom.querySelector("input.verbatim").value
14
+ // If the form has not been filled out, don't consider this form
15
+ return formValue.length > 0 ? formValue : false
16
+ }
17
+
18
+ check() {
19
+ return true
20
+ }
21
+ }
@@ -0,0 +1,424 @@
1
+ import {BibFieldTypes, BibTypes} from "bibliojson"
2
+ import {Dialog, DialogTabs, InfoRow, addAlert} from "fwtoolkit"
3
+ import {dateToYear, litToText, nameToText} from "../tools"
4
+ import {CatsForm} from "./cats"
5
+ import {DateFieldForm} from "./fields/date"
6
+ import {KeyFieldForm} from "./fields/key"
7
+ import {KeyListForm} from "./fields/key_list"
8
+ import {LiteralFieldForm} from "./fields/literal"
9
+ import {LiteralListForm} from "./fields/literal_list"
10
+ import {LiteralLongFieldForm} from "./fields/literal_long"
11
+ import {NameListForm} from "./fields/name_list"
12
+ import {RangeListForm} from "./fields/range_list"
13
+ import {TagListForm} from "./fields/tag_list"
14
+ import {TitleFieldForm} from "./fields/title"
15
+ import {URIFieldForm} from "./fields/uri"
16
+ import {VerbatimFieldForm} from "./fields/verbatim"
17
+ import {
18
+ BibFieldHelp,
19
+ BibFieldTitles,
20
+ BibTypeTitles,
21
+ getBibFieldTitle
22
+ } from "./strings"
23
+ import {bibDialog} from "./templates"
24
+
25
+ const FIELD_FORMS = {
26
+ f_date: DateFieldForm,
27
+ f_integer: LiteralFieldForm,
28
+ f_literal: LiteralFieldForm,
29
+ l_literal: LiteralListForm,
30
+ f_long_literal: LiteralLongFieldForm,
31
+ f_key: KeyFieldForm,
32
+ l_key: KeyListForm,
33
+ l_name: NameListForm,
34
+ l_range: RangeListForm,
35
+ l_tag: TagListForm,
36
+ f_title: TitleFieldForm,
37
+ f_uri: URIFieldForm,
38
+ f_verbatim: VerbatimFieldForm
39
+ }
40
+
41
+ export class BibEntryForm {
42
+ constructor(bibDB, app = false, itemId = false) {
43
+ this.bibDB = bibDB
44
+ this.itemId = itemId
45
+ this.app = app
46
+ this.fields = {}
47
+ this.currentValues = {}
48
+ }
49
+
50
+ init() {
51
+ if (this.app && this.app.isOffline()) {
52
+ // Diable the editing of main user bibliography , since Document bibliography is stored in Editor/Document.
53
+ addAlert(
54
+ "info",
55
+ gettext(
56
+ "You are currently offline. Please try again when you are back online."
57
+ )
58
+ )
59
+ return Promise.resolve()
60
+ }
61
+ if (this.itemId !== false) {
62
+ this.dialogHeader = gettext("Edit Source")
63
+ const bibEntry = this.bibDB.db[this.itemId]
64
+ this.currentValues = JSON.parse(JSON.stringify(bibEntry)) // copy current values
65
+ } else {
66
+ this.dialogHeader = gettext("Register New Source")
67
+ this.currentValues = {
68
+ bib_type: false,
69
+ cats: [],
70
+ entry_key: "",
71
+ fields: {}
72
+ }
73
+ }
74
+ return this.createForm()
75
+ }
76
+
77
+ addDialogToDOM() {
78
+ // Add form to DOM
79
+ const buttons = [
80
+ {
81
+ type: "close"
82
+ }
83
+ ]
84
+
85
+ return new Promise(resolve => {
86
+ buttons.push({
87
+ classes: "fw-dark",
88
+ text: gettext("Submit"),
89
+ click: () => {
90
+ if (this.check()) {
91
+ const returnValue = this.save()
92
+ this.dialog.close()
93
+ resolve(returnValue)
94
+ }
95
+ }
96
+ })
97
+
98
+ this.entryKeyAutoGenerate = !this.currentValues.entry_key
99
+ this.programmaticallySettingKey = false
100
+ this.lastAutoGeneratedKey = null
101
+
102
+ const hasCats = this.bibDB.cats.length > 0
103
+ this.dialog = new Dialog({
104
+ title: this.dialogHeader,
105
+ id: "bib-dialog",
106
+ width: 940,
107
+ body: bibDialog({
108
+ bib_type: this.currentValues.bib_type,
109
+ BibTypes,
110
+ hasCats
111
+ }),
112
+ buttons
113
+ })
114
+
115
+ this.dialog.open()
116
+
117
+ this.dialogTabs = new DialogTabs(
118
+ [
119
+ {
120
+ id: "req-fields-tab",
121
+ title: gettext("Required Fields"),
122
+ template: () => ""
123
+ },
124
+ {
125
+ id: "opt-fields-tab",
126
+ title: gettext("Optional Fields"),
127
+ template: () => ""
128
+ },
129
+ ...(hasCats
130
+ ? [
131
+ {
132
+ id: "categories-tab",
133
+ title: gettext("Categories"),
134
+ template: () => ""
135
+ }
136
+ ]
137
+ : [])
138
+ ],
139
+ {containerId: "bib-dialog-tabs"}
140
+ )
141
+ this.dialogTabs.bind(
142
+ this.dialog.dialogEl.querySelector("#bib-dialog-tabs")
143
+ )
144
+
145
+ const entryKeyInput = document.getElementById("entry-key")
146
+ if (entryKeyInput) {
147
+ entryKeyInput.value = this.currentValues.entry_key || ""
148
+ entryKeyInput.addEventListener("input", () => {
149
+ if (this.programmaticallySettingKey) {
150
+ return
151
+ }
152
+ this.entryKeyAutoGenerate = !entryKeyInput.value.trim()
153
+ if (this.entryKeyAutoGenerate) {
154
+ this.lastAutoGeneratedKey = null
155
+ }
156
+ this.checkEntryKey()
157
+ })
158
+ this.checkEntryKey()
159
+ this.dialog.dialogEl.addEventListener("focusout", () => {
160
+ if (this.entryKeyAutoGenerate) {
161
+ this.maybeAutoGenerateEntryKey()
162
+ }
163
+ })
164
+ }
165
+
166
+ document
167
+ .getElementById("select-bibtype")
168
+ .addEventListener("change", () => resolve(this.changeBibType()))
169
+ })
170
+ }
171
+
172
+ addField(fieldName, dom) {
173
+ const fieldType = BibFieldTypes[fieldName]
174
+ const fieldTitleText = getBibFieldTitle(
175
+ fieldName,
176
+ this.currentValues.bib_type
177
+ )
178
+
179
+ const row = new InfoRow({
180
+ label: fieldTitleText,
181
+ helpText: BibFieldHelp[fieldName],
182
+ fieldClass: fieldName
183
+ })
184
+ dom.insertAdjacentHTML("beforeend", row.html())
185
+ const fieldDOM = dom.lastChild.lastChild
186
+ const FieldClass = FIELD_FORMS[fieldType.type]
187
+ if (FieldClass) {
188
+ const fieldHandler = new FieldClass(
189
+ fieldDOM,
190
+ this.currentValues.fields[fieldName],
191
+ undefined,
192
+ fieldType
193
+ )
194
+ fieldHandler.init()
195
+ this.fields[fieldName] = fieldHandler
196
+ }
197
+ }
198
+
199
+ createForm() {
200
+ const dialogPromise = this.addDialogToDOM()
201
+ if (this.currentValues.bib_type !== false) {
202
+ const eitherOrFields = document.getElementById("eo-fields")
203
+ BibTypes[this.currentValues.bib_type].eitheror.forEach(
204
+ fieldName => {
205
+ this.addField(fieldName, eitherOrFields)
206
+ }
207
+ )
208
+ const reqFields = document.getElementById("req-fields")
209
+ BibTypes[this.currentValues.bib_type].required.forEach(
210
+ fieldName => {
211
+ this.addField(fieldName, reqFields)
212
+ }
213
+ )
214
+ const optFields = document.getElementById("opt-fields")
215
+ BibTypes[this.currentValues.bib_type].optional.forEach(
216
+ fieldName => {
217
+ this.addField(fieldName, optFields)
218
+ }
219
+ )
220
+ const catsField = document.getElementById("categories-field")
221
+ if (catsField) {
222
+ this.catsForm = new CatsForm(
223
+ catsField,
224
+ this.currentValues.cats,
225
+ this.bibDB.cats
226
+ )
227
+ this.catsForm.init()
228
+ }
229
+ }
230
+ return dialogPromise
231
+ }
232
+
233
+ changeBibType() {
234
+ // Add all current values into temporary currentValues, in case the
235
+ // user still wants them.
236
+ const formValue = this.value
237
+ Object.assign(this.currentValues.fields, formValue.fields)
238
+ this.currentValues.cats = formValue.cats
239
+ this.currentValues.bib_type = formValue.bib_type
240
+ // Reset fields and close dialog.
241
+ this.fields = {}
242
+ this.dialog.close()
243
+ return this.createForm()
244
+ }
245
+
246
+ createEntryKey(bibItem) {
247
+ // We attempt to create a biblatex compatible entry key if there is no entry
248
+ // key so far.
249
+ let entryKey = ""
250
+ if (bibItem.fields.author) {
251
+ entryKey += nameToText(bibItem.fields.author).replace(
252
+ /\s|,|=|;|:|{|}/g,
253
+ ""
254
+ )
255
+ } else if (bibItem.fields.editor) {
256
+ entryKey += nameToText(bibItem.fields.editor).replace(
257
+ /\s|,|=|;|:|{|}/g,
258
+ ""
259
+ )
260
+ }
261
+ if (bibItem.fields.date) {
262
+ entryKey += bibItem.fields.date
263
+ .split("/")[0]
264
+ .replace(/\?|\*|u|~|-/g, "")
265
+ }
266
+ if (entryKey.length) {
267
+ bibItem.entry_key = entryKey
268
+ }
269
+ }
270
+
271
+ maybeAutoGenerateEntryKey() {
272
+ if (!this.entryKeyAutoGenerate) {
273
+ return
274
+ }
275
+ const input = document.getElementById("entry-key")
276
+ if (!input) {
277
+ return
278
+ }
279
+ const currentValue = input.value.trim()
280
+ if (currentValue && currentValue !== this.lastAutoGeneratedKey) {
281
+ return
282
+ }
283
+
284
+ let lastName = ""
285
+ const nameField = this.fields.author || this.fields.editor
286
+ if (nameField && nameField.value && nameField.value.length) {
287
+ const firstAuthor = nameField.value[0]
288
+ if (firstAuthor.family && firstAuthor.family.length) {
289
+ lastName = litToText(firstAuthor.family)
290
+ } else if (firstAuthor.literal && firstAuthor.literal.length) {
291
+ lastName = litToText(firstAuthor.literal)
292
+ }
293
+ }
294
+ lastName = lastName.replace(/[^a-zA-Z0-9_-]/g, "")
295
+
296
+ let year = ""
297
+ if (this.fields.date && this.fields.date.value) {
298
+ const yearStr = dateToYear(this.fields.date.value)
299
+ const yearMatch = yearStr.match(/-?(\d+)/)
300
+ year = yearMatch ? yearMatch[1] : ""
301
+ }
302
+ year = year.replace(/[^a-zA-Z0-9_-]/g, "")
303
+
304
+ const baseKey = `${lastName}${year}`
305
+ if (!baseKey) {
306
+ return
307
+ }
308
+
309
+ const existingKeys = new Set(
310
+ Object.entries(this.bibDB.db)
311
+ .filter(
312
+ ([id]) =>
313
+ Number.parseInt(id) !==
314
+ Number.parseInt(this.itemId || 0)
315
+ )
316
+ .map(([, entry]) => entry.entry_key)
317
+ )
318
+
319
+ let key = baseKey
320
+ let suffix = ""
321
+ while (existingKeys.has(key + suffix)) {
322
+ suffix =
323
+ suffix === ""
324
+ ? "a"
325
+ : String.fromCharCode(suffix.charCodeAt(0) + 1)
326
+ }
327
+ key = key + suffix
328
+
329
+ if (key === currentValue) {
330
+ return
331
+ }
332
+
333
+ this.programmaticallySettingKey = true
334
+ input.value = key
335
+ this.programmaticallySettingKey = false
336
+ this.lastAutoGeneratedKey = key
337
+ this.checkEntryKey()
338
+ }
339
+
340
+ checkEntryKey() {
341
+ const input = document.getElementById("entry-key")
342
+ const warningEl = document.getElementById("entry-key-warning")
343
+ if (!input || !warningEl) {
344
+ return
345
+ }
346
+ const value = input.value.trim()
347
+ if (!value) {
348
+ warningEl.textContent = ""
349
+ return
350
+ }
351
+ const slugPattern = /^[a-zA-Z0-9_-]+$/
352
+ if (!slugPattern.test(value)) {
353
+ warningEl.textContent = gettext(
354
+ "Only letters, numbers, hyphens and underscores are allowed."
355
+ )
356
+ return
357
+ }
358
+ // Check for duplicates in the user's library (soft requirement - just warn)
359
+ const duplicate = Object.entries(this.bibDB.db).find(
360
+ ([id, entry]) =>
361
+ entry.entry_key === value &&
362
+ Number.parseInt(id) !== Number.parseInt(this.itemId || 0)
363
+ )
364
+ if (duplicate) {
365
+ warningEl.textContent = gettext(
366
+ "Warning: this key is already used in your library."
367
+ )
368
+ } else {
369
+ warningEl.textContent = ""
370
+ }
371
+ }
372
+
373
+ get value() {
374
+ const entryKeyInput = document.querySelector("#entry-key")
375
+ const entryKey = entryKeyInput
376
+ ? entryKeyInput.value.trim()
377
+ : this.currentValues.entry_key
378
+ const returnObj = {
379
+ bib_type: document.querySelector("#select-bibtype").value,
380
+ cats: this.catsForm ? this.catsForm.value : [],
381
+ entry_key: entryKey,
382
+ fields: {}
383
+ }
384
+ Object.keys(this.fields).forEach(fieldName => {
385
+ const fieldValue = this.fields[fieldName].value
386
+ if (fieldValue !== false) {
387
+ returnObj["fields"][fieldName] = fieldValue
388
+ }
389
+ })
390
+ return returnObj
391
+ }
392
+
393
+ save() {
394
+ const isNew = this.itemId === false ? true : false,
395
+ itemId = this.itemId === false ? 0 : this.itemId,
396
+ item = this.value
397
+
398
+ if (!item.entry_key) {
399
+ this.createEntryKey(item)
400
+ }
401
+ const saveObj = {}
402
+ saveObj[itemId] = item
403
+ return this.bibDB.saveBibEntries(saveObj, isNew)
404
+ }
405
+
406
+ check() {
407
+ let passed = true
408
+ if (!this.currentValues.bib_type) {
409
+ return false
410
+ }
411
+ Object.keys(this.fields).forEach(fieldName => {
412
+ if (this.fields[fieldName].check() !== true) {
413
+ passed = false
414
+ }
415
+ })
416
+ if (!passed) {
417
+ addAlert(
418
+ "error",
419
+ gettext("Error in form, check highlighted values!")
420
+ )
421
+ }
422
+ return passed
423
+ }
424
+ }