@natlibfi/marc-record-validators-melinda 9.0.3 → 9.0.5

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/dist/isbn-issn.js CHANGED
@@ -17,7 +17,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
17
17
  *
18
18
  * MARC record validators used in Melinda
19
19
  *
20
- * Copyright (c) 2014-2020 University Of Helsinki (The National Library Of Finland)
20
+ * Copyright (c) 2014-2022 University Of Helsinki (The National Library Of Finland)
21
21
  *
22
22
  * This file is part of marc-record-validators-melinda
23
23
  *
@@ -38,6 +38,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
38
38
  * for the JavaScript code in this file.
39
39
  *
40
40
  */
41
+ // handleInvalid: move invalid 020$a to 020$z, and invalid 022$a to 022$y
41
42
  var _default = ({
42
43
  hyphenateISBN = false,
43
44
  handleInvalid = false
@@ -48,62 +49,118 @@ var _default = ({
48
49
  description: 'Validates ISBN and ISSN values'
49
50
  };
50
51
 
51
- function getInvalidFields(record) {
52
- return record.get(/^(020|022)$/u).filter(field => {
53
- // eslint-disable-line prefer-named-capture-group
54
- // Check ISBN:
55
- if (field.tag === '020') {
56
- if (invalidField020(field)) {
57
- return true;
58
- }
52
+ function stringHasSpace(str) {
53
+ return str.indexOf(' ') > -1;
54
+ }
59
55
 
60
- const subfield = field.subfields.find(sf => sf.code === 'a');
56
+ function trimSpaces(value) {
57
+ return value.replace(/^\s+/u, '').replace(/\s+$/u, '').replace(/\s+/gu, ' ');
58
+ }
61
59
 
62
- const auditedIsbn = _isbn.default.audit(subfield.value);
60
+ function isMultiWord(inputString) {
61
+ const trimmedString = trimSpaces(inputString);
62
+ return stringHasSpace(trimmedString);
63
+ }
63
64
 
64
- if (!auditedIsbn.validIsbn) {
65
- return true;
66
- } // Should we refactor code by adding a function that returns legal set of values,
67
- // and then we compare subfield.value against that list?
65
+ function getFirstWord(inputString) {
66
+ const trimmedString = trimSpaces(inputString);
67
+ const arr = trimmedString.split(' ');
68
+ return arr[0];
69
+ }
68
70
 
71
+ function invalidISBN(isbn) {
72
+ const isbnOnly = getFirstWord(isbn);
69
73
 
70
- const parsedIsbn = _isbn.default.parse(subfield.value);
74
+ const auditedIsbn = _isbn.default.audit(isbnOnly);
71
75
 
72
- if (hyphenateISBN) {
73
- return !(subfield.value === parsedIsbn.isbn10h || subfield.value === parsedIsbn.isbn13h);
74
- }
76
+ return !auditedIsbn.validIsbn;
77
+ }
75
78
 
76
- return !(subfield.value === parsedIsbn.isbn10 || subfield.value === parsedIsbn.isbn13);
77
- } // Check ISSN:
79
+ function invalidSubfield(subfield) {
80
+ if (subfield.code !== 'a') {
81
+ return false;
82
+ }
78
83
 
84
+ return invalidISBN(subfield.value) || isMultiWord(subfield.value);
85
+ }
79
86
 
80
- if (invalidField022(field)) {
81
- return true;
87
+ function invalidField020(field) {
88
+ if (field.subfields && field.subfields.some(sf => invalidSubfield(sf))) {
89
+ return true;
90
+ }
91
+
92
+ return false;
93
+ }
94
+
95
+ function subfieldsIsbnRequiresHyphenation(subfield) {
96
+ if (!hyphenateISBN || !['a', 'z'].includes(subfield.code)) {
97
+ return false;
98
+ }
99
+
100
+ const isbn = getFirstWord(subfield.value);
101
+
102
+ if (subfield.code === 'a') {
103
+ return requiresHyphenation(isbn);
104
+ } // $z is a bit hacky: hyphenation is required only iff valid and no '-' chars
105
+
106
+
107
+ if (isbn.indexOf('-') > -1) {
108
+ return false;
109
+ }
110
+
111
+ return !invalidISBN(isbn);
112
+
113
+ function requiresHyphenation(isbn) {
114
+ if (!hyphenateISBN) {
115
+ return false;
116
+ } // Handle old notation such as "978-952-396-001-5 (nid.)"
117
+
118
+
119
+ const isbn2 = getFirstWord(isbn);
120
+
121
+ if (invalidISBN(isbn2)) {
122
+ return false;
82
123
  }
83
124
 
84
- const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');
85
- return !(0, _issnVerify.default)(subfield.value);
86
- });
125
+ const parsedIsbn = _isbn.default.parse(isbn2); // Return true only if existing ISBN is a valid and hyphenated 10 or 13 digit ISBN:
87
126
 
88
- function invalidField020(field) {
89
- const subfieldA = field.subfields.find(sf => sf.code === 'a');
90
127
 
91
- if (subfieldA === undefined) {
92
- const subfieldZ = field.subfields.find(sf => sf.code === 'z');
128
+ return !(isbn2 === parsedIsbn.isbn10h || isbn2 === parsedIsbn.isbn13h);
129
+ }
130
+ }
93
131
 
94
- if (subfieldZ !== undefined) {
95
- return false;
132
+ function getRelevantFields(record) {
133
+ //return record.get(/^(?:020|022)$/u).filter(field => {
134
+ return record.fields.filter(field => {
135
+ if (!field.subfields) {
136
+ return false;
137
+ } // Check ISBN:
138
+
139
+
140
+ if (field.tag === '020') {
141
+ if (invalidField020(field)) {
142
+ // checks multiwordness
143
+ return true;
96
144
  }
97
145
 
98
- return true;
99
- } // If value contains space, it's not ok (it's typically something like "1234567890 (nid.)")
146
+ return fieldsIsbnRequiresHyphenation(field);
147
+ } // Check ISSN:
100
148
 
101
149
 
102
- if (subfieldA.value.indexOf(' ') > -1) {
103
- return true;
150
+ if (field.tag === '022') {
151
+ if (invalidField022(field)) {
152
+ return true;
153
+ }
154
+
155
+ const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');
156
+ return !(0, _issnVerify.default)(subfield.value);
104
157
  }
105
158
 
106
159
  return false;
160
+ });
161
+
162
+ function fieldsIsbnRequiresHyphenation(field) {
163
+ return field.subfields && field.subfields.some(sf => subfieldsIsbnRequiresHyphenation(sf));
107
164
  }
108
165
 
109
166
  function invalidField022(field) {
@@ -124,7 +181,7 @@ var _default = ({
124
181
  }
125
182
 
126
183
  function validate(record) {
127
- const fields = getInvalidFields(record);
184
+ const fields = getRelevantFields(record);
128
185
 
129
186
  if (fields.length === 0) {
130
187
  return {
@@ -143,6 +200,15 @@ var _default = ({
143
200
  };
144
201
  }
145
202
 
203
+ const subfieldZ = field.subfields.find(sf => sf.code === 'z');
204
+
205
+ if (subfieldZ) {
206
+ return {
207
+ name: 'ISBN (subfield Z)',
208
+ value: subfieldZ.value
209
+ };
210
+ }
211
+
146
212
  return {
147
213
  name: 'ISBN',
148
214
  value: undefined
@@ -179,68 +245,118 @@ var _default = ({
179
245
  }
180
246
 
181
247
  function fix(record) {
182
- getInvalidFields(record).forEach(field => {
248
+ getRelevantFields(record).forEach(field => {
183
249
  if (field.tag === '020') {
184
- const subfield = field.subfields.find(sf => sf.code === 'a');
185
-
186
- if (subfield) {
187
- // ISBN is valid but is missing hyphens
188
- const normalizedValue = normalizeIsbnValue(subfield.value);
189
-
190
- if (normalizedValue !== undefined) {
191
- // eslint-disable-line functional/no-conditional-statement
192
- subfield.value = normalizedValue; // eslint-disable-line functional/immutable-data
193
- } else if (handleInvalid) {
194
- // eslint-disable-line functional/no-conditional-statement
195
- field.subfields.push({
196
- code: 'z',
197
- value: subfield.value
198
- }); // eslint-disable-line functional/immutable-data
199
-
200
- record.removeSubfield(subfield, field);
201
- }
202
- }
203
- } else {
204
- const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');
250
+ field.subfields.forEach(subfield => fixField020Subfield(field, subfield));
251
+ return;
252
+ } // 022 ISSN:
205
253
 
206
- if (subfield && handleInvalid) {
207
- // eslint-disable-line functional/no-conditional-statement
208
- field.subfields.push({
209
- code: 'y',
210
- value: trimSpaces(subfield.value)
211
- }); // eslint-disable-line functional/immutable-data
212
254
 
213
- record.removeSubfield(subfield, field);
214
- }
255
+ const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');
256
+
257
+ if (subfield && handleInvalid) {
258
+ // eslint-disable-line functional/no-conditional-statement
259
+ // $a/$l => $y (bit overkill to add $z and remove $a/$l instead of just renaming)
260
+ field.subfields.push({
261
+ code: 'y',
262
+ value: subfield.value
263
+ }); // eslint-disable-line functional/immutable-data
264
+
265
+ record.removeSubfield(subfield, field);
215
266
  }
216
267
  });
217
268
 
218
- function normalizeIsbnValue(value) {
219
- const trimmedValue = trimISBN(value); // NB! This might lose information that should be stored in $q...
269
+ function fixField020Subfield(field, subfield) {
270
+ split020A(); // subfield and field are in the scope
220
271
 
221
- const auditResult = _isbn.default.audit(trimmedValue);
272
+ addHyphens(subfield);
273
+ handleInvalidIsbn(field, subfield); // remove 020$a, add 020$z, Do this last, as it uses deletion
222
274
 
223
- if (auditResult.validIsbn) {
224
- const parsedIsbn = _isbn.default.parse(trimmedValue);
275
+ return;
225
276
 
226
- if (hyphenateISBN) {
277
+ function addHyphens(subfield) {
278
+ if (!subfieldsIsbnRequiresHyphenation(subfield)) {
279
+ return;
280
+ } // ISBN is valid but is missing hyphens
281
+
282
+
283
+ const normalizedValue = normalizeIsbnValue(subfield.value);
284
+
285
+ if (normalizedValue !== undefined) {
227
286
  // eslint-disable-line functional/no-conditional-statement
228
- return trimmedValue.length === 10 ? parsedIsbn.isbn10h : parsedIsbn.isbn13h; // eslint-disable-line functional/immutable-data
229
- } // Just trim
287
+ subfield.value = normalizedValue; // eslint-disable-line functional/immutable-data
288
+ }
289
+ }
290
+
291
+ function handleInvalidIsbn(field, subfield) {
292
+ if (subfield.code !== 'a' || !handleInvalid) {
293
+ return;
294
+ }
295
+
296
+ const head = getFirstWord(subfield.value);
230
297
 
298
+ if (!invalidISBN(head)) {
299
+ return;
300
+ } // $a => $z (bit overkill to add $z and remove $a instead of just renaming, but too lazy to fix/test thorougly)
231
301
 
232
- return trimmedValue.length === 10 ? parsedIsbn.isbn10 : parsedIsbn.isbn13; // eslint-disable-line functional/immutable-data
302
+
303
+ field.subfields.push({
304
+ code: 'z',
305
+ value: subfield.value
306
+ }); // eslint-disable-line functional/immutable-data
307
+
308
+ record.removeSubfield(subfield, field);
233
309
  }
234
310
 
235
- return undefined;
236
- }
311
+ function split020A() {
312
+ // Move non-initial words from $a to $q:
313
+ if (subfield.code !== 'a') {
314
+ return;
315
+ }
237
316
 
238
- function trimSpaces(value) {
239
- return value.replace(/\s/gu, '');
240
- }
317
+ const value = trimSpaces(subfield.value);
318
+ const position = value.indexOf(' ');
319
+
320
+ if (position === -1) {
321
+ return;
322
+ }
323
+
324
+ const head = getFirstWord(value);
325
+
326
+ if (invalidISBN(head)) {
327
+ // Don't split, if first word ain't ISBN
328
+ return;
329
+ }
330
+
331
+ const tail = value.substring(position + 1);
332
+ subfield.value = head; // eslint-disable-line functional/immutable-data
333
+
334
+ field.subfields.push({
335
+ code: 'q',
336
+ value: tail
337
+ }); // eslint-disable-line functional/immutable-data
338
+ }
241
339
 
242
- function trimISBN(value) {
243
- return trimSpaces(value.replace(/\s\D+$/gu, '')); // handle "1234567890 (nid.)" => "1234567890" as well as spaces
340
+ function normalizeIsbnValue(value) {
341
+ const trimmedValue = getFirstWord(value); //const trimmedValue = trimISBN(value); // NB! This might lose information that should be stored in $q...
342
+
343
+ const auditResult = _isbn.default.audit(trimmedValue);
344
+
345
+ if (!auditResult.validIsbn) {
346
+ return undefined;
347
+ }
348
+
349
+ const numbersOnly = trimmedValue.replace(/[^0-9Xx]+/ug, '');
350
+
351
+ const parsedIsbn = _isbn.default.parse(trimmedValue);
352
+
353
+ if (hyphenateISBN) {
354
+ // eslint-disable-line functional/no-conditional-statement
355
+ return numbersOnly.length === 10 ? parsedIsbn.isbn10h : parsedIsbn.isbn13h; // eslint-disable-line functional/immutable-data
356
+ }
357
+
358
+ return numbersOnly.length === 10 ? parsedIsbn.isbn10 : parsedIsbn.isbn13; // eslint-disable-line functional/immutable-data
359
+ }
244
360
  }
245
361
  }
246
362
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/isbn-issn.js"],"names":["hyphenateISBN","handleInvalid","validate","fix","description","getInvalidFields","record","get","filter","field","tag","invalidField020","subfield","subfields","find","sf","code","auditedIsbn","ISBN","audit","value","validIsbn","parsedIsbn","parse","isbn10h","isbn13h","isbn10","isbn13","invalidField022","subfieldA","undefined","subfieldZ","indexOf","subfieldAorL","subfieldY","fields","length","valid","map","name","getISSN","reduce","acc","obj","msg","messages","concat","forEach","normalizedValue","normalizeIsbnValue","push","removeSubfield","trimSpaces","trimmedValue","trimISBN","auditResult","replace"],"mappings":"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","sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* MARC record validators used in Melinda\n*\n* Copyright (c) 2014-2020 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of marc-record-validators-melinda\n*\n* marc-record-validators-melinda program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Lesser General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* marc-record-validators-melinda is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Lesser General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport ISBN from 'isbn3';\nimport validateISSN from '@natlibfi/issn-verify';\n\nexport default ({hyphenateISBN = false, handleInvalid = false} = {}) => {\n return {\n validate, fix,\n description: 'Validates ISBN and ISSN values'\n };\n\n function getInvalidFields(record) {\n return record.get(/^(020|022)$/u).filter(field => { // eslint-disable-line prefer-named-capture-group\n // Check ISBN:\n if (field.tag === '020') {\n if (invalidField020(field)) {\n return true;\n }\n\n const subfield = field.subfields.find(sf => sf.code === 'a');\n const auditedIsbn = ISBN.audit(subfield.value);\n if (!auditedIsbn.validIsbn) {\n return true;\n }\n // Should we refactor code by adding a function that returns legal set of values,\n // and then we compare subfield.value against that list?\n const parsedIsbn = ISBN.parse(subfield.value);\n if (hyphenateISBN) {\n return !(subfield.value === parsedIsbn.isbn10h || subfield.value === parsedIsbn.isbn13h);\n }\n\n return !(subfield.value === parsedIsbn.isbn10 || subfield.value === parsedIsbn.isbn13);\n }\n // Check ISSN:\n if (invalidField022(field)) {\n return true;\n }\n\n const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n return !validateISSN(subfield.value);\n });\n\n function invalidField020(field) {\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n\n if (subfieldA === undefined) {\n const subfieldZ = field.subfields.find(sf => sf.code === 'z');\n if (subfieldZ !== undefined) {\n return false;\n }\n return true;\n }\n\n // If value contains space, it's not ok (it's typically something like \"1234567890 (nid.)\")\n if (subfieldA.value.indexOf(' ') > -1) {\n return true;\n }\n return false;\n }\n\n function invalidField022(field) {\n const subfieldAorL = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n if (subfieldAorL === undefined) {\n const subfieldY = field.subfields.find(sf => sf.code === 'y');\n if (subfieldY) {\n return false;\n }\n\n return true;\n }\n return false;\n }\n }\n\n function validate(record) {\n const fields = getInvalidFields(record);\n\n if (fields.length === 0) {\n return {valid: true};\n }\n\n return fields\n .map(field => {\n if (field.tag === '020') {\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n if (subfieldA) {\n return {name: 'ISBN', value: subfieldA.value};\n }\n\n return {name: 'ISBN', value: undefined};\n }\n\n return {name: 'ISSN', value: getISSN()};\n\n function getISSN() {\n const subfieldAorL = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n if (subfieldAorL) {\n return subfieldAorL.value;\n }\n\n return undefined;\n }\n })\n .reduce((acc, obj) => {\n const {name, value} = obj;\n const msg = `${name} (${value}) is not valid`;\n\n return {...acc, messages: acc.messages.concat(msg)};\n }, {valid: false, messages: []});\n }\n\n function fix(record) {\n getInvalidFields(record).forEach(field => {\n if (field.tag === '020') {\n const subfield = field.subfields.find(sf => sf.code === 'a');\n if (subfield) {\n // ISBN is valid but is missing hyphens\n const normalizedValue = normalizeIsbnValue(subfield.value);\n if (normalizedValue !== undefined) { // eslint-disable-line functional/no-conditional-statement\n subfield.value = normalizedValue; // eslint-disable-line functional/immutable-data\n } else if (handleInvalid) { // eslint-disable-line functional/no-conditional-statement\n field.subfields.push({code: 'z', value: subfield.value}); // eslint-disable-line functional/immutable-data\n record.removeSubfield(subfield, field);\n }\n }\n } else {\n const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n if (subfield && handleInvalid) { // eslint-disable-line functional/no-conditional-statement\n field.subfields.push({code: 'y', value: trimSpaces(subfield.value)}); // eslint-disable-line functional/immutable-data\n record.removeSubfield(subfield, field);\n }\n }\n });\n\n function normalizeIsbnValue(value) {\n const trimmedValue = trimISBN(value); // NB! This might lose information that should be stored in $q...\n const auditResult = ISBN.audit(trimmedValue);\n if (auditResult.validIsbn) {\n const parsedIsbn = ISBN.parse(trimmedValue);\n if (hyphenateISBN) { // eslint-disable-line functional/no-conditional-statement\n return trimmedValue.length === 10 ? parsedIsbn.isbn10h : parsedIsbn.isbn13h; // eslint-disable-line functional/immutable-data\n }\n // Just trim\n return trimmedValue.length === 10 ? parsedIsbn.isbn10 : parsedIsbn.isbn13; // eslint-disable-line functional/immutable-data\n }\n return undefined;\n }\n\n function trimSpaces(value) {\n return value.replace(/\\s/gu, '');\n }\n\n function trimISBN(value) {\n return trimSpaces(value.replace(/\\s\\D+$/gu, '')); // handle \"1234567890 (nid.)\" => \"1234567890\" as well as spaces\n }\n }\n};\n"],"file":"isbn-issn.js"}
1
+ {"version":3,"sources":["../src/isbn-issn.js"],"names":["hyphenateISBN","handleInvalid","validate","fix","description","stringHasSpace","str","indexOf","trimSpaces","value","replace","isMultiWord","inputString","trimmedString","getFirstWord","arr","split","invalidISBN","isbn","isbnOnly","auditedIsbn","ISBN","audit","validIsbn","invalidSubfield","subfield","code","invalidField020","field","subfields","some","sf","subfieldsIsbnRequiresHyphenation","includes","requiresHyphenation","isbn2","parsedIsbn","parse","isbn10h","isbn13h","getRelevantFields","record","fields","filter","tag","fieldsIsbnRequiresHyphenation","invalidField022","find","subfieldAorL","undefined","subfieldY","length","valid","map","subfieldA","name","subfieldZ","getISSN","reduce","acc","obj","msg","messages","concat","forEach","fixField020Subfield","push","removeSubfield","split020A","addHyphens","handleInvalidIsbn","normalizedValue","normalizeIsbnValue","head","position","tail","substring","trimmedValue","auditResult","numbersOnly","isbn10","isbn13"],"mappings":"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","sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* MARC record validators used in Melinda\n*\n* Copyright (c) 2014-2022 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of marc-record-validators-melinda\n*\n* marc-record-validators-melinda program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Lesser General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* marc-record-validators-melinda is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Lesser General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport ISBN from 'isbn3';\nimport validateISSN from '@natlibfi/issn-verify';\n\n// handleInvalid: move invalid 020$a to 020$z, and invalid 022$a to 022$y\nexport default ({hyphenateISBN = false, handleInvalid = false} = {}) => {\n return {\n validate, fix,\n description: 'Validates ISBN and ISSN values'\n };\n\n function stringHasSpace(str) {\n return str.indexOf(' ') > -1;\n }\n\n function trimSpaces(value) {\n return value.replace(/^\\s+/u, '').replace(/\\s+$/u, '').replace(/\\s+/gu, ' ');\n }\n\n function isMultiWord(inputString) {\n const trimmedString = trimSpaces(inputString);\n return stringHasSpace(trimmedString);\n }\n\n function getFirstWord(inputString) {\n const trimmedString = trimSpaces(inputString);\n const arr = trimmedString.split(' ');\n return arr[0];\n }\n\n function invalidISBN(isbn) {\n const isbnOnly = getFirstWord(isbn);\n const auditedIsbn = ISBN.audit(isbnOnly);\n return !auditedIsbn.validIsbn;\n }\n\n function invalidSubfield(subfield) {\n if (subfield.code !== 'a') {\n return false;\n }\n return invalidISBN(subfield.value) || isMultiWord(subfield.value);\n }\n\n\n function invalidField020(field) {\n if (field.subfields && field.subfields.some(sf => invalidSubfield(sf))) {\n return true;\n }\n return false;\n }\n\n function subfieldsIsbnRequiresHyphenation(subfield) {\n if (!hyphenateISBN || !['a', 'z'].includes(subfield.code)) {\n return false;\n }\n\n const isbn = getFirstWord(subfield.value);\n if (subfield.code === 'a') {\n return requiresHyphenation(isbn);\n }\n\n // $z is a bit hacky: hyphenation is required only iff valid and no '-' chars\n if (isbn.indexOf('-') > -1) {\n return false;\n }\n return !invalidISBN(isbn);\n\n function requiresHyphenation(isbn) {\n if (!hyphenateISBN) {\n return false;\n }\n // Handle old notation such as \"978-952-396-001-5 (nid.)\"\n const isbn2 = getFirstWord(isbn);\n\n if (invalidISBN(isbn2)) {\n return false;\n }\n\n const parsedIsbn = ISBN.parse(isbn2);\n // Return true only if existing ISBN is a valid and hyphenated 10 or 13 digit ISBN:\n return !(isbn2 === parsedIsbn.isbn10h || isbn2 === parsedIsbn.isbn13h);\n }\n }\n\n function getRelevantFields(record) {\n //return record.get(/^(?:020|022)$/u).filter(field => {\n return record.fields.filter(field => {\n if (!field.subfields) {\n return false;\n }\n // Check ISBN:\n if (field.tag === '020') {\n if (invalidField020(field)) { // checks multiwordness\n return true;\n }\n return fieldsIsbnRequiresHyphenation(field);\n }\n\n // Check ISSN:\n if (field.tag === '022') {\n if (invalidField022(field)) {\n return true;\n }\n\n const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n return !validateISSN(subfield.value);\n }\n return false;\n });\n\n function fieldsIsbnRequiresHyphenation(field) {\n return field.subfields && field.subfields.some(sf => subfieldsIsbnRequiresHyphenation(sf));\n }\n\n function invalidField022(field) {\n const subfieldAorL = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n if (subfieldAorL === undefined) {\n const subfieldY = field.subfields.find(sf => sf.code === 'y');\n if (subfieldY) {\n return false;\n }\n\n return true;\n }\n return false;\n }\n }\n\n function validate(record) {\n const fields = getRelevantFields(record);\n\n if (fields.length === 0) {\n return {valid: true};\n }\n\n return fields\n .map(field => {\n if (field.tag === '020') {\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n if (subfieldA) {\n return {name: 'ISBN', value: subfieldA.value};\n }\n const subfieldZ = field.subfields.find(sf => sf.code === 'z');\n if (subfieldZ) {\n return {name: 'ISBN (subfield Z)', value: subfieldZ.value};\n }\n\n return {name: 'ISBN', value: undefined};\n }\n\n return {name: 'ISSN', value: getISSN()};\n\n function getISSN() {\n const subfieldAorL = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n\n if (subfieldAorL) {\n return subfieldAorL.value;\n }\n\n return undefined;\n }\n })\n .reduce((acc, obj) => {\n const {name, value} = obj;\n const msg = `${name} (${value}) is not valid`;\n\n return {...acc, messages: acc.messages.concat(msg)};\n }, {valid: false, messages: []});\n }\n\n\n function fix(record) {\n getRelevantFields(record).forEach(field => {\n if (field.tag === '020') {\n field.subfields.forEach(subfield => fixField020Subfield(field, subfield));\n return;\n }\n // 022 ISSN:\n const subfield = field.subfields.find(sf => sf.code === 'a' || sf.code === 'l');\n if (subfield && handleInvalid) { // eslint-disable-line functional/no-conditional-statement\n // $a/$l => $y (bit overkill to add $z and remove $a/$l instead of just renaming)\n field.subfields.push({code: 'y', value: subfield.value}); // eslint-disable-line functional/immutable-data\n record.removeSubfield(subfield, field);\n }\n });\n\n\n function fixField020Subfield(field, subfield) {\n split020A(); // subfield and field are in the scope\n addHyphens(subfield);\n handleInvalidIsbn(field, subfield); // remove 020$a, add 020$z, Do this last, as it uses deletion\n return;\n\n function addHyphens(subfield) {\n if (!subfieldsIsbnRequiresHyphenation(subfield)) {\n return;\n }\n // ISBN is valid but is missing hyphens\n const normalizedValue = normalizeIsbnValue(subfield.value);\n if (normalizedValue !== undefined) { // eslint-disable-line functional/no-conditional-statement\n subfield.value = normalizedValue; // eslint-disable-line functional/immutable-data\n }\n }\n\n function handleInvalidIsbn(field, subfield) {\n if (subfield.code !== 'a' || !handleInvalid) {\n return;\n }\n const head = getFirstWord(subfield.value);\n if (!invalidISBN(head)) {\n return;\n }\n // $a => $z (bit overkill to add $z and remove $a instead of just renaming, but too lazy to fix/test thorougly)\n field.subfields.push({code: 'z', value: subfield.value}); // eslint-disable-line functional/immutable-data\n record.removeSubfield(subfield, field);\n }\n\n function split020A() {\n // Move non-initial words from $a to $q:\n if (subfield.code !== 'a') {\n return;\n }\n const value = trimSpaces(subfield.value);\n const position = value.indexOf(' ');\n if (position === -1) {\n return;\n }\n const head = getFirstWord(value);\n if (invalidISBN(head)) { // Don't split, if first word ain't ISBN\n return;\n }\n const tail = value.substring(position + 1);\n subfield.value = head; // eslint-disable-line functional/immutable-data\n field.subfields.push({code: 'q', value: tail}); // eslint-disable-line functional/immutable-data\n }\n\n function normalizeIsbnValue(value) {\n const trimmedValue = getFirstWord(value);\n //const trimmedValue = trimISBN(value); // NB! This might lose information that should be stored in $q...\n const auditResult = ISBN.audit(trimmedValue);\n if (!auditResult.validIsbn) {\n return undefined;\n }\n const numbersOnly = trimmedValue.replace(/[^0-9Xx]+/ug, '');\n const parsedIsbn = ISBN.parse(trimmedValue);\n if (hyphenateISBN) { // eslint-disable-line functional/no-conditional-statement\n return numbersOnly.length === 10 ? parsedIsbn.isbn10h : parsedIsbn.isbn13h; // eslint-disable-line functional/immutable-data\n }\n return numbersOnly.length === 10 ? parsedIsbn.isbn10 : parsedIsbn.isbn13; // eslint-disable-line functional/immutable-data\n }\n }\n }\n};\n"],"file":"isbn-issn.js"}