@natlibfi/marc-record-validators-melinda 13.0.2 → 13.0.3

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 (99) hide show
  1. package/dist/cyrillux-usemarcon-replacement.js +2 -3
  2. package/dist/cyrillux-usemarcon-replacement.js.map +2 -2
  3. package/dist/cyrillux.js +4 -5
  4. package/dist/cyrillux.js.map +2 -2
  5. package/dist/disambiguateSeriesStatements.js +1 -3
  6. package/dist/disambiguateSeriesStatements.js.map +2 -2
  7. package/dist/drop-terms.js +1 -2
  8. package/dist/drop-terms.js.map +2 -2
  9. package/dist/field-008-18-34-character-groups.js +1 -2
  10. package/dist/field-008-18-34-character-groups.js.map +2 -2
  11. package/dist/field-505-separators.js +1 -2
  12. package/dist/field-505-separators.js.map +2 -2
  13. package/dist/field-521-fix.js +1 -2
  14. package/dist/field-521-fix.js.map +2 -2
  15. package/dist/fix-33X.js +1 -2
  16. package/dist/fix-33X.js.map +2 -2
  17. package/dist/fix-language-codes.js +1 -2
  18. package/dist/fix-language-codes.js.map +2 -2
  19. package/dist/fix-sami-041.js +2 -3
  20. package/dist/fix-sami-041.js.map +2 -2
  21. package/dist/fixRelatorTerms.js +1 -2
  22. package/dist/fixRelatorTerms.js.map +2 -2
  23. package/dist/identical-fields.js +2 -2
  24. package/dist/identical-fields.js.map +2 -2
  25. package/dist/mergeRelatorTermFields.js +3 -4
  26. package/dist/mergeRelatorTermFields.js.map +2 -2
  27. package/dist/modernize-502.js +1 -2
  28. package/dist/modernize-502.js.map +2 -2
  29. package/dist/multiple-subfield-0.js.map +2 -2
  30. package/dist/normalize-dashes.js +1 -2
  31. package/dist/normalize-dashes.js.map +2 -2
  32. package/dist/normalize-identifiers.js +1 -2
  33. package/dist/normalize-identifiers.js.map +2 -2
  34. package/dist/normalize-qualifying-information.js +1 -2
  35. package/dist/normalize-qualifying-information.js.map +2 -2
  36. package/dist/normalize-utf8-diacritics.js +1 -2
  37. package/dist/normalize-utf8-diacritics.js.map +2 -2
  38. package/dist/normalizeFieldForComparison.js +1 -2
  39. package/dist/normalizeFieldForComparison.js.map +2 -2
  40. package/dist/punctuation/index.js +3 -8
  41. package/dist/punctuation/index.js.map +2 -2
  42. package/dist/punctuation2.js +1 -2
  43. package/dist/punctuation2.js.map +2 -2
  44. package/dist/remove-041-zxx.js +1 -2
  45. package/dist/remove-041-zxx.js.map +2 -2
  46. package/dist/sanitize-vocabulary-source-codes.js +1 -2
  47. package/dist/sanitize-vocabulary-source-codes.js.map +2 -2
  48. package/dist/sortFields.js +1 -2
  49. package/dist/sortFields.js.map +2 -2
  50. package/dist/sortRelatorTerms.js +1 -2
  51. package/dist/sortRelatorTerms.js.map +2 -2
  52. package/dist/sortSubfields.js +1 -2
  53. package/dist/sortSubfields.js.map +2 -2
  54. package/dist/subfieldValueNormalizations.js +1 -2
  55. package/dist/subfieldValueNormalizations.js.map +2 -2
  56. package/dist/sync-007-and-300.js +1 -2
  57. package/dist/sync-007-and-300.js.map +2 -2
  58. package/dist/sync-language.js +2 -3
  59. package/dist/sync-language.js.map +2 -2
  60. package/dist/translate-terms.js +1 -2
  61. package/dist/translate-terms.js.map +2 -2
  62. package/package.json +2 -2
  63. package/src/cyrillux-usemarcon-replacement.js +3 -3
  64. package/src/cyrillux.js +5 -5
  65. package/src/disambiguateSeriesStatements.js +2 -3
  66. package/src/drop-terms.js +2 -2
  67. package/src/field-008-18-34-character-groups.js +2 -2
  68. package/src/field-505-separators.js +2 -2
  69. package/src/field-521-fix.js +2 -2
  70. package/src/fix-33X.js +2 -2
  71. package/src/fix-language-codes.js +2 -2
  72. package/src/fix-sami-041.js +3 -3
  73. package/src/fixRelatorTerms.js +2 -2
  74. package/src/identical-fields.js +2 -3
  75. package/src/mergeRelatorTermFields.js +4 -4
  76. package/src/modernize-502.js +2 -2
  77. package/src/multiple-subfield-0.js +0 -1
  78. package/src/normalize-dashes.js +2 -2
  79. package/src/normalize-identifiers.js +2 -2
  80. package/src/normalize-qualifying-information.js +1 -2
  81. package/src/normalize-utf8-diacritics.js +2 -2
  82. package/src/normalizeFieldForComparison.js +3 -3
  83. package/src/punctuation/index.js +4 -9
  84. package/src/punctuation2.js +2 -2
  85. package/src/remove-041-zxx.js +2 -2
  86. package/src/sanitize-vocabulary-source-codes.js +2 -2
  87. package/src/sortFields.js +2 -2
  88. package/src/sortRelatorTerms.js +2 -2
  89. package/src/sortSubfields.js +2 -2
  90. package/src/subfieldValueNormalizations.js +2 -2
  91. package/src/sync-007-and-300.js +2 -2
  92. package/src/sync-language.js +2 -3
  93. package/src/translate-terms.js +1 -3
  94. package/dist/sort-tags.js +0 -41
  95. package/dist/sort-tags.js.map +0 -7
  96. package/dist/sort-tags.test.js +0 -261
  97. package/dist/sort-tags.test.js.map +0 -7
  98. package/src/sort-tags.js +0 -48
  99. package/src/sort-tags.test.js +0 -265
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/punctuation2.js"],
4
- "sourcesContent": ["/* eslint-disable max-lines */\n/*\n* punctuation.js -- try and fix a marc field punctuation\n*\n* Author(s): Nicholas Volk <nicholas.volk@helsinki.fi>\n*\n* NOTE #1: https://www.kiwi.fi/display/kumea/Loppupisteohje is implemented via another validator/fixer (ending-punctuation).\n* This file has some support but it's now yet thorough. (And mmight never be.)\n* NOTE #2: Validator/fixer punctuation does similar stuff, but focuses on X00 fields.\n* NOTE #3: As of 2023-06-05 control subfields ($0...$9) are obsolete. Don't use them in rules.\n* (They are jumped over when looking for next (non-controlfield subfield)\n*/\nimport {validateSingleField} from './ending-punctuation.js';\nimport {tagToDataProvenanceSubfieldCode} from './dataProvenanceUtils.js';\nimport {fieldGetUnambiguousTag} from './subfield6Utils.js';\nimport createDebugLogger from 'debug';\nimport {fieldToString, isContentSubfieldCode, nvdebug} from './utils.js';\nimport clone from 'clone';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:punctuation2');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nconst descriptionString = 'Remove invalid and add valid punctuation to data fields';\nexport default function () {\n return {\n description: descriptionString,\n validate, fix\n };\n\n function fix(record) {\n nvdebug(`${descriptionString}: fixer`, debugDev);\n const res = {message: [], fix: [], valid: true};\n record.fields.forEach(f => fieldFixPunctuation(f));\n return res;\n }\n\n function validate(record) {\n nvdebug(`${descriptionString}: validate`, debugDev);\n\n const fieldsNeedingModification = record.fields.filter(f => fieldNeedsModification(f, true));\n\n\n const values = fieldsNeedingModification.map(f => fieldToString(f));\n const newValues = fieldsNeedingModification.map(f => fieldGetFixedString(f, true));\n\n const messages = values.map((val, i) => `'${val}' => '${newValues[i]}'`);\n\n const res = {message: messages};\n\n res.valid = res.message.length < 1;\n return res;\n }\n}\n\n\n\nfunction isIrrelevantSubfield(subfield, tag) {\n const dataProvenanceSubfieldCode = tagToDataProvenanceSubfieldCode(tag);\n if (subfield.code === dataProvenanceSubfieldCode) {\n return true;\n }\n return !isContentSubfieldCode(subfield.code); // Currently this contains other stuff as well ($3, $4, $7, $9...)\n}\n\n\nfunction getNextRelevantSubfield(field, currSubfieldIndex) {\n return field.subfields.find((subfield, index) => index > currSubfieldIndex && !isIrrelevantSubfield(subfield, field.tag));\n}\n\nexport function fieldGetFixedString(field, add = true) {\n const cloneField = clone(field);\n const operation = add ? subfieldFixPunctuation : subfieldStripPunctuation;\n cloneField.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n operation(cloneField, sf, getNextRelevantSubfield(cloneField, i));\n });\n return fieldToString(cloneField);\n}\n\nexport function fieldNeedsModification(field, add = true) {\n if (!field.subfields) {\n return false;\n }\n\n const originalFieldAsString = fieldToString(field);\n const modifiedFieldAsString = fieldGetFixedString(field, add);\n\n return modifiedFieldAsString !== originalFieldAsString;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////\n// <= Above code is written for the validator logic <= //\n// => Everything below was originally transferred from reducers' punctuation.js => //\n/////////////////////////////////////////////////////////////////////////////////////\n\n\n//const stripCrap = / *[-;:,+]+$/u;\nconst needsPuncAfterAlphanumeric = /(?:[a-z0-9A-Z]|\u00E5|\u00E4|\u00F6|\u00C5|\u00C4|\u00D6)$/u;\nconst defaultNeedsPuncAfter2 = /(?:[\\]a-zA-Z0-9)]|\u00E4|\u00E5|\u00F6|\u00C5|\u00C4|\u00D6)$/u;\nconst doesNotEndInPunc = /[^!?.:;,]$/u; // non-punc for pre-240/700/XXX $, note that '.' comes if preceded by ')'\nconst blocksPuncRHS = /^(?:\\()/u;\nconst allowsPuncRHS = /^(?:[A-Za-z0-9]|\u00E5|\u00E4|\u00F6|\u00C5|\u00C4|\u00D6)/u;\nconst aToZ = 'abcdefghijklmnopqrstuvwxyz';\n\n\nconst dotIsProbablyPunc = /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6|(?:[A-Za-z0-9]|\u00C5|\u00C4|\u00D6)(?:[A-Z]|\u00C5|\u00C4|\u00D6))\\.$/u;\nconst puncIsProbablyPunc = /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6) ?[.,:;]$/u;\n// NB! 65X: Finnish terms don't use punctuation, but international ones do. Neither one is currently (2021-11-08) coded here.\n\n// Will unfortunately trigger \"Sukunimi, Th.\" type:\nconst removeColons = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / *[;:]$/u};\nconst removeX00Comma = {'code': 'abcdejnqt', 'followedBy': 'abcdenqtv#', 'context': /.,$/u, 'remove': /,$/u};\nconst cleanRHS = {'code': 'abcd', 'followedBy': 'bcde', 'context': /(?:(?:[a-z0-9]|\u00E5|\u00E4|\u00F6)\\.|,)$/u, 'contextRHS': blocksPuncRHS, 'remove': /[.,]$/u};\nconst cleanX00dCommaOrDot = {'code': 'd', 'followedBy': 'et#', 'context': /[0-9]-[,.]$/u, 'remove': /[,.]$/u};\nconst cleanX00aDot = {'code': 'abcde', 'followedBy': 'cdegj', 'context': dotIsProbablyPunc, 'remove': /\\.$/u};\nconst cleanCorruption = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / \\.$/u};\n// These $e dot removals are tricky: before removing the comma, we should know that it ain't an abbreviation such as \"esitt.\"...\nconst cleanX00eDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|j\u00E4)[.,]$/u, 'remove': /\\.$/u};\nconst cleanX11jDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|j\u00E4)[.,]$/u, 'remove': /\\.$/u};\nconst removeCommaBeforeLanguageSubfieldL = {'followedBy': 'l', 'remove': /,$/u};\nconst removeCommaBeforeTitleSubfieldT = {'followedBy': 't', 'remove': /,$/u};\n\nconst X00RemoveDotAfterBracket = {'code': 'cq', 'context': /\\)\\.$/u, 'remove': /\\.$/u};\n// 390, 800, 810, 830...\nconst cleanPuncBeforeLanguage = {'code': 'atvxyz', 'followedBy': 'l', 'context': puncIsProbablyPunc, 'remove': / *[.,:;]$/u};\n\nconst addX00aComma = {'add': ',', 'code': 'abcqejt', 'followedBy': 'cdegnr', 'context': doesNotEndInPunc, 'contextRHS': allowsPuncRHS};\nconst addX00dComma = {'name': 'X00$d ending in \"-\" does not get comma', 'add': ',', 'code': 'd', 'followedBy': 'cdeg', 'context': /[^-,.!]$/u, 'contextRHS': allowsPuncRHS};\nconst addX00aComma2 = {'add': ',', 'code': 'abcdej', 'followedBy': 'cdeg', 'context': /(?:[A-Z]|\u00C5|\u00C4|\u00D6)\\.$/u, 'contextRHS': allowsPuncRHS};\nconst addX00Dot = {'add': '.', 'code': 'abcdetv', 'followedBy': 'fklptu', 'context': needsPuncAfterAlphanumeric};\nconst addEntryFieldFinalDot = {'name': 'X00 final dot', 'add': '.', 'code': 'abcdefghijklmnopqrstuvwxyz', 'followedBy': '#', 'context': /[^.)!?-]$/u};\n\n\nconst addXX0iColon = {name: 'Punctuate relationship information', add: ':', code: 'i', context: defaultNeedsPuncAfter2}; // Not explicitly checking it, but this should always be followed by 'a' or 't'\nconst addX10bDot = {'name': 'Add X10 pre-$b dot', 'add': '.', 'code': 'ab', 'followedBy': 'b', 'context': defaultNeedsPuncAfter2};\nconst addX10Comma = {'add': ',', 'code': 'abet', 'followedBy': 'en', 'context': defaultNeedsPuncAfter2};\nconst addX10Dot = {'name': 'Add X10 final dot', 'add': '.', 'code': 'abet', 'followedBy': 'tu#', 'context': needsPuncAfterAlphanumeric};\nconst addColonToRelationshipInformation = {'name': 'Add \\':\\' to 7X0 $i relationship info', 'add': ':', 'code': 'i', 'context': defaultNeedsPuncAfter2};\n\nconst addX11Spacecolon = {name: '611 space colon(y :-)', add: ' :', code: 'nd', followedBy: 'dc', 'context': defaultNeedsPuncAfter2};\n\nconst addDotBeforeLanguageSubfieldL = {'name': 'Add dot before $l', 'add': '.', 'code': 'abepst', 'followedBy': 'l', 'context': doesNotEndInPunc};\n\n// 490:\nconst addSemicolonBeforeVolumeDesignation = {'name': 'Add \" ;\" before $v', 'add': ' ;', 'code': 'atxyz', 'followedBy': 'v', 'context': /[^;]$/u};\n\nconst NONE = 0;\nconst ADD = 2;\nconst REMOVE = 1;\nconst REMOVE_AND_ADD = 3;\n\n// Crappy punctuation consists of various crap that is somewhat common.\n// We strip crap for merge decisions. We are not trying to actively remove crap here.\n\nconst removeCrapFromAllEntryFields = [removeCommaBeforeLanguageSubfieldL, removeCommaBeforeTitleSubfieldT];\n\nconst removeX00Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, cleanX00dCommaOrDot, cleanRHS, X00RemoveDotAfterBracket, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];\nconst removeX10Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];\nconst removeX11Whatever = [removeX00Comma, cleanX11jDot, ...removeCrapFromAllEntryFields];\nconst removeX30Whatever = removeCrapFromAllEntryFields;\n\nconst remove490And830Whatever = [{'code': 'axyzv', 'followedBy': 'axyzv', 'remove': /(?: *;| *=|,)$/u}];\n\nconst linkingEntryRemoveWhatever = [\n {'code': 'i', 'followedBy': 'at', 'remove': / ?:$/u}, // ':'\n {'code': 'at', 'remove': /\\.$/u},\n // Only \". -\" separator is still used in music. We can strip it, but can only create the non-music punctuation!\n {'code': 'abdghiklmnopqrstuwxyz', 'followedBy': 'abdghiklmnopqrstuwxyz#', 'remove': /\\. -$/u}\n];\n\n\n// '!' means negation, thus '!b' means any other subfield but 'b'.\n// 'followedBy': '#' means that current subfield is the last subfield.\n// NB! Note that control subfields are ignored in punctuation rules.\n// NB #2! Control field ignorance causes issues with field 257: https://wiki.helsinki.fi/display/rdasovellusohje/Loppupisteohje\n// Might need to work on that at some point. NOT a top priority though.\n// NB #3! Final punctuation creation is/should be handled by endind-punctuation.js validator!\n\nconst crappy24X = [\n {'code': 'abnp', 'followedBy': '!c', 'remove': / \\/$/u},\n {'code': 'abn', 'followedBy': 'c', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abn', 'followedBy': 'c', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abc', 'followedBy': '#', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abfghinp', 'followedBy': '#', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'n', 'followedBy': 'p', 'remove': /\\.$/u, 'context': dotIsProbablyPunc}, // MELINDA-8817\n {'code': 'p', 'followedBy': 'pc', 'remove': /\\.$/u, 'context': dotIsProbablyPunc}, // MELINDA-8817\n removeCommaBeforeLanguageSubfieldL\n];\n\n\nconst cleanCrappyPunctuationRules = {\n '100': removeX00Whatever,\n '110': removeX10Whatever,\n '111': removeX11Whatever,\n '130': removeX30Whatever,\n '240': crappy24X,\n '245': crappy24X,\n '246': crappy24X,\n '300': [\n {'code': 'a', 'followedBy': '!b', 'remove': / *:$/u},\n {'code': 'a', 'followedBy': 'b', 'remove': /:$/u, 'context': /[^ ]:$/u},\n {'code': 'ab', 'followedBy': '!c', 'remove': / *;$/u},\n {'code': 'ab', 'followedBy': 'c', 'remove': /;$/u, 'context': /[^ ];$/u},\n {'code': 'abc', 'followedBy': '!e', 'remove': / *\\+$/u} // Removes both valid (with one space) and invalid (spaceless et al) puncs\n\n ],\n\n '490': remove490And830Whatever,\n '600': removeX00Whatever,\n '610': removeX10Whatever,\n '611': removeX11Whatever,\n '630': removeX30Whatever,\n '700': removeX00Whatever,\n '710': removeX10Whatever,\n '711': removeX11Whatever,\n '730': removeX30Whatever,\n '773': linkingEntryRemoveWhatever,\n '774': linkingEntryRemoveWhatever,\n '776': linkingEntryRemoveWhatever,\n '787': linkingEntryRemoveWhatever,\n '800': removeX00Whatever,\n '810': removeX10Whatever,\n '830': remove490And830Whatever,\n '946': crappy24X\n};\n\nconst cleanLegalX00Comma = {'code': 'abcdetn', 'followedBy': 'cdegjnr', 'context': /.,$/u, 'remove': /,$/u};\n// Accept upper case letters in X00$b, since they are probably Roman numerals.\nconst cleanLegalX00bDot = {'code': 'b', 'followedBy': 't#', context: /^[IVXLCDM]+\\.$/u, 'remove': /\\.$/u};\nconst cleanLegalX00iColon = {'code': 'i', 'followedBy': 'a', 'remove': / *:$/u}; // NB! context is not needed\nconst cleanLegalX00Dot = {'code': 'abcdetkvl', 'followedBy': 'tklu#', 'context': /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6)\\.$/u, 'remove': /\\.$/u};\nconst cleanDotBeforeLanguageSubfieldL = {'name': 'pre-language-$l dot', 'followedBy': 'l', 'context': /.\\.$/u, 'remove': /\\.$/u};\n\nconst legalEntryField = [cleanDotBeforeLanguageSubfieldL];\n\nconst legalX11SpaceColon = {name: 'legal X11 spacecolony', code: 'nd', followedBy: 'dc', context: / :$/u, remove: / :$/u};\nconst legalX00punc = [cleanLegalX00Comma, cleanLegalX00iColon, cleanLegalX00bDot, cleanLegalX00Dot, ...legalEntryField];\n\nconst cleanLegalX10Comma = {'name': 'X10comma', 'code': 'abe', 'followedBy': 'e', 'context': /.,$/u, 'remove': /,$/u};\nconst cleanLegalX10Dot = {'name': 'X10dot', 'code': 'abt', 'followedBy': 'bst#', 'context': /.\\.$/u, 'remove': /\\.$/u};\n\nconst legalX10punc = [cleanLegalX10Comma, cleanLegalX10Dot, cleanX00eDot, ...legalEntryField];\n\nconst cleanLegalSeriesTitle = [ // 490 and 830\n {'code': 'a', 'followedBy': 'a', 'remove': / =$/u},\n {'code': 'axyz', 'followedBy': 'xyz', 'remove': /,$/u, 'context': /.,$/u},\n {'code': 'axyz', 'followedBy': 'v', 'remove': / *;$/u}\n];\n\nconst clean24X = [\n {'name': 'I:A', 'code': 'i', 'followedBy': 'a', 'remove': / *:$/u},\n {'name': 'A:B', 'code': 'a', 'followedBy': 'b', 'remove': / [:;=]$/u},\n {'name': 'AB:K', 'code': 'ab', 'followedBy': 'k', 'remove': / :$/u},\n {'name': 'ABK:F', 'code': 'abk', 'followedBy': 'f', 'remove': /,$/u},\n {'name': 'ABFNP:C', 'code': 'abfnp', 'followedBy': 'c', 'remove': / \\/$/u},\n {'name': 'ABN:N', 'code': 'abn', 'followedBy': 'n', 'remove': /\\.$/u},\n {'name': 'ABNP:#', 'code': 'abnp', 'followedBy': '#', 'remove': /\\.$/u},\n {'name': 'N:P', 'code': 'n', 'followedBy': 'p', 'remove': /,$/u},\n cleanDotBeforeLanguageSubfieldL\n];\n\nconst legalX11Punc = [...legalEntryField, legalX11SpaceColon];\n\nconst cleanValidPunctuationRules = {\n '100': legalX00punc,\n '110': legalX10punc,\n '111': legalX11Punc,\n '130': legalEntryField,\n '240': clean24X,\n '243': clean24X,\n '245': clean24X,\n '246': clean24X,\n '260': [\n {'code': 'abc', 'followedBy': 'a', 'remove': / ;$/u},\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},\n {'code': 'c', 'followedBy': '#', 'remove': /\\.$/u},\n {'code': 'd', 'followedBy': 'e', 'remove': / :$/u},\n {'code': 'e', 'followedBy': 'f', 'remove': /,$/u},\n {'code': 'f', 'followedBy': '#', 'remove': /\\.$/u} // Probably ')' but should it be removed?\n ],\n '264': [\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},\n {'code': 'c', 'followedBy': '#', 'remove': /\\.$/u}\n ],\n '300': [\n // NB! Remove crap as well, thus the '*' in / *:$/\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'ab', 'followedBy': 'c', 'remove': / ;$/u},\n {'code': 'abc', 'followedBy': 'e', 'remove': / \\+$/u}\n ],\n '490': cleanLegalSeriesTitle,\n '534': [{'code': 'p', 'followedBy': 'c', 'remove': /:$/u}],\n '600': legalX00punc,\n '610': legalX10punc,\n '611': legalX11Punc,\n '630': legalEntryField,\n // Experimental, MET366-ish (end punc in internationally valid, but we don't use it here in Finland):\n '648': [{'code': 'a', 'content': /^[0-9]+\\.$/u, 'ind2': ['4'], 'remove': /\\.$/u}],\n '700': legalX00punc,\n '710': legalX10punc,\n '711': legalX11Punc,\n '730': legalEntryField,\n '800': legalX00punc,\n '810': legalX10punc,\n '811': legalX11Punc,\n '830': [...legalEntryField, ...cleanLegalSeriesTitle],\n '946': clean24X\n};\n\n\n// Overgeneralizes a bit: eg. addColonToRelationshipInformation only applies to 700/710 but as others don't have $i, it's fine.\nconst addToAllEntryFields = [addDotBeforeLanguageSubfieldL, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation, addEntryFieldFinalDot];\n\n\nconst addX00 = [addXX0iColon, addX00aComma, addX00aComma2, addX00Dot, addX00dComma, ...addToAllEntryFields];\nconst addX10 = [addXX0iColon, addX10bDot, addX10Comma, addX10Dot, ...addToAllEntryFields];\nconst addX11 = [...addToAllEntryFields, addX11Spacecolon];\nconst addX30 = [...addToAllEntryFields];\n\nconst add24X = [\n {'code': 'i', 'followedBy': 'a', 'add': ':', 'context': needsPuncAfterAlphanumeric},\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': '[^:]$'},\n {'code': 'abk', 'followedBy': 'f', 'add': ',', 'context': needsPuncAfterAlphanumeric},\n {'code': 'abfnp', 'followedBy': 'c', 'add': ' /', 'context': '[^/]$'},\n addDotBeforeLanguageSubfieldL\n];\n\nconst add245 = [\n ...add24X,\n // Blah! Also \"$a = $b\" and \"$a ; $b\" can be valid... But ' :' is better than nothing, I guess...\n {'code': 'ab', 'followedBy': 'n', 'add': '.', 'context': needsPuncAfterAlphanumeric},\n {'code': 'n', 'followedBy': 'p', 'add': ',', 'context': defaultNeedsPuncAfter2},\n {'code': 'abnpc', 'followedBy': '#', 'add': '.', 'context': needsPuncAfterAlphanumeric} // Stepping on \"punctuation validator's\" toes\n];\n\nconst addSeriesTitle = [ // 490 and 830\n {'code': 'a', 'followedBy': 'a', 'add': ' =', 'context': defaultNeedsPuncAfter2},\n {'code': 'axyz', 'followedBy': 'xy', 'add': ',', 'context': defaultNeedsPuncAfter2},\n addSemicolonBeforeVolumeDesignation // eg. 490$axyz-$v\n];\n\nconst addLinkingEntry = [ // NB! Music 773 uses different punctuation rules, that are not implement here (can they even be?)\n {'code': 'i', 'followedBy': aToZ, 'add': ':', 'context': defaultNeedsPuncAfter2},\n {'code': 'a', 'followedBy': 't', 'add': '.', 'context': defaultNeedsPuncAfter2},\n {'code': 't', 'followedBy': 'dghoz', 'add': '.', 'context': defaultNeedsPuncAfter2}\n];\n\nconst addPairedPunctuationRules = {\n '100': addX00,\n '110': addX10,\n '111': addX11,\n '130': addX30,\n '240': add24X,\n '243': add24X,\n '245': add245,\n '246': add24X,\n '260': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'ab', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},\n {'code': 'abc', 'followedBy': 'a', 'add': ' ;', 'context': defaultNeedsPuncAfter2},\n {'code': 'e', 'followedBy': 'f', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'f', 'followedBy': 'g', 'add': ',', 'context': defaultNeedsPuncAfter2}\n ],\n '264': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'b', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},\n // NB! The $c rule messes dotless exception \"264 #4 $c p1983\" up\n // We'll need to add a hacky postprocessor for this? Add 'hasInd1': '0123' etc?\n {'code': 'c', 'followedBy': '#', 'add': '.', 'context': needsPuncAfterAlphanumeric, 'ind2': ['0', '1', '2', '3']}\n ],\n '300': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'ab', 'followedBy': 'c', 'add': ' ;', 'context': defaultNeedsPuncAfter2},\n {'code': 'abc', 'followedBy': 'e', 'add': ' +', 'context': defaultNeedsPuncAfter2}\n ],\n '490': addSeriesTitle,\n '506': [{'code': 'a', 'followedBy': '#', 'add': '.', 'context': defaultNeedsPuncAfter2}],\n '534': [{'code': 'p', 'followedBy': 'c', 'add': ':', 'context': defaultNeedsPuncAfter2}],\n '600': addX00,\n '610': addX10,\n '611': addX11,\n '630': addX30,\n '700': addX00,\n '710': addX10,\n '711': addX11,\n '730': addX30,\n '773': addLinkingEntry,\n '787': addLinkingEntry,\n '800': addX00,\n '810': addX10,\n '811': addX11,\n '830': [...addX30, ...addSeriesTitle],\n '946': [{'code': 'i', 'followedBy': 'a', 'add': ':', 'context': defaultNeedsPuncAfter2}]\n};\n\n/*\nfunction debugRule(rule) {\n //nvdebug('', debugDev);\n nvdebug(`NAME ${rule.name ? rule.name : '<unnamed>'}`, debugDev);\n nvdebug(`SUBFIELD CODE '${rule.code}' FOLLOWED BY SUBFIELD CODE '${rule.followedBy}'`, debugDev);\n if ('add' in rule) {\n nvdebug(`ADD '${rule.add}'`, debugDev);\n }\n if ('remove' in rule) {\n nvdebug(`REMOVE '${rule.remove}'`, debugDev);\n }\n if ('context' in rule) {\n nvdebug(`CONTEXT '${rule.context.toString()}'`, debugDev);\n }\n //nvdebug('', debugDev);\n}\n*/\n\nfunction ruleAppliesToSubfieldCode(targetSubfieldCodes, currSubfieldCode) {\n if (!targetSubfieldCodes) { // We are not interested in what subfield precedes 240$l, ',' is removed anyway\n return true;\n }\n const negation = targetSubfieldCodes.includes('!');\n if (negation) {\n return !targetSubfieldCodes.includes(currSubfieldCode);\n }\n return targetSubfieldCodes.includes(currSubfieldCode);\n}\n\n\nfunction ruleAppliesToField(rule, field) {\n if ('ind1' in rule && !rule.ind1.includes(field.ind1)) {\n return false;\n }\n\n if ('ind2' in rule && !rule.ind2.includes(field.ind2)) {\n return false;\n }\n\n // If we want to check, say, $2, it should be implemented here!\n\n return true;\n}\n\n\nfunction ruleAppliesToCurrentSubfield(rule, subfield) {\n //nvdebug(` Apply rule on LHS?`, debugDev);\n if (!ruleAppliesToSubfieldCode(rule.code, subfield.code)) {\n //nvdebug(` Reject rule!`, debugDev);\n return false;\n }\n if ('context' in rule) {\n //nvdebug(` Check '${subfield.value}' versus '${rule.context.toString()}'`, debugDev);\n if (!subfield.value.match(rule.context)) { // njsscan-ignore: regex_injection_dos\n //nvdebug(` Reject rule!`, debugDev);\n return false;\n }\n }\n //nvdebug(` Apply rule!`, debugDev);\n return true;\n}\n\nfunction ruleAppliesToNextSubfield(rule, nextSubfield) {\n if (!('followedBy' in rule)) { // Return true, if we are not interested in the next subfield\n return true;\n }\n // The '#' existence check applies only to the RHS field. LHS always exists.\n if (!nextSubfield) {\n const negation = rule.followedBy.includes('!');\n if (negation) {\n return !rule.followedBy.includes('#');\n }\n return rule.followedBy.includes('#');\n }\n\n if (!ruleAppliesToSubfieldCode(rule.followedBy, nextSubfield.code)) {\n return false;\n }\n if ('contextRHS' in rule && !nextSubfield.value.match(rule.contextRHS)) { // njsscan-ignore: regex_injection_dos\n return false;\n }\n return true;\n}\n\nfunction checkRule(rule, field, subfield1, subfield2) {\n if (!ruleAppliesToField(rule, field)) {\n //nvdebug(`FAIL ON WHOLE FIELD: '${fieldToString(field)}`, debugDev);\n return false;\n }\n //const name = rule.name || 'UNNAMED';\n if (!ruleAppliesToCurrentSubfield(rule, subfield1)) {\n //nvdebug(`${name}: FAIL ON LHS SUBFIELD: '$${subfield1.code} ${subfield1.value}', SF=${rule.code}`, debugDev);\n return false;\n }\n\n // NB! This is not a perfect solution. We might have $e$0$e where $e$0 punctuation should actually be based on $e$e rules\n if (!ruleAppliesToNextSubfield(rule, subfield2)) {\n //const msg = subfield2 ? `${name}: FAIL ON RHS SUBFIELD '${subfield2.code}' not in [${rule.followedBy}]` : `${name}: FAIL ON RHS FIELD`;\n //nvdebug(msg, debugDev);\n return false;\n }\n\n \n //nvdebug(`${rule.name ? rule.name : '<unnamed>'}: ACCEPT ${rule.code} (${subfield1.code}), SF2=${rule.followedBy} (${subfield2 ? subfield2.code : '#'})`, debugDev);\n return true;\n}\n\n\nfunction applyPunctuationRules(field, subfield1, subfield2, ruleArray = null, operation = NONE) {\n if (operation === NONE || ruleArray === null) { // !fieldIsApplicable(field, ruleArray)) {\n return;\n }\n const tag2 = field.tag === '880' ? fieldGetUnambiguousTag(field) : field.tag;\n if (!tag2) {\n return;\n }\n if (!(`${tag2}` in ruleArray)) {\n return;\n }\n\n //nvdebug(`PUNCTUATE ${field.tag}/${tag2} '${subfieldToString(subfield1)}' XXX '${subfield2 ? subfieldToString(subfield2) : '#'} }`, debugDev);\n\n //nvdebug(`OP=${operation} ${tag2}: '${subfield1.code}: ${subfield1.value}' ??? '${subfield2 ? subfield2.code : '#'}'`, debugDev);\n const candRules = ruleArray[tag2];\n candRules.every(rule => { // uses \"every\", not \"forEach\", so that only one rule is applies to the given subfields\n //debugRule(rule);\n if (!checkRule(rule, field, subfield1, subfield2)) {\n return true;\n }\n\n //const originalValue = subfield1.value;\n if (rule.remove && [REMOVE, REMOVE_AND_ADD].includes(operation) && subfield1.value.match(rule.remove)) {\n //nvdebug(` PUNC REMOVAL TO BE PERFORMED FOR $${subfield1.code} '${subfield1.value}'`, debugDev);\n subfield1.value = subfield1.value.replace(rule.remove, '');\n //nvdebug(` PUNC REMOVAL PERFORMED FOR '${subfield1.value}'`, debugDev);\n return false;\n }\n if (rule.add && [ADD, REMOVE_AND_ADD].includes(operation)) {\n subfield1.value += rule.add;\n //nvdebug(` ADDED '${rule.add}' TO FORM '${subfield1.value}' USING RULE ${rule.name}`, debugDev);\n return false;\n }\n\n /*\n if (subfield1.value !== originalValue) {\n nvdebug(` PROCESS PUNC: '\u2021${subfield1.code} ${originalValue}' => '\u2021${subfield1.code} ${subfield1.value}'`, debugDev);\n }\n */\n\n return true;\n });\n}\n\nfunction subfieldFixPunctuation(field, subfield1, subfield2) {\n applyPunctuationRules(field, subfield1, subfield2, cleanCrappyPunctuationRules, REMOVE);\n applyPunctuationRules(field, subfield1, subfield2, addPairedPunctuationRules, ADD);\n}\n\nfunction subfieldStripPunctuation(field, subfield1, subfield2) {\n //nvdebug(`FSP1: '${subfield1.value}'`, debugDev);\n applyPunctuationRules(field, subfield1, subfield2, cleanValidPunctuationRules, REMOVE);\n //nvdebug(`FSP2: '${subfield1.value}'`, debugDev);\n applyPunctuationRules(field, subfield1, subfield2, cleanCrappyPunctuationRules, REMOVE);\n //nvdebug(`FSP3: '${subfield1.value}'`, debugDev);\n\n}\n\nexport function fieldStripPunctuation(field) {\n if (!field.subfields) {\n return field;\n }\n\n field.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n subfieldStripPunctuation(field, sf, getNextRelevantSubfield(field, i));\n\n });\n return field;\n}\n\nexport function fieldFixPunctuation(field) {\n if (!field.subfields) {\n return field;\n }\n //nvdebug(`################### fieldFixPunctuation() TEST ${fieldToString(field)}`, debugDev);\n\n field.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n // We'll need some magic for field 257 here, do we? (Also Finnish lexicons vs global lexicons in 65X fields)\n subfieldFixPunctuation(field, sf, getNextRelevantSubfield(field, i));\n });\n\n // Use shared code for final punctuation (sadly this does not fix intermediate punc):\n if (field.useExternalEndPunctuation) {\n // addFinalPunctuation(field); // local version. use shared code instead.\n validateSingleField(field, false, true); // NB! Don't use field.tag as second argument! It's a string, not an int. 3rd arg must be true (=fix)\n }\n return field;\n}\n"],
5
- "mappings": "AAYA,SAAQ,2BAA0B;AAClC,SAAQ,uCAAsC;AAC9C,SAAQ,8BAA6B;AACrC,OAAO,uBAAuB;AAC9B,SAAQ,eAAe,uBAAuB,eAAc;AAC5D,OAAO,WAAW;AAElB,MAAM,QAAQ,kBAAkB,uDAAuD;AAEvF,MAAM,WAAW,MAAM,OAAO,KAAK;AAEnC,MAAM,oBAAoB;AAC1B,0BAA2B;AACzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,YAAQ,GAAG,iBAAiB,WAAW,QAAQ;AAC/C,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAC9C,WAAO,OAAO,QAAQ,OAAK,oBAAoB,CAAC,CAAC;AACjD,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,YAAQ,GAAG,iBAAiB,cAAc,QAAQ;AAElD,UAAM,4BAA4B,OAAO,OAAO,OAAO,OAAK,uBAAuB,GAAG,IAAI,CAAC;AAG3F,UAAM,SAAS,0BAA0B,IAAI,OAAK,cAAc,CAAC,CAAC;AAClE,UAAM,YAAY,0BAA0B,IAAI,OAAK,oBAAoB,GAAG,IAAI,CAAC;AAEjF,UAAM,WAAW,OAAO,IAAI,CAAC,KAAK,MAAM,IAAI,GAAG,SAAS,UAAU,CAAC,CAAC,GAAG;AAEvE,UAAM,MAAM,EAAC,SAAS,SAAQ;AAE9B,QAAI,QAAQ,IAAI,QAAQ,SAAS;AACjC,WAAO;AAAA,EACT;AACF;AAIA,SAAS,qBAAqB,UAAU,KAAK;AAC3C,QAAM,6BAA6B,gCAAgC,GAAG;AACtE,MAAI,SAAS,SAAS,4BAA4B;AAChD,WAAO;AAAA,EACT;AACA,SAAO,CAAC,sBAAsB,SAAS,IAAI;AAC7C;AAGA,SAAS,wBAAwB,OAAO,mBAAmB;AACzD,SAAO,MAAM,UAAU,KAAK,CAAC,UAAU,UAAU,QAAQ,qBAAqB,CAAC,qBAAqB,UAAU,MAAM,GAAG,CAAC;AAC1H;AAEO,gBAAS,oBAAoB,OAAO,MAAM,MAAM;AACrD,QAAM,aAAa,MAAM,KAAK;AAC9B,QAAM,YAAY,MAAM,yBAAyB;AACjD,aAAW,UAAU,QAAQ,CAAC,IAAI,MAAM;AAGtC,cAAU,YAAY,IAAI,wBAAwB,YAAY,CAAC,CAAC;AAAA,EAClE,CAAC;AACD,SAAO,cAAc,UAAU;AACjC;AAEO,gBAAS,uBAAuB,OAAO,MAAM,MAAM;AACxD,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,wBAAwB,cAAc,KAAK;AACjD,QAAM,wBAAwB,oBAAoB,OAAO,GAAG;AAE5D,SAAO,0BAA0B;AACnC;AASA,MAAM,6BAA6B;AACnC,MAAM,yBAAyB;AAC/B,MAAM,mBAAmB;AACzB,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AACtB,MAAM,OAAO;AAGb,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAI3B,MAAM,eAAe,EAAC,QAAQ,8BAA8B,UAAU,WAAU;AAChF,MAAM,iBAAiB,EAAC,QAAQ,aAAa,cAAc,cAAc,WAAW,QAAQ,UAAU,MAAK;AAC3G,MAAM,WAAW,EAAC,QAAQ,QAAQ,cAAc,QAAQ,WAAW,gCAAgC,cAAc,eAAe,UAAU,SAAQ;AAClJ,MAAM,sBAAsB,EAAC,QAAQ,KAAK,cAAc,OAAO,WAAW,gBAAgB,UAAU,SAAQ;AAC5G,MAAM,eAAe,EAAC,QAAQ,SAAS,cAAc,SAAS,WAAW,mBAAmB,UAAU,OAAM;AAC5G,MAAM,kBAAkB,EAAC,QAAQ,8BAA8B,UAAU,QAAO;AAEhF,MAAM,eAAe,EAAC,QAAQ,KAAK,cAAc,QAAQ,WAAW,uBAAuB,UAAU,OAAM;AAC3G,MAAM,eAAe,EAAC,QAAQ,KAAK,cAAc,QAAQ,WAAW,uBAAuB,UAAU,OAAM;AAC3G,MAAM,qCAAqC,EAAC,cAAc,KAAK,UAAU,MAAK;AAC9E,MAAM,kCAAkC,EAAC,cAAc,KAAK,UAAU,MAAK;AAE3E,MAAM,2BAA2B,EAAC,QAAQ,MAAM,WAAW,UAAU,UAAU,OAAM;AAErF,MAAM,0BAA0B,EAAC,QAAQ,UAAU,cAAc,KAAK,WAAW,oBAAoB,UAAU,aAAY;AAE3H,MAAM,eAAe,EAAC,OAAO,KAAK,QAAQ,WAAW,cAAc,UAAU,WAAW,kBAAkB,cAAc,cAAa;AACrI,MAAM,eAAe,EAAC,QAAQ,0CAA0C,OAAO,KAAK,QAAQ,KAAK,cAAc,QAAQ,WAAW,aAAa,cAAc,cAAa;AAC1K,MAAM,gBAAgB,EAAC,OAAO,KAAK,QAAQ,UAAU,cAAc,QAAQ,WAAW,uBAAuB,cAAc,cAAa;AACxI,MAAM,YAAY,EAAC,OAAO,KAAK,QAAQ,WAAW,cAAc,UAAU,WAAW,2BAA0B;AAC/G,MAAM,wBAAwB,EAAC,QAAQ,iBAAiB,OAAO,KAAK,QAAQ,8BAA8B,cAAc,KAAK,WAAW,aAAY;AAGpJ,MAAM,eAAe,EAAC,MAAM,sCAAsC,KAAK,KAAK,MAAM,KAAK,SAAS,uBAAsB;AACtH,MAAM,aAAa,EAAC,QAAQ,sBAAsB,OAAO,KAAK,QAAQ,MAAM,cAAc,KAAK,WAAW,uBAAsB;AAChI,MAAM,cAAc,EAAC,OAAO,KAAK,QAAQ,QAAQ,cAAc,MAAM,WAAW,uBAAsB;AACtG,MAAM,YAAY,EAAC,QAAQ,qBAAqB,OAAO,KAAK,QAAQ,QAAQ,cAAc,OAAO,WAAW,2BAA0B;AACtI,MAAM,oCAAoC,EAAC,QAAQ,uCAAyC,OAAO,KAAK,QAAQ,KAAK,WAAW,uBAAsB;AAEtJ,MAAM,mBAAmB,EAAC,MAAM,yBAAyB,KAAK,MAAM,MAAM,MAAM,YAAY,MAAM,WAAW,uBAAsB;AAEnI,MAAM,gCAAgC,EAAC,QAAQ,qBAAqB,OAAO,KAAK,QAAQ,UAAU,cAAc,KAAK,WAAW,iBAAgB;AAGhJ,MAAM,sCAAsC,EAAC,QAAQ,sBAAsB,OAAO,MAAM,QAAQ,SAAS,cAAc,KAAK,WAAW,SAAQ;AAE/I,MAAM,OAAO;AACb,MAAM,MAAM;AACZ,MAAM,SAAS;AACf,MAAM,iBAAiB;AAKvB,MAAM,+BAA+B,CAAC,oCAAoC,+BAA+B;AAEzG,MAAM,oBAAoB,CAAC,gBAAgB,cAAc,cAAc,iBAAiB,qBAAqB,UAAU,0BAA0B,cAAc,yBAAyB,GAAG,4BAA4B;AACvN,MAAM,oBAAoB,CAAC,gBAAgB,cAAc,cAAc,iBAAiB,cAAc,yBAAyB,GAAG,4BAA4B;AAC9J,MAAM,oBAAoB,CAAC,gBAAgB,cAAc,GAAG,4BAA4B;AACxF,MAAM,oBAAoB;AAE1B,MAAM,0BAA0B,CAAC,EAAC,QAAQ,SAAS,cAAc,SAAS,UAAU,kBAAiB,CAAC;AAEtG,MAAM,6BAA6B;AAAA,EACjC,EAAC,QAAQ,KAAK,cAAc,MAAM,UAAU,QAAO;AAAA;AAAA,EACnD,EAAC,QAAQ,MAAM,UAAU,OAAM;AAAA;AAAA,EAE/B,EAAC,QAAQ,yBAAyB,cAAc,0BAA0B,UAAU,SAAQ;AAC9F;AAUA,MAAM,YAAY;AAAA,EAChB,EAAC,QAAQ,QAAQ,cAAc,MAAM,UAAU,QAAO;AAAA,EACtD,EAAC,QAAQ,OAAO,cAAc,KAAK,UAAU,QAAQ,WAAW,kBAAiB;AAAA,EACjF,EAAC,QAAQ,OAAO,cAAc,KAAK,UAAU,QAAQ,WAAW,kBAAiB;AAAA,EACjF,EAAC,QAAQ,OAAO,cAAc,KAAK,UAAU,QAAQ,WAAW,kBAAiB;AAAA,EACjF,EAAC,QAAQ,YAAY,cAAc,KAAK,UAAU,QAAQ,WAAW,kBAAiB;AAAA,EACtF,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,QAAQ,WAAW,kBAAiB;AAAA;AAAA,EAC/E,EAAC,QAAQ,KAAK,cAAc,MAAM,UAAU,QAAQ,WAAW,kBAAiB;AAAA;AAAA,EAChF;AACF;AAGA,MAAM,8BAA8B;AAAA,EAClC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,IACL,EAAC,QAAQ,KAAK,cAAc,MAAM,UAAU,QAAO;AAAA,IACnD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAO,WAAW,UAAS;AAAA,IACtE,EAAC,QAAQ,MAAM,cAAc,MAAM,UAAU,QAAO;AAAA,IACpD,EAAC,QAAQ,MAAM,cAAc,KAAK,UAAU,OAAO,WAAW,UAAS;AAAA,IACvE,EAAC,QAAQ,OAAO,cAAc,MAAM,UAAU,SAAQ;AAAA;AAAA,EAExD;AAAA,EAEA,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEA,MAAM,qBAAqB,EAAC,QAAQ,WAAW,cAAc,WAAW,WAAW,QAAQ,UAAU,MAAK;AAE1G,MAAM,oBAAoB,EAAC,QAAQ,KAAK,cAAc,MAAM,SAAS,mBAAmB,UAAU,OAAM;AACxG,MAAM,sBAAsB,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,QAAO;AAC9E,MAAM,mBAAmB,EAAC,QAAQ,aAAa,cAAc,SAAS,WAAW,2BAA2B,UAAU,OAAM;AAC5H,MAAM,kCAAkC,EAAC,QAAQ,uBAAuB,cAAc,KAAK,WAAW,SAAS,UAAU,OAAM;AAE/H,MAAM,kBAAkB,CAAC,+BAA+B;AAExD,MAAM,qBAAqB,EAAC,MAAM,yBAAyB,MAAM,MAAM,YAAY,MAAM,SAAS,QAAQ,QAAQ,OAAM;AACxH,MAAM,eAAe,CAAC,oBAAoB,qBAAqB,mBAAmB,kBAAkB,GAAG,eAAe;AAEtH,MAAM,qBAAqB,EAAC,QAAQ,YAAY,QAAQ,OAAO,cAAc,KAAK,WAAW,QAAQ,UAAU,MAAK;AACpH,MAAM,mBAAmB,EAAC,QAAQ,UAAU,QAAQ,OAAO,cAAc,QAAQ,WAAW,SAAS,UAAU,OAAM;AAErH,MAAM,eAAe,CAAC,oBAAoB,kBAAkB,cAAc,GAAG,eAAe;AAE5F,MAAM,wBAAwB;AAAA;AAAA,EAC5B,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,EACjD,EAAC,QAAQ,QAAQ,cAAc,OAAO,UAAU,OAAO,WAAW,OAAM;AAAA,EACxE,EAAC,QAAQ,QAAQ,cAAc,KAAK,UAAU,QAAO;AACvD;AAEA,MAAM,WAAW;AAAA,EACf,EAAC,QAAQ,OAAO,QAAQ,KAAK,cAAc,KAAK,UAAU,QAAO;AAAA,EACjE,EAAC,QAAQ,OAAO,QAAQ,KAAK,cAAc,KAAK,UAAU,WAAU;AAAA,EACpE,EAAC,QAAQ,QAAQ,QAAQ,MAAM,cAAc,KAAK,UAAU,OAAM;AAAA,EAClE,EAAC,QAAQ,SAAS,QAAQ,OAAO,cAAc,KAAK,UAAU,MAAK;AAAA,EACnE,EAAC,QAAQ,WAAW,QAAQ,SAAS,cAAc,KAAK,UAAU,QAAO;AAAA,EACzE,EAAC,QAAQ,SAAS,QAAQ,OAAO,cAAc,KAAK,UAAU,OAAM;AAAA,EACpE,EAAC,QAAQ,UAAU,QAAQ,QAAQ,cAAc,KAAK,UAAU,OAAM;AAAA,EACtE,EAAC,QAAQ,OAAO,QAAQ,KAAK,cAAc,KAAK,UAAU,MAAK;AAAA,EAC/D;AACF;AAEA,MAAM,eAAe,CAAC,GAAG,iBAAiB,kBAAkB;AAE5D,MAAM,6BAA6B;AAAA,EACjC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,IACL,EAAC,QAAQ,OAAO,cAAc,KAAK,UAAU,OAAM;AAAA,IACnD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,IACjD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,MAAK;AAAA,IAChD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,IACjD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,IACjD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,MAAK;AAAA,IAChD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA;AAAA,EACnD;AAAA,EACA,OAAO;AAAA,IACL,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,IACjD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,MAAK;AAAA,IAChD,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,EACnD;AAAA,EACA,OAAO;AAAA;AAAA,IAEL,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,OAAM;AAAA,IACjD,EAAC,QAAQ,MAAM,cAAc,KAAK,UAAU,OAAM;AAAA,IAClD,EAAC,QAAQ,OAAO,cAAc,KAAK,UAAU,QAAO;AAAA,EACtD;AAAA,EACA,OAAO;AAAA,EACP,OAAO,CAAC,EAAC,QAAQ,KAAK,cAAc,KAAK,UAAU,MAAK,CAAC;AAAA,EACzD,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA;AAAA,EAEP,OAAO,CAAC,EAAC,QAAQ,KAAK,WAAW,eAAe,QAAQ,CAAC,GAAG,GAAG,UAAU,OAAM,CAAC;AAAA,EAChF,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO,CAAC,GAAG,iBAAiB,GAAG,qBAAqB;AAAA,EACpD,OAAO;AACT;AAIA,MAAM,sBAAsB,CAAC,+BAA+B,qCAAqC,mCAAmC,qBAAqB;AAGzJ,MAAM,SAAS,CAAC,cAAc,cAAc,eAAe,WAAW,cAAc,GAAG,mBAAmB;AAC1G,MAAM,SAAS,CAAC,cAAc,YAAY,aAAa,WAAW,GAAG,mBAAmB;AACxF,MAAM,SAAS,CAAC,GAAG,qBAAqB,gBAAgB;AACxD,MAAM,SAAS,CAAC,GAAG,mBAAmB;AAEtC,MAAM,SAAS;AAAA,EACb,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,2BAA0B;AAAA,EAClF,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,QAAO;AAAA,EAChE,EAAC,QAAQ,OAAO,cAAc,KAAK,OAAO,KAAK,WAAW,2BAA0B;AAAA,EACpF,EAAC,QAAQ,SAAS,cAAc,KAAK,OAAO,MAAM,WAAW,QAAO;AAAA,EACpE;AACF;AAEA,MAAM,SAAS;AAAA,EACb,GAAG;AAAA;AAAA,EAEH,EAAC,QAAQ,MAAM,cAAc,KAAK,OAAO,KAAK,WAAW,2BAA0B;AAAA,EACnF,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB;AAAA,EAC9E,EAAC,QAAQ,SAAS,cAAc,KAAK,OAAO,KAAK,WAAW,2BAA0B;AAAA;AACxF;AAEA,MAAM,iBAAiB;AAAA;AAAA,EACrB,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,EAC/E,EAAC,QAAQ,QAAQ,cAAc,MAAM,OAAO,KAAK,WAAW,uBAAsB;AAAA,EAClF;AAAA;AACF;AAEA,MAAM,kBAAkB;AAAA;AAAA,EACtB,EAAC,QAAQ,KAAK,cAAc,MAAM,OAAO,KAAK,WAAW,uBAAsB;AAAA,EAC/E,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB;AAAA,EAC9E,EAAC,QAAQ,KAAK,cAAc,SAAS,OAAO,KAAK,WAAW,uBAAsB;AACpF;AAEA,MAAM,4BAA4B;AAAA,EAChC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,IACL,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IAC/E,EAAC,QAAQ,MAAM,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB;AAAA,IAC/E,EAAC,QAAQ,OAAO,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IACjF,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IAC/E,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB;AAAA,EAChF;AAAA,EACA,OAAO;AAAA,IACL,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IAC/E,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB;AAAA;AAAA;AAAA,IAG9E,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,4BAA4B,QAAQ,CAAC,KAAK,KAAK,KAAK,GAAG,EAAC;AAAA,EAClH;AAAA,EACA,OAAO;AAAA,IACL,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IAC/E,EAAC,QAAQ,MAAM,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,IAChF,EAAC,QAAQ,OAAO,cAAc,KAAK,OAAO,MAAM,WAAW,uBAAsB;AAAA,EACnF;AAAA,EACA,OAAO;AAAA,EACP,OAAO,CAAC,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB,CAAC;AAAA,EACvF,OAAO,CAAC,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB,CAAC;AAAA,EACvF,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO,CAAC,GAAG,QAAQ,GAAG,cAAc;AAAA,EACpC,OAAO,CAAC,EAAC,QAAQ,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,uBAAsB,CAAC;AACzF;AAoBA,SAAS,0BAA0B,qBAAqB,kBAAkB;AACxE,MAAI,CAAC,qBAAqB;AACxB,WAAO;AAAA,EACT;AACA,QAAM,WAAW,oBAAoB,SAAS,GAAG;AACjD,MAAI,UAAU;AACZ,WAAO,CAAC,oBAAoB,SAAS,gBAAgB;AAAA,EACvD;AACA,SAAO,oBAAoB,SAAS,gBAAgB;AACtD;AAGA,SAAS,mBAAmB,MAAM,OAAO;AACvC,MAAI,UAAU,QAAQ,CAAC,KAAK,KAAK,SAAS,MAAM,IAAI,GAAG;AACrD,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,QAAQ,CAAC,KAAK,KAAK,SAAS,MAAM,IAAI,GAAG;AACrD,WAAO;AAAA,EACT;AAIA,SAAO;AACT;AAGA,SAAS,6BAA6B,MAAM,UAAU;AAEpD,MAAI,CAAC,0BAA0B,KAAK,MAAM,SAAS,IAAI,GAAG;AAExD,WAAO;AAAA,EACT;AACA,MAAI,aAAa,MAAM;AAErB,QAAI,CAAC,SAAS,MAAM,MAAM,KAAK,OAAO,GAAG;AAEvC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,0BAA0B,MAAM,cAAc;AACrD,MAAI,EAAE,gBAAgB,OAAO;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,cAAc;AACjB,UAAM,WAAW,KAAK,WAAW,SAAS,GAAG;AAC7C,QAAI,UAAU;AACZ,aAAO,CAAC,KAAK,WAAW,SAAS,GAAG;AAAA,IACtC;AACA,WAAO,KAAK,WAAW,SAAS,GAAG;AAAA,EACrC;AAEA,MAAI,CAAC,0BAA0B,KAAK,YAAY,aAAa,IAAI,GAAG;AAClE,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,QAAQ,CAAC,aAAa,MAAM,MAAM,KAAK,UAAU,GAAG;AACtE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,UAAU,MAAM,OAAO,WAAW,WAAW;AACpD,MAAI,CAAC,mBAAmB,MAAM,KAAK,GAAG;AAEpC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,6BAA6B,MAAM,SAAS,GAAG;AAElD,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,0BAA0B,MAAM,SAAS,GAAG;AAG/C,WAAO;AAAA,EACT;AAIA,SAAO;AACT;AAGA,SAAS,sBAAsB,OAAO,WAAW,WAAW,YAAY,MAAM,YAAY,MAAM;AAC9F,MAAI,cAAc,QAAQ,cAAc,MAAM;AAC5C;AAAA,EACF;AACA,QAAM,OAAO,MAAM,QAAQ,QAAQ,uBAAuB,KAAK,IAAI,MAAM;AACzE,MAAI,CAAC,MAAM;AACT;AAAA,EACF;AACA,MAAI,EAAE,GAAG,IAAI,MAAM,YAAY;AAC7B;AAAA,EACF;AAKA,QAAM,YAAY,UAAU,IAAI;AAChC,YAAU,MAAM,UAAQ;AAEtB,QAAI,CAAC,UAAU,MAAM,OAAO,WAAW,SAAS,GAAG;AACjD,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,UAAU,CAAC,QAAQ,cAAc,EAAE,SAAS,SAAS,KAAK,UAAU,MAAM,MAAM,KAAK,MAAM,GAAG;AAErG,gBAAU,QAAQ,UAAU,MAAM,QAAQ,KAAK,QAAQ,EAAE;AAEzD,aAAO;AAAA,IACT;AACA,QAAI,KAAK,OAAO,CAAC,KAAK,cAAc,EAAE,SAAS,SAAS,GAAG;AACzD,gBAAU,SAAS,KAAK;AAExB,aAAO;AAAA,IACT;AAQA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,uBAAuB,OAAO,WAAW,WAAW;AAC3D,wBAAsB,OAAO,WAAW,WAAW,6BAA6B,MAAM;AACtF,wBAAsB,OAAO,WAAW,WAAW,2BAA2B,GAAG;AACnF;AAEA,SAAS,yBAAyB,OAAO,WAAW,WAAW;AAE7D,wBAAsB,OAAO,WAAW,WAAW,4BAA4B,MAAM;AAErF,wBAAsB,OAAO,WAAW,WAAW,6BAA6B,MAAM;AAGxF;AAEO,gBAAS,sBAAsB,OAAO;AAC3C,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,CAAC,IAAI,MAAM;AAGjC,6BAAyB,OAAO,IAAI,wBAAwB,OAAO,CAAC,CAAC;AAAA,EAEvE,CAAC;AACD,SAAO;AACT;AAEO,gBAAS,oBAAoB,OAAO;AACzC,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,QAAQ,CAAC,IAAI,MAAM;AAIjC,2BAAuB,OAAO,IAAI,wBAAwB,OAAO,CAAC,CAAC;AAAA,EACrE,CAAC;AAGD,MAAI,MAAM,2BAA2B;AAEnC,wBAAoB,OAAO,OAAO,IAAI;AAAA,EACxC;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["/* eslint-disable max-lines */\n/*\n* punctuation.js -- try and fix a marc field punctuation\n*\n* Author(s): Nicholas Volk <nicholas.volk@helsinki.fi>\n*\n* NOTE #1: https://www.kiwi.fi/display/kumea/Loppupisteohje is implemented via another validator/fixer (ending-punctuation).\n* This file has some support but it's now yet thorough. (And mmight never be.)\n* NOTE #2: Validator/fixer punctuation does similar stuff, but focuses on X00 fields.\n* NOTE #3: As of 2023-06-05 control subfields ($0...$9) are obsolete. Don't use them in rules.\n* (They are jumped over when looking for next (non-controlfield subfield)\n*/\nimport {validateSingleField} from './ending-punctuation.js';\nimport {tagToDataProvenanceSubfieldCode} from './dataProvenanceUtils.js';\nimport {fieldGetUnambiguousTag} from './subfield6Utils.js';\nimport createDebugLogger from 'debug';\nimport {fieldToString, isContentSubfieldCode, nvdebug} from './utils.js';\n\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:punctuation2');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nconst descriptionString = 'Remove invalid and add valid punctuation to data fields';\nexport default function () {\n return {\n description: descriptionString,\n validate, fix\n };\n\n function fix(record) {\n nvdebug(`${descriptionString}: fixer`, debugDev);\n const res = {message: [], fix: [], valid: true};\n record.fields.forEach(f => fieldFixPunctuation(f));\n return res;\n }\n\n function validate(record) {\n nvdebug(`${descriptionString}: validate`, debugDev);\n\n const fieldsNeedingModification = record.fields.filter(f => fieldNeedsModification(f, true));\n\n\n const values = fieldsNeedingModification.map(f => fieldToString(f));\n const newValues = fieldsNeedingModification.map(f => fieldGetFixedString(f, true));\n\n const messages = values.map((val, i) => `'${val}' => '${newValues[i]}'`);\n\n const res = {message: messages};\n\n res.valid = res.message.length < 1;\n return res;\n }\n}\n\n\n\nfunction isIrrelevantSubfield(subfield, tag) {\n const dataProvenanceSubfieldCode = tagToDataProvenanceSubfieldCode(tag);\n if (subfield.code === dataProvenanceSubfieldCode) {\n return true;\n }\n return !isContentSubfieldCode(subfield.code); // Currently this contains other stuff as well ($3, $4, $7, $9...)\n}\n\n\nfunction getNextRelevantSubfield(field, currSubfieldIndex) {\n return field.subfields.find((subfield, index) => index > currSubfieldIndex && !isIrrelevantSubfield(subfield, field.tag));\n}\n\nexport function fieldGetFixedString(field, add = true) {\n const cloneField = structuredClone(field);\n const operation = add ? subfieldFixPunctuation : subfieldStripPunctuation;\n cloneField.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n operation(cloneField, sf, getNextRelevantSubfield(cloneField, i));\n });\n return fieldToString(cloneField);\n}\n\nexport function fieldNeedsModification(field, add = true) {\n if (!field.subfields) {\n return false;\n }\n\n const originalFieldAsString = fieldToString(field);\n const modifiedFieldAsString = fieldGetFixedString(field, add);\n\n return modifiedFieldAsString !== originalFieldAsString;\n}\n\n/////////////////////////////////////////////////////////////////////////////////////\n// <= Above code is written for the validator logic <= //\n// => Everything below was originally transferred from reducers' punctuation.js => //\n/////////////////////////////////////////////////////////////////////////////////////\n\n\n//const stripCrap = / *[-;:,+]+$/u;\nconst needsPuncAfterAlphanumeric = /(?:[a-z0-9A-Z]|\u00E5|\u00E4|\u00F6|\u00C5|\u00C4|\u00D6)$/u;\nconst defaultNeedsPuncAfter2 = /(?:[\\]a-zA-Z0-9)]|\u00E4|\u00E5|\u00F6|\u00C5|\u00C4|\u00D6)$/u;\nconst doesNotEndInPunc = /[^!?.:;,]$/u; // non-punc for pre-240/700/XXX $, note that '.' comes if preceded by ')'\nconst blocksPuncRHS = /^(?:\\()/u;\nconst allowsPuncRHS = /^(?:[A-Za-z0-9]|\u00E5|\u00E4|\u00F6|\u00C5|\u00C4|\u00D6)/u;\nconst aToZ = 'abcdefghijklmnopqrstuvwxyz';\n\n\nconst dotIsProbablyPunc = /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6|(?:[A-Za-z0-9]|\u00C5|\u00C4|\u00D6)(?:[A-Z]|\u00C5|\u00C4|\u00D6))\\.$/u;\nconst puncIsProbablyPunc = /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6) ?[.,:;]$/u;\n// NB! 65X: Finnish terms don't use punctuation, but international ones do. Neither one is currently (2021-11-08) coded here.\n\n// Will unfortunately trigger \"Sukunimi, Th.\" type:\nconst removeColons = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / *[;:]$/u};\nconst removeX00Comma = {'code': 'abcdejnqt', 'followedBy': 'abcdenqtv#', 'context': /.,$/u, 'remove': /,$/u};\nconst cleanRHS = {'code': 'abcd', 'followedBy': 'bcde', 'context': /(?:(?:[a-z0-9]|\u00E5|\u00E4|\u00F6)\\.|,)$/u, 'contextRHS': blocksPuncRHS, 'remove': /[.,]$/u};\nconst cleanX00dCommaOrDot = {'code': 'd', 'followedBy': 'et#', 'context': /[0-9]-[,.]$/u, 'remove': /[,.]$/u};\nconst cleanX00aDot = {'code': 'abcde', 'followedBy': 'cdegj', 'context': dotIsProbablyPunc, 'remove': /\\.$/u};\nconst cleanCorruption = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / \\.$/u};\n// These $e dot removals are tricky: before removing the comma, we should know that it ain't an abbreviation such as \"esitt.\"...\nconst cleanX00eDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|j\u00E4)[.,]$/u, 'remove': /\\.$/u};\nconst cleanX11jDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|j\u00E4)[.,]$/u, 'remove': /\\.$/u};\nconst removeCommaBeforeLanguageSubfieldL = {'followedBy': 'l', 'remove': /,$/u};\nconst removeCommaBeforeTitleSubfieldT = {'followedBy': 't', 'remove': /,$/u};\n\nconst X00RemoveDotAfterBracket = {'code': 'cq', 'context': /\\)\\.$/u, 'remove': /\\.$/u};\n// 390, 800, 810, 830...\nconst cleanPuncBeforeLanguage = {'code': 'atvxyz', 'followedBy': 'l', 'context': puncIsProbablyPunc, 'remove': / *[.,:;]$/u};\n\nconst addX00aComma = {'add': ',', 'code': 'abcqejt', 'followedBy': 'cdegnr', 'context': doesNotEndInPunc, 'contextRHS': allowsPuncRHS};\nconst addX00dComma = {'name': 'X00$d ending in \"-\" does not get comma', 'add': ',', 'code': 'd', 'followedBy': 'cdeg', 'context': /[^-,.!]$/u, 'contextRHS': allowsPuncRHS};\nconst addX00aComma2 = {'add': ',', 'code': 'abcdej', 'followedBy': 'cdeg', 'context': /(?:[A-Z]|\u00C5|\u00C4|\u00D6)\\.$/u, 'contextRHS': allowsPuncRHS};\nconst addX00Dot = {'add': '.', 'code': 'abcdetv', 'followedBy': 'fklptu', 'context': needsPuncAfterAlphanumeric};\nconst addEntryFieldFinalDot = {'name': 'X00 final dot', 'add': '.', 'code': 'abcdefghijklmnopqrstuvwxyz', 'followedBy': '#', 'context': /[^.)!?-]$/u};\n\n\nconst addXX0iColon = {name: 'Punctuate relationship information', add: ':', code: 'i', context: defaultNeedsPuncAfter2}; // Not explicitly checking it, but this should always be followed by 'a' or 't'\nconst addX10bDot = {'name': 'Add X10 pre-$b dot', 'add': '.', 'code': 'ab', 'followedBy': 'b', 'context': defaultNeedsPuncAfter2};\nconst addX10Comma = {'add': ',', 'code': 'abet', 'followedBy': 'en', 'context': defaultNeedsPuncAfter2};\nconst addX10Dot = {'name': 'Add X10 final dot', 'add': '.', 'code': 'abet', 'followedBy': 'tu#', 'context': needsPuncAfterAlphanumeric};\nconst addColonToRelationshipInformation = {'name': 'Add \\':\\' to 7X0 $i relationship info', 'add': ':', 'code': 'i', 'context': defaultNeedsPuncAfter2};\n\nconst addX11Spacecolon = {name: '611 space colon(y :-)', add: ' :', code: 'nd', followedBy: 'dc', 'context': defaultNeedsPuncAfter2};\n\nconst addDotBeforeLanguageSubfieldL = {'name': 'Add dot before $l', 'add': '.', 'code': 'abepst', 'followedBy': 'l', 'context': doesNotEndInPunc};\n\n// 490:\nconst addSemicolonBeforeVolumeDesignation = {'name': 'Add \" ;\" before $v', 'add': ' ;', 'code': 'atxyz', 'followedBy': 'v', 'context': /[^;]$/u};\n\nconst NONE = 0;\nconst ADD = 2;\nconst REMOVE = 1;\nconst REMOVE_AND_ADD = 3;\n\n// Crappy punctuation consists of various crap that is somewhat common.\n// We strip crap for merge decisions. We are not trying to actively remove crap here.\n\nconst removeCrapFromAllEntryFields = [removeCommaBeforeLanguageSubfieldL, removeCommaBeforeTitleSubfieldT];\n\nconst removeX00Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, cleanX00dCommaOrDot, cleanRHS, X00RemoveDotAfterBracket, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];\nconst removeX10Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];\nconst removeX11Whatever = [removeX00Comma, cleanX11jDot, ...removeCrapFromAllEntryFields];\nconst removeX30Whatever = removeCrapFromAllEntryFields;\n\nconst remove490And830Whatever = [{'code': 'axyzv', 'followedBy': 'axyzv', 'remove': /(?: *;| *=|,)$/u}];\n\nconst linkingEntryRemoveWhatever = [\n {'code': 'i', 'followedBy': 'at', 'remove': / ?:$/u}, // ':'\n {'code': 'at', 'remove': /\\.$/u},\n // Only \". -\" separator is still used in music. We can strip it, but can only create the non-music punctuation!\n {'code': 'abdghiklmnopqrstuwxyz', 'followedBy': 'abdghiklmnopqrstuwxyz#', 'remove': /\\. -$/u}\n];\n\n\n// '!' means negation, thus '!b' means any other subfield but 'b'.\n// 'followedBy': '#' means that current subfield is the last subfield.\n// NB! Note that control subfields are ignored in punctuation rules.\n// NB #2! Control field ignorance causes issues with field 257: https://wiki.helsinki.fi/display/rdasovellusohje/Loppupisteohje\n// Might need to work on that at some point. NOT a top priority though.\n// NB #3! Final punctuation creation is/should be handled by endind-punctuation.js validator!\n\nconst crappy24X = [\n {'code': 'abnp', 'followedBy': '!c', 'remove': / \\/$/u},\n {'code': 'abn', 'followedBy': 'c', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abn', 'followedBy': 'c', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abc', 'followedBy': '#', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'abfghinp', 'followedBy': '#', 'remove': /\\.$/u, 'context': dotIsProbablyPunc},\n {'code': 'n', 'followedBy': 'p', 'remove': /\\.$/u, 'context': dotIsProbablyPunc}, // MELINDA-8817\n {'code': 'p', 'followedBy': 'pc', 'remove': /\\.$/u, 'context': dotIsProbablyPunc}, // MELINDA-8817\n removeCommaBeforeLanguageSubfieldL\n];\n\n\nconst cleanCrappyPunctuationRules = {\n '100': removeX00Whatever,\n '110': removeX10Whatever,\n '111': removeX11Whatever,\n '130': removeX30Whatever,\n '240': crappy24X,\n '245': crappy24X,\n '246': crappy24X,\n '300': [\n {'code': 'a', 'followedBy': '!b', 'remove': / *:$/u},\n {'code': 'a', 'followedBy': 'b', 'remove': /:$/u, 'context': /[^ ]:$/u},\n {'code': 'ab', 'followedBy': '!c', 'remove': / *;$/u},\n {'code': 'ab', 'followedBy': 'c', 'remove': /;$/u, 'context': /[^ ];$/u},\n {'code': 'abc', 'followedBy': '!e', 'remove': / *\\+$/u} // Removes both valid (with one space) and invalid (spaceless et al) puncs\n\n ],\n\n '490': remove490And830Whatever,\n '600': removeX00Whatever,\n '610': removeX10Whatever,\n '611': removeX11Whatever,\n '630': removeX30Whatever,\n '700': removeX00Whatever,\n '710': removeX10Whatever,\n '711': removeX11Whatever,\n '730': removeX30Whatever,\n '773': linkingEntryRemoveWhatever,\n '774': linkingEntryRemoveWhatever,\n '776': linkingEntryRemoveWhatever,\n '787': linkingEntryRemoveWhatever,\n '800': removeX00Whatever,\n '810': removeX10Whatever,\n '830': remove490And830Whatever,\n '946': crappy24X\n};\n\nconst cleanLegalX00Comma = {'code': 'abcdetn', 'followedBy': 'cdegjnr', 'context': /.,$/u, 'remove': /,$/u};\n// Accept upper case letters in X00$b, since they are probably Roman numerals.\nconst cleanLegalX00bDot = {'code': 'b', 'followedBy': 't#', context: /^[IVXLCDM]+\\.$/u, 'remove': /\\.$/u};\nconst cleanLegalX00iColon = {'code': 'i', 'followedBy': 'a', 'remove': / *:$/u}; // NB! context is not needed\nconst cleanLegalX00Dot = {'code': 'abcdetkvl', 'followedBy': 'tklu#', 'context': /(?:[a-z0-9)]|\u00E5|\u00E4|\u00F6)\\.$/u, 'remove': /\\.$/u};\nconst cleanDotBeforeLanguageSubfieldL = {'name': 'pre-language-$l dot', 'followedBy': 'l', 'context': /.\\.$/u, 'remove': /\\.$/u};\n\nconst legalEntryField = [cleanDotBeforeLanguageSubfieldL];\n\nconst legalX11SpaceColon = {name: 'legal X11 spacecolony', code: 'nd', followedBy: 'dc', context: / :$/u, remove: / :$/u};\nconst legalX00punc = [cleanLegalX00Comma, cleanLegalX00iColon, cleanLegalX00bDot, cleanLegalX00Dot, ...legalEntryField];\n\nconst cleanLegalX10Comma = {'name': 'X10comma', 'code': 'abe', 'followedBy': 'e', 'context': /.,$/u, 'remove': /,$/u};\nconst cleanLegalX10Dot = {'name': 'X10dot', 'code': 'abt', 'followedBy': 'bst#', 'context': /.\\.$/u, 'remove': /\\.$/u};\n\nconst legalX10punc = [cleanLegalX10Comma, cleanLegalX10Dot, cleanX00eDot, ...legalEntryField];\n\nconst cleanLegalSeriesTitle = [ // 490 and 830\n {'code': 'a', 'followedBy': 'a', 'remove': / =$/u},\n {'code': 'axyz', 'followedBy': 'xyz', 'remove': /,$/u, 'context': /.,$/u},\n {'code': 'axyz', 'followedBy': 'v', 'remove': / *;$/u}\n];\n\nconst clean24X = [\n {'name': 'I:A', 'code': 'i', 'followedBy': 'a', 'remove': / *:$/u},\n {'name': 'A:B', 'code': 'a', 'followedBy': 'b', 'remove': / [:;=]$/u},\n {'name': 'AB:K', 'code': 'ab', 'followedBy': 'k', 'remove': / :$/u},\n {'name': 'ABK:F', 'code': 'abk', 'followedBy': 'f', 'remove': /,$/u},\n {'name': 'ABFNP:C', 'code': 'abfnp', 'followedBy': 'c', 'remove': / \\/$/u},\n {'name': 'ABN:N', 'code': 'abn', 'followedBy': 'n', 'remove': /\\.$/u},\n {'name': 'ABNP:#', 'code': 'abnp', 'followedBy': '#', 'remove': /\\.$/u},\n {'name': 'N:P', 'code': 'n', 'followedBy': 'p', 'remove': /,$/u},\n cleanDotBeforeLanguageSubfieldL\n];\n\nconst legalX11Punc = [...legalEntryField, legalX11SpaceColon];\n\nconst cleanValidPunctuationRules = {\n '100': legalX00punc,\n '110': legalX10punc,\n '111': legalX11Punc,\n '130': legalEntryField,\n '240': clean24X,\n '243': clean24X,\n '245': clean24X,\n '246': clean24X,\n '260': [\n {'code': 'abc', 'followedBy': 'a', 'remove': / ;$/u},\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},\n {'code': 'c', 'followedBy': '#', 'remove': /\\.$/u},\n {'code': 'd', 'followedBy': 'e', 'remove': / :$/u},\n {'code': 'e', 'followedBy': 'f', 'remove': /,$/u},\n {'code': 'f', 'followedBy': '#', 'remove': /\\.$/u} // Probably ')' but should it be removed?\n ],\n '264': [\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'b', 'followedBy': 'c', 'remove': /,$/u},\n {'code': 'c', 'followedBy': '#', 'remove': /\\.$/u}\n ],\n '300': [\n // NB! Remove crap as well, thus the '*' in / *:$/\n {'code': 'a', 'followedBy': 'b', 'remove': / :$/u},\n {'code': 'ab', 'followedBy': 'c', 'remove': / ;$/u},\n {'code': 'abc', 'followedBy': 'e', 'remove': / \\+$/u}\n ],\n '490': cleanLegalSeriesTitle,\n '534': [{'code': 'p', 'followedBy': 'c', 'remove': /:$/u}],\n '600': legalX00punc,\n '610': legalX10punc,\n '611': legalX11Punc,\n '630': legalEntryField,\n // Experimental, MET366-ish (end punc in internationally valid, but we don't use it here in Finland):\n '648': [{'code': 'a', 'content': /^[0-9]+\\.$/u, 'ind2': ['4'], 'remove': /\\.$/u}],\n '700': legalX00punc,\n '710': legalX10punc,\n '711': legalX11Punc,\n '730': legalEntryField,\n '800': legalX00punc,\n '810': legalX10punc,\n '811': legalX11Punc,\n '830': [...legalEntryField, ...cleanLegalSeriesTitle],\n '946': clean24X\n};\n\n\n// Overgeneralizes a bit: eg. addColonToRelationshipInformation only applies to 700/710 but as others don't have $i, it's fine.\nconst addToAllEntryFields = [addDotBeforeLanguageSubfieldL, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation, addEntryFieldFinalDot];\n\n\nconst addX00 = [addXX0iColon, addX00aComma, addX00aComma2, addX00Dot, addX00dComma, ...addToAllEntryFields];\nconst addX10 = [addXX0iColon, addX10bDot, addX10Comma, addX10Dot, ...addToAllEntryFields];\nconst addX11 = [...addToAllEntryFields, addX11Spacecolon];\nconst addX30 = [...addToAllEntryFields];\n\nconst add24X = [\n {'code': 'i', 'followedBy': 'a', 'add': ':', 'context': needsPuncAfterAlphanumeric},\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': '[^:]$'},\n {'code': 'abk', 'followedBy': 'f', 'add': ',', 'context': needsPuncAfterAlphanumeric},\n {'code': 'abfnp', 'followedBy': 'c', 'add': ' /', 'context': '[^/]$'},\n addDotBeforeLanguageSubfieldL\n];\n\nconst add245 = [\n ...add24X,\n // Blah! Also \"$a = $b\" and \"$a ; $b\" can be valid... But ' :' is better than nothing, I guess...\n {'code': 'ab', 'followedBy': 'n', 'add': '.', 'context': needsPuncAfterAlphanumeric},\n {'code': 'n', 'followedBy': 'p', 'add': ',', 'context': defaultNeedsPuncAfter2},\n {'code': 'abnpc', 'followedBy': '#', 'add': '.', 'context': needsPuncAfterAlphanumeric} // Stepping on \"punctuation validator's\" toes\n];\n\nconst addSeriesTitle = [ // 490 and 830\n {'code': 'a', 'followedBy': 'a', 'add': ' =', 'context': defaultNeedsPuncAfter2},\n {'code': 'axyz', 'followedBy': 'xy', 'add': ',', 'context': defaultNeedsPuncAfter2},\n addSemicolonBeforeVolumeDesignation // eg. 490$axyz-$v\n];\n\nconst addLinkingEntry = [ // NB! Music 773 uses different punctuation rules, that are not implement here (can they even be?)\n {'code': 'i', 'followedBy': aToZ, 'add': ':', 'context': defaultNeedsPuncAfter2},\n {'code': 'a', 'followedBy': 't', 'add': '.', 'context': defaultNeedsPuncAfter2},\n {'code': 't', 'followedBy': 'dghoz', 'add': '.', 'context': defaultNeedsPuncAfter2}\n];\n\nconst addPairedPunctuationRules = {\n '100': addX00,\n '110': addX10,\n '111': addX11,\n '130': addX30,\n '240': add24X,\n '243': add24X,\n '245': add245,\n '246': add24X,\n '260': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'ab', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},\n {'code': 'abc', 'followedBy': 'a', 'add': ' ;', 'context': defaultNeedsPuncAfter2},\n {'code': 'e', 'followedBy': 'f', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'f', 'followedBy': 'g', 'add': ',', 'context': defaultNeedsPuncAfter2}\n ],\n '264': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'b', 'followedBy': 'c', 'add': ',', 'context': defaultNeedsPuncAfter2},\n // NB! The $c rule messes dotless exception \"264 #4 $c p1983\" up\n // We'll need to add a hacky postprocessor for this? Add 'hasInd1': '0123' etc?\n {'code': 'c', 'followedBy': '#', 'add': '.', 'context': needsPuncAfterAlphanumeric, 'ind2': ['0', '1', '2', '3']}\n ],\n '300': [\n {'code': 'a', 'followedBy': 'b', 'add': ' :', 'context': defaultNeedsPuncAfter2},\n {'code': 'ab', 'followedBy': 'c', 'add': ' ;', 'context': defaultNeedsPuncAfter2},\n {'code': 'abc', 'followedBy': 'e', 'add': ' +', 'context': defaultNeedsPuncAfter2}\n ],\n '490': addSeriesTitle,\n '506': [{'code': 'a', 'followedBy': '#', 'add': '.', 'context': defaultNeedsPuncAfter2}],\n '534': [{'code': 'p', 'followedBy': 'c', 'add': ':', 'context': defaultNeedsPuncAfter2}],\n '600': addX00,\n '610': addX10,\n '611': addX11,\n '630': addX30,\n '700': addX00,\n '710': addX10,\n '711': addX11,\n '730': addX30,\n '773': addLinkingEntry,\n '787': addLinkingEntry,\n '800': addX00,\n '810': addX10,\n '811': addX11,\n '830': [...addX30, ...addSeriesTitle],\n '946': [{'code': 'i', 'followedBy': 'a', 'add': ':', 'context': defaultNeedsPuncAfter2}]\n};\n\n/*\nfunction debugRule(rule) {\n //nvdebug('', debugDev);\n nvdebug(`NAME ${rule.name ? rule.name : '<unnamed>'}`, debugDev);\n nvdebug(`SUBFIELD CODE '${rule.code}' FOLLOWED BY SUBFIELD CODE '${rule.followedBy}'`, debugDev);\n if ('add' in rule) {\n nvdebug(`ADD '${rule.add}'`, debugDev);\n }\n if ('remove' in rule) {\n nvdebug(`REMOVE '${rule.remove}'`, debugDev);\n }\n if ('context' in rule) {\n nvdebug(`CONTEXT '${rule.context.toString()}'`, debugDev);\n }\n //nvdebug('', debugDev);\n}\n*/\n\nfunction ruleAppliesToSubfieldCode(targetSubfieldCodes, currSubfieldCode) {\n if (!targetSubfieldCodes) { // We are not interested in what subfield precedes 240$l, ',' is removed anyway\n return true;\n }\n const negation = targetSubfieldCodes.includes('!');\n if (negation) {\n return !targetSubfieldCodes.includes(currSubfieldCode);\n }\n return targetSubfieldCodes.includes(currSubfieldCode);\n}\n\n\nfunction ruleAppliesToField(rule, field) {\n if ('ind1' in rule && !rule.ind1.includes(field.ind1)) {\n return false;\n }\n\n if ('ind2' in rule && !rule.ind2.includes(field.ind2)) {\n return false;\n }\n\n // If we want to check, say, $2, it should be implemented here!\n\n return true;\n}\n\n\nfunction ruleAppliesToCurrentSubfield(rule, subfield) {\n //nvdebug(` Apply rule on LHS?`, debugDev);\n if (!ruleAppliesToSubfieldCode(rule.code, subfield.code)) {\n //nvdebug(` Reject rule!`, debugDev);\n return false;\n }\n if ('context' in rule) {\n //nvdebug(` Check '${subfield.value}' versus '${rule.context.toString()}'`, debugDev);\n if (!subfield.value.match(rule.context)) { // njsscan-ignore: regex_injection_dos\n //nvdebug(` Reject rule!`, debugDev);\n return false;\n }\n }\n //nvdebug(` Apply rule!`, debugDev);\n return true;\n}\n\nfunction ruleAppliesToNextSubfield(rule, nextSubfield) {\n if (!('followedBy' in rule)) { // Return true, if we are not interested in the next subfield\n return true;\n }\n // The '#' existence check applies only to the RHS field. LHS always exists.\n if (!nextSubfield) {\n const negation = rule.followedBy.includes('!');\n if (negation) {\n return !rule.followedBy.includes('#');\n }\n return rule.followedBy.includes('#');\n }\n\n if (!ruleAppliesToSubfieldCode(rule.followedBy, nextSubfield.code)) {\n return false;\n }\n if ('contextRHS' in rule && !nextSubfield.value.match(rule.contextRHS)) { // njsscan-ignore: regex_injection_dos\n return false;\n }\n return true;\n}\n\nfunction checkRule(rule, field, subfield1, subfield2) {\n if (!ruleAppliesToField(rule, field)) {\n //nvdebug(`FAIL ON WHOLE FIELD: '${fieldToString(field)}`, debugDev);\n return false;\n }\n //const name = rule.name || 'UNNAMED';\n if (!ruleAppliesToCurrentSubfield(rule, subfield1)) {\n //nvdebug(`${name}: FAIL ON LHS SUBFIELD: '$${subfield1.code} ${subfield1.value}', SF=${rule.code}`, debugDev);\n return false;\n }\n\n // NB! This is not a perfect solution. We might have $e$0$e where $e$0 punctuation should actually be based on $e$e rules\n if (!ruleAppliesToNextSubfield(rule, subfield2)) {\n //const msg = subfield2 ? `${name}: FAIL ON RHS SUBFIELD '${subfield2.code}' not in [${rule.followedBy}]` : `${name}: FAIL ON RHS FIELD`;\n //nvdebug(msg, debugDev);\n return false;\n }\n\n \n //nvdebug(`${rule.name ? rule.name : '<unnamed>'}: ACCEPT ${rule.code} (${subfield1.code}), SF2=${rule.followedBy} (${subfield2 ? subfield2.code : '#'})`, debugDev);\n return true;\n}\n\n\nfunction applyPunctuationRules(field, subfield1, subfield2, ruleArray = null, operation = NONE) {\n if (operation === NONE || ruleArray === null) { // !fieldIsApplicable(field, ruleArray)) {\n return;\n }\n const tag2 = field.tag === '880' ? fieldGetUnambiguousTag(field) : field.tag;\n if (!tag2) {\n return;\n }\n if (!(`${tag2}` in ruleArray)) {\n return;\n }\n\n //nvdebug(`PUNCTUATE ${field.tag}/${tag2} '${subfieldToString(subfield1)}' XXX '${subfield2 ? subfieldToString(subfield2) : '#'} }`, debugDev);\n\n //nvdebug(`OP=${operation} ${tag2}: '${subfield1.code}: ${subfield1.value}' ??? '${subfield2 ? subfield2.code : '#'}'`, debugDev);\n const candRules = ruleArray[tag2];\n candRules.every(rule => { // uses \"every\", not \"forEach\", so that only one rule is applies to the given subfields\n //debugRule(rule);\n if (!checkRule(rule, field, subfield1, subfield2)) {\n return true;\n }\n\n //const originalValue = subfield1.value;\n if (rule.remove && [REMOVE, REMOVE_AND_ADD].includes(operation) && subfield1.value.match(rule.remove)) {\n //nvdebug(` PUNC REMOVAL TO BE PERFORMED FOR $${subfield1.code} '${subfield1.value}'`, debugDev);\n subfield1.value = subfield1.value.replace(rule.remove, '');\n //nvdebug(` PUNC REMOVAL PERFORMED FOR '${subfield1.value}'`, debugDev);\n return false;\n }\n if (rule.add && [ADD, REMOVE_AND_ADD].includes(operation)) {\n subfield1.value += rule.add;\n //nvdebug(` ADDED '${rule.add}' TO FORM '${subfield1.value}' USING RULE ${rule.name}`, debugDev);\n return false;\n }\n\n /*\n if (subfield1.value !== originalValue) {\n nvdebug(` PROCESS PUNC: '\u2021${subfield1.code} ${originalValue}' => '\u2021${subfield1.code} ${subfield1.value}'`, debugDev);\n }\n */\n\n return true;\n });\n}\n\nfunction subfieldFixPunctuation(field, subfield1, subfield2) {\n applyPunctuationRules(field, subfield1, subfield2, cleanCrappyPunctuationRules, REMOVE);\n applyPunctuationRules(field, subfield1, subfield2, addPairedPunctuationRules, ADD);\n}\n\nfunction subfieldStripPunctuation(field, subfield1, subfield2) {\n //nvdebug(`FSP1: '${subfield1.value}'`, debugDev);\n applyPunctuationRules(field, subfield1, subfield2, cleanValidPunctuationRules, REMOVE);\n //nvdebug(`FSP2: '${subfield1.value}'`, debugDev);\n applyPunctuationRules(field, subfield1, subfield2, cleanCrappyPunctuationRules, REMOVE);\n //nvdebug(`FSP3: '${subfield1.value}'`, debugDev);\n\n}\n\nexport function fieldStripPunctuation(field) {\n if (!field.subfields) {\n return field;\n }\n\n field.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n subfieldStripPunctuation(field, sf, getNextRelevantSubfield(field, i));\n\n });\n return field;\n}\n\nexport function fieldFixPunctuation(field) {\n if (!field.subfields) {\n return field;\n }\n //nvdebug(`################### fieldFixPunctuation() TEST ${fieldToString(field)}`, debugDev);\n\n field.subfields.forEach((sf, i) => {\n // NB! instead of next subfield, we should actually get next *non-control-subfield*!!!\n // (In plain English: We should skip $0 - $9 at least, maybe $w as well...)\n // We'll need some magic for field 257 here, do we? (Also Finnish lexicons vs global lexicons in 65X fields)\n subfieldFixPunctuation(field, sf, getNextRelevantSubfield(field, i));\n });\n\n // Use shared code for final punctuation (sadly this does not fix intermediate punc):\n if (field.useExternalEndPunctuation) {\n // addFinalPunctuation(field); // local version. use shared code instead.\n validateSingleField(field, false, true); // NB! Don't use field.tag as second argument! It's a string, not an int. 3rd arg must be true (=fix)\n }\n return field;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,3 @@
1
- import clone from "clone";
2
1
  import { fieldToString } from "./utils.js";
3
2
  export default function() {
4
3
  return {
@@ -30,7 +29,7 @@ export default function() {
30
29
  return handleZxx(remainingFields, [...result, message2]);
31
30
  }
32
31
  if (validateMode) {
33
- const clonedField = clone(currField);
32
+ const clonedField = structuredClone(currField);
34
33
  clonedField.subfields = otherSubfields;
35
34
  const modifiedString2 = fieldToString(clonedField);
36
35
  const message2 = `Modify '${originalString}' => '${modifiedString2}`;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/remove-041-zxx.js"],
4
- "sourcesContent": ["/*\n// remove-041-zxx.js\n//\n// https://wiki.helsinki.fi/xwiki/bin/view/rdasovellusohje/RDA-kuvailu%20MARC%2021%20-formaatilla/RDA-sovellusohje/008%20Kontrollikentt%C3%A4/#Hmp.35-37Kieli\n// \"008-kielikoodi on sama kuin ensimm\u00E4inen 041-kent\u00E4n a- (tai d-) osakentt\u00E4. Poikkeuksen muodostaa koodi zxx:\n// jos tietueen 008ssa on kielikoodi zxx (ei kielellist\u00E4 sis\u00E4lt\u00F6\u00E4, esim. soitinmusiikki), tietueessa ei voi olla 041 a- eik\u00E4 d-osakentt\u00E4\u00E4.\"\n// Effectively this means, that 'zxx' does not appear in 041!\n// Author(s): Nicholas Volk\n*/\n\n//import createDebugLogger from 'debug';\nimport clone from 'clone';\n\nimport {fieldToString} from './utils.js';\n\n// const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:remove-041-zxx');\n\n\n\n\nexport default function () {\n\n return {\n description: \"Remove 'zxx' from f041\",\n validate, fix\n };\n\n function zxxRemoval041(record, validateMode) {\n const fields041 = record.fields.filter(f => f.tag === '041');\n\n return handleZxx(fields041);\n\n function handleZxx(fields, result = []) {\n const [currField, ...remainingFields] = fields;\n if ( !currField) {\n return result;\n }\n // Theoretically 'zxx' might mean something in ISO-639-3 or some other language code list:\n if (!currField.subfields || currField.subfields.some(sf => sf.code === '2')) {\n return handleZxx(remainingFields, result);\n }\n // Presumable 'zxx' is bad in any data subfield, not just 'a' and 'd':\n const otherSubfields = currField.subfields.filter(sf => (!sf.code.match(/^[a-z]$/u) || sf.value !== 'zxx'));\n if (otherSubfields.length === currField.subfields.length) {\n return handleZxx(remainingFields, result);\n }\n const originalString = fieldToString(currField);\n if (otherSubfields.length === 0) {\n const message = `Remove '${originalString}'`;\n if (!validateMode) {\n record.removeField(currField);\n }\n return handleZxx(remainingFields, [...result, message]);\n }\n // Some subfields are removed:\n if (validateMode) {\n // NB! In validation field is not really deleted, and the non-deleted field might trigger other fixes (than won't be done to a deleted field) later on\n const clonedField = clone(currField);\n clonedField.subfields = otherSubfields;\n const modifiedString = fieldToString(clonedField);\n const message = `Modify '${originalString}' => '${modifiedString}`;\n return handleZxx(remainingFields, [...result, message]);\n }\n currField.subfields = otherSubfields;\n const modifiedString = fieldToString(currField);\n const message = `Modify '${originalString}' => '${modifiedString}`;\n return handleZxx(remainingFields, [...result, message]);\n }\n }\n\n function fix(record, validateMode = false) {\n const messages = zxxRemoval041(record, validateMode);\n\n if (validateMode) {\n return {message: messages, valid: messages.length === 0};\n }\n return {message: [], fix: messages, valid: true};\n }\n\n\n function validate(record) {\n return fix(record, true);\n }\n}\n\n"],
5
- "mappings": "AAWA,OAAO,WAAW;AAElB,SAAQ,qBAAoB;AAO5B,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,cAAc,QAAQ,cAAc;AAC3C,UAAM,YAAY,OAAO,OAAO,OAAO,OAAK,EAAE,QAAQ,KAAK;AAE3D,WAAO,UAAU,SAAS;AAE1B,aAAS,UAAU,QAAQ,SAAS,CAAC,GAAG;AACtC,YAAM,CAAC,WAAW,GAAG,eAAe,IAAI;AACxC,UAAK,CAAC,WAAW;AACf,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,UAAU,aAAa,UAAU,UAAU,KAAK,QAAM,GAAG,SAAS,GAAG,GAAG;AAC3E,eAAO,UAAU,iBAAiB,MAAM;AAAA,MAC1C;AAEA,YAAM,iBAAiB,UAAU,UAAU,OAAO,QAAO,CAAC,GAAG,KAAK,MAAM,UAAU,KAAK,GAAG,UAAU,KAAM;AAC1G,UAAI,eAAe,WAAW,UAAU,UAAU,QAAQ;AACxD,eAAO,UAAU,iBAAiB,MAAM;AAAA,MAC1C;AACA,YAAM,iBAAiB,cAAc,SAAS;AAC9C,UAAI,eAAe,WAAW,GAAG;AAC/B,cAAMA,WAAU,WAAW,cAAc;AACzC,YAAI,CAAC,cAAc;AACjB,iBAAO,YAAY,SAAS;AAAA,QAC9B;AACA,eAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQA,QAAO,CAAC;AAAA,MACxD;AAEA,UAAI,cAAc;AAEhB,cAAM,cAAc,MAAM,SAAS;AACnC,oBAAY,YAAY;AACxB,cAAMC,kBAAiB,cAAc,WAAW;AAChD,cAAMD,WAAU,WAAW,cAAc,SAASC,eAAc;AAChE,eAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQD,QAAO,CAAC;AAAA,MACxD;AACA,gBAAU,YAAY;AACtB,YAAM,iBAAiB,cAAc,SAAS;AAC9C,YAAM,UAAU,WAAW,cAAc,SAAS,cAAc;AAChE,aAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQ,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,WAAS,IAAI,QAAQ,eAAe,OAAO;AACzC,UAAM,WAAW,cAAc,QAAQ,YAAY;AAEnD,QAAI,cAAc;AACf,aAAO,EAAC,SAAS,UAAU,OAAO,SAAS,WAAW,EAAC;AAAA,IAC1D;AACA,WAAO,EAAC,SAAS,CAAC,GAAG,KAAK,UAAU,OAAO,KAAI;AAAA,EACjD;AAGA,WAAS,SAAS,QAAQ;AACxB,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB;AACF;",
4
+ "sourcesContent": ["/*\n// remove-041-zxx.js\n//\n// https://wiki.helsinki.fi/xwiki/bin/view/rdasovellusohje/RDA-kuvailu%20MARC%2021%20-formaatilla/RDA-sovellusohje/008%20Kontrollikentt%C3%A4/#Hmp.35-37Kieli\n// \"008-kielikoodi on sama kuin ensimm\u00E4inen 041-kent\u00E4n a- (tai d-) osakentt\u00E4. Poikkeuksen muodostaa koodi zxx:\n// jos tietueen 008ssa on kielikoodi zxx (ei kielellist\u00E4 sis\u00E4lt\u00F6\u00E4, esim. soitinmusiikki), tietueessa ei voi olla 041 a- eik\u00E4 d-osakentt\u00E4\u00E4.\"\n// Effectively this means, that 'zxx' does not appear in 041!\n// Author(s): Nicholas Volk\n*/\n\n//import createDebugLogger from 'debug';\n\n\nimport {fieldToString} from './utils.js';\n\n// const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:remove-041-zxx');\n\n\n\n\nexport default function () {\n\n return {\n description: \"Remove 'zxx' from f041\",\n validate, fix\n };\n\n function zxxRemoval041(record, validateMode) {\n const fields041 = record.fields.filter(f => f.tag === '041');\n\n return handleZxx(fields041);\n\n function handleZxx(fields, result = []) {\n const [currField, ...remainingFields] = fields;\n if ( !currField) {\n return result;\n }\n // Theoretically 'zxx' might mean something in ISO-639-3 or some other language code list:\n if (!currField.subfields || currField.subfields.some(sf => sf.code === '2')) {\n return handleZxx(remainingFields, result);\n }\n // Presumable 'zxx' is bad in any data subfield, not just 'a' and 'd':\n const otherSubfields = currField.subfields.filter(sf => (!sf.code.match(/^[a-z]$/u) || sf.value !== 'zxx'));\n if (otherSubfields.length === currField.subfields.length) {\n return handleZxx(remainingFields, result);\n }\n const originalString = fieldToString(currField);\n if (otherSubfields.length === 0) {\n const message = `Remove '${originalString}'`;\n if (!validateMode) {\n record.removeField(currField);\n }\n return handleZxx(remainingFields, [...result, message]);\n }\n // Some subfields are removed:\n if (validateMode) {\n // NB! In validation field is not really deleted, and the non-deleted field might trigger other fixes (than won't be done to a deleted field) later on\n const clonedField = structuredClone(currField);\n clonedField.subfields = otherSubfields;\n const modifiedString = fieldToString(clonedField);\n const message = `Modify '${originalString}' => '${modifiedString}`;\n return handleZxx(remainingFields, [...result, message]);\n }\n currField.subfields = otherSubfields;\n const modifiedString = fieldToString(currField);\n const message = `Modify '${originalString}' => '${modifiedString}`;\n return handleZxx(remainingFields, [...result, message]);\n }\n }\n\n function fix(record, validateMode = false) {\n const messages = zxxRemoval041(record, validateMode);\n\n if (validateMode) {\n return {message: messages, valid: messages.length === 0};\n }\n return {message: [], fix: messages, valid: true};\n }\n\n\n function validate(record) {\n return fix(record, true);\n }\n}\n\n"],
5
+ "mappings": "AAaA,SAAQ,qBAAoB;AAO5B,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,cAAc,QAAQ,cAAc;AAC3C,UAAM,YAAY,OAAO,OAAO,OAAO,OAAK,EAAE,QAAQ,KAAK;AAE3D,WAAO,UAAU,SAAS;AAE1B,aAAS,UAAU,QAAQ,SAAS,CAAC,GAAG;AACtC,YAAM,CAAC,WAAW,GAAG,eAAe,IAAI;AACxC,UAAK,CAAC,WAAW;AACf,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,UAAU,aAAa,UAAU,UAAU,KAAK,QAAM,GAAG,SAAS,GAAG,GAAG;AAC3E,eAAO,UAAU,iBAAiB,MAAM;AAAA,MAC1C;AAEA,YAAM,iBAAiB,UAAU,UAAU,OAAO,QAAO,CAAC,GAAG,KAAK,MAAM,UAAU,KAAK,GAAG,UAAU,KAAM;AAC1G,UAAI,eAAe,WAAW,UAAU,UAAU,QAAQ;AACxD,eAAO,UAAU,iBAAiB,MAAM;AAAA,MAC1C;AACA,YAAM,iBAAiB,cAAc,SAAS;AAC9C,UAAI,eAAe,WAAW,GAAG;AAC/B,cAAMA,WAAU,WAAW,cAAc;AACzC,YAAI,CAAC,cAAc;AACjB,iBAAO,YAAY,SAAS;AAAA,QAC9B;AACA,eAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQA,QAAO,CAAC;AAAA,MACxD;AAEA,UAAI,cAAc;AAEhB,cAAM,cAAc,gBAAgB,SAAS;AAC7C,oBAAY,YAAY;AACxB,cAAMC,kBAAiB,cAAc,WAAW;AAChD,cAAMD,WAAU,WAAW,cAAc,SAASC,eAAc;AAChE,eAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQD,QAAO,CAAC;AAAA,MACxD;AACA,gBAAU,YAAY;AACtB,YAAM,iBAAiB,cAAc,SAAS;AAC9C,YAAM,UAAU,WAAW,cAAc,SAAS,cAAc;AAChE,aAAO,UAAU,iBAAiB,CAAC,GAAG,QAAQ,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAEA,WAAS,IAAI,QAAQ,eAAe,OAAO;AACzC,UAAM,WAAW,cAAc,QAAQ,YAAY;AAEnD,QAAI,cAAc;AACf,aAAO,EAAC,SAAS,UAAU,OAAO,SAAS,WAAW,EAAC;AAAA,IAC1D;AACA,WAAO,EAAC,SAAS,CAAC,GAAG,KAAK,UAAU,OAAO,KAAI;AAAA,EACjD;AAGA,WAAS,SAAS,QAAQ;AACxB,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB;AACF;",
6
6
  "names": ["message", "modifiedString"]
7
7
  }
@@ -1,5 +1,4 @@
1
1
  import { fieldToString } from "./utils.js";
2
- import clone from "clone";
3
2
  export default function() {
4
3
  return {
5
4
  description: "Validator for sanitizing vocabulary source codes in subfield $2 (MRA-532)",
@@ -24,7 +23,7 @@ export default function() {
24
23
  }
25
24
  function validateField(field, res) {
26
25
  const orig = fieldToString(field);
27
- const normalizedField = fieldSanitizeVocabularySourceCode(clone(field));
26
+ const normalizedField = fieldSanitizeVocabularySourceCode(structuredClone(field));
28
27
  const mod = fieldToString(normalizedField);
29
28
  if (orig !== mod) {
30
29
  res.message.push(`FIXABLE: '${orig}' => '${mod}'`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/sanitize-vocabulary-source-codes.js"],
4
- "sourcesContent": ["//import createDebugLogger from 'debug';\nimport {fieldToString} from './utils.js';\nimport clone from 'clone';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/sanitize-vocabulary-source-codes);\n\n// Author(s): Nicholas Volk, Joni Ollila\nexport default function () {\n\n return {\n description: 'Validator for sanitizing vocabulary source codes in subfield $2 (MRA-532)',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n const relevantFields = getRelevantFields(record);\n relevantFields.forEach(f => fieldSanitizeVocabularySourceCode(f));\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n const relevantFields = getRelevantFields(record);\n\n relevantFields.forEach(field => validateField(field, res));\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function getRelevantFields(record) {\n return record.fields.filter(f => f.subfields && f.tag.match(/^(?:6..|257|370|38.)$/u));\n }\n\n function validateField(field, res) {\n const orig = fieldToString(field);\n\n const normalizedField = fieldSanitizeVocabularySourceCode(clone(field));\n const mod = fieldToString(normalizedField);\n if (orig !== mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n res.message.push(`FIXABLE: '${orig}' => '${mod}'`);\n return;\n }\n // Handle illegal values here\n if (fieldHasUnfixableVocabularySourceCode(field)) {\n res.message.push(`CAN'T BE FIXED AUTOMATICALLY: '${orig}'`);\n return;\n }\n return;\n }\n\n\n function fieldSanitizeVocabularySourceCode(field) {\n field.subfields.forEach(sf => subfieldSanitizeVocabularySourceCode(sf));\n return field;\n }\n\n function subfieldSanitizeVocabularySourceCode(subfield) {\n if (subfield.code !== '2') {\n return;\n }\n subfield.value = stringFixVocabularySourceCode(subfield.value);\n }\n\n}\n\n// Note that language suffix is optional\nconst legalSubfieldCode = ['allars', 'kauno', 'kauno/fin', 'kauno/swe', 'mts', 'mts/fin', 'mts/swe', 'slm', 'slm/fin', 'slm/swe', 'ysa', 'yso', 'yso/eng', 'yso/fin', 'yso/sme', 'yso/swe'];\n\nfunction stringFixVocabularySourceCode(value) {\n // Try to remove spaces, change '//' to '/' and remove final '.' and '/':\n const tmp = value.replace(/ /ug, '')\n .replace(/\\/+/ug, '/')\n .replace(/(.)[./]$/gu, '$1')\n .replace(/^yso-(?:aika|paikat)\\//u, 'yso/'); // IMP-HELMET crap. Also, they still have a '.' at the end of $a...\n\n // NB! Use the modified value ONLY if the result (tmp variable) is a legal subfield code...\n if (legalSubfieldCode.includes(tmp)) {\n return tmp;\n }\n\n return value;\n}\n\nfunction fieldHasUnfixableVocabularySourceCode(field) {\n return field.subfields.some(sf => subfieldHasUnfixableVocabularySourceCode(sf));\n}\n\nfunction subfieldHasUnfixableVocabularySourceCode(subfield) {\n if (subfield.code !== '2') {\n return false;\n }\n\n // As we can't fix this here, apply this yso-rule only when validating!\n if (subfield.value.indexOf('yso/') === 0) {\n return !['yso/eng', 'yso/fin', 'yso/sme', 'yso/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('slm/') === 0) {\n return !['slm/fin', 'slm/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('mts/') === 0) {\n return !['mts/fin', 'mts/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('kauno/') === 0) {\n return !['kauno/fin', 'kauno/swe'].includes(subfield.value);\n }\n\n return false;\n}\n"],
5
- "mappings": "AACA,SAAQ,qBAAoB;AAC5B,OAAO,WAAW;AAKlB,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAC9C,UAAM,iBAAiB,kBAAkB,MAAM;AAC/C,mBAAe,QAAQ,OAAK,kCAAkC,CAAC,CAAC;AAChE,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,MAAM,EAAC,SAAS,CAAC,EAAC;AACxB,UAAM,iBAAiB,kBAAkB,MAAM;AAE/C,mBAAe,QAAQ,WAAS,cAAc,OAAO,GAAG,CAAC;AAEzD,QAAI,QAAQ,EAAE,IAAI,QAAQ,UAAU;AACpC,WAAO;AAAA,EACT;AAEA,WAAS,kBAAkB,QAAQ;AACjC,WAAO,OAAO,OAAO,OAAO,OAAK,EAAE,aAAa,EAAE,IAAI,MAAM,wBAAwB,CAAC;AAAA,EACvF;AAEA,WAAS,cAAc,OAAO,KAAK;AACjC,UAAM,OAAO,cAAc,KAAK;AAEhC,UAAM,kBAAkB,kCAAkC,MAAM,KAAK,CAAC;AACtE,UAAM,MAAM,cAAc,eAAe;AACzC,QAAI,SAAS,KAAK;AAChB,UAAI,QAAQ,KAAK,aAAa,IAAI,SAAS,GAAG,GAAG;AACjD;AAAA,IACF;AAEA,QAAI,sCAAsC,KAAK,GAAG;AAChD,UAAI,QAAQ,KAAK,kCAAkC,IAAI,GAAG;AAC1D;AAAA,IACF;AACA;AAAA,EACF;AAGA,WAAS,kCAAkC,OAAO;AAChD,UAAM,UAAU,QAAQ,QAAM,qCAAqC,EAAE,CAAC;AACtE,WAAO;AAAA,EACT;AAEA,WAAS,qCAAqC,UAAU;AACtD,QAAI,SAAS,SAAS,KAAK;AACzB;AAAA,IACF;AACA,aAAS,QAAQ,8BAA8B,SAAS,KAAK;AAAA,EAC/D;AAEF;AAGA,MAAM,oBAAoB,CAAC,UAAU,SAAS,aAAa,aAAa,OAAO,WAAW,WAAW,OAAO,WAAW,WAAW,OAAO,OAAO,WAAW,WAAW,WAAW,SAAS;AAE1L,SAAS,8BAA8B,OAAO;AAE5C,QAAM,MAAM,MAAM,QAAQ,OAAO,EAAE,EAChC,QAAQ,SAAS,GAAG,EACpB,QAAQ,cAAc,IAAI,EAC1B,QAAQ,2BAA2B,MAAM;AAG5C,MAAI,kBAAkB,SAAS,GAAG,GAAG;AACnC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,sCAAsC,OAAO;AACpD,SAAO,MAAM,UAAU,KAAK,QAAM,yCAAyC,EAAE,CAAC;AAChF;AAEA,SAAS,yCAAyC,UAAU;AAC1D,MAAI,SAAS,SAAS,KAAK;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,WAAW,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EAC9E;AAEA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EACxD;AAEA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EACxD;AAEA,MAAI,SAAS,MAAM,QAAQ,QAAQ,MAAM,GAAG;AAC1C,WAAO,CAAC,CAAC,aAAa,WAAW,EAAE,SAAS,SAAS,KAAK;AAAA,EAC5D;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["//import createDebugLogger from 'debug';\n\nimport {fieldToString} from './utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/sanitize-vocabulary-source-codes);\n\n// Author(s): Nicholas Volk, Joni Ollila\nexport default function () {\n\n return {\n description: 'Validator for sanitizing vocabulary source codes in subfield $2 (MRA-532)',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n const relevantFields = getRelevantFields(record);\n relevantFields.forEach(f => fieldSanitizeVocabularySourceCode(f));\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n const relevantFields = getRelevantFields(record);\n\n relevantFields.forEach(field => validateField(field, res));\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function getRelevantFields(record) {\n return record.fields.filter(f => f.subfields && f.tag.match(/^(?:6..|257|370|38.)$/u));\n }\n\n function validateField(field, res) {\n const orig = fieldToString(field);\n\n const normalizedField = fieldSanitizeVocabularySourceCode(structuredClone(field));\n const mod = fieldToString(normalizedField);\n if (orig !== mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n res.message.push(`FIXABLE: '${orig}' => '${mod}'`);\n return;\n }\n // Handle illegal values here\n if (fieldHasUnfixableVocabularySourceCode(field)) {\n res.message.push(`CAN'T BE FIXED AUTOMATICALLY: '${orig}'`);\n return;\n }\n return;\n }\n\n\n function fieldSanitizeVocabularySourceCode(field) {\n field.subfields.forEach(sf => subfieldSanitizeVocabularySourceCode(sf));\n return field;\n }\n\n function subfieldSanitizeVocabularySourceCode(subfield) {\n if (subfield.code !== '2') {\n return;\n }\n subfield.value = stringFixVocabularySourceCode(subfield.value);\n }\n\n}\n\n// Note that language suffix is optional\nconst legalSubfieldCode = ['allars', 'kauno', 'kauno/fin', 'kauno/swe', 'mts', 'mts/fin', 'mts/swe', 'slm', 'slm/fin', 'slm/swe', 'ysa', 'yso', 'yso/eng', 'yso/fin', 'yso/sme', 'yso/swe'];\n\nfunction stringFixVocabularySourceCode(value) {\n // Try to remove spaces, change '//' to '/' and remove final '.' and '/':\n const tmp = value.replace(/ /ug, '')\n .replace(/\\/+/ug, '/')\n .replace(/(.)[./]$/gu, '$1')\n .replace(/^yso-(?:aika|paikat)\\//u, 'yso/'); // IMP-HELMET crap. Also, they still have a '.' at the end of $a...\n\n // NB! Use the modified value ONLY if the result (tmp variable) is a legal subfield code...\n if (legalSubfieldCode.includes(tmp)) {\n return tmp;\n }\n\n return value;\n}\n\nfunction fieldHasUnfixableVocabularySourceCode(field) {\n return field.subfields.some(sf => subfieldHasUnfixableVocabularySourceCode(sf));\n}\n\nfunction subfieldHasUnfixableVocabularySourceCode(subfield) {\n if (subfield.code !== '2') {\n return false;\n }\n\n // As we can't fix this here, apply this yso-rule only when validating!\n if (subfield.value.indexOf('yso/') === 0) {\n return !['yso/eng', 'yso/fin', 'yso/sme', 'yso/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('slm/') === 0) {\n return !['slm/fin', 'slm/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('mts/') === 0) {\n return !['mts/fin', 'mts/swe'].includes(subfield.value);\n }\n\n if (subfield.value.indexOf('kauno/') === 0) {\n return !['kauno/fin', 'kauno/swe'].includes(subfield.value);\n }\n\n return false;\n}\n"],
5
+ "mappings": "AAEA,SAAQ,qBAAoB;AAK5B,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAC9C,UAAM,iBAAiB,kBAAkB,MAAM;AAC/C,mBAAe,QAAQ,OAAK,kCAAkC,CAAC,CAAC;AAChE,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,MAAM,EAAC,SAAS,CAAC,EAAC;AACxB,UAAM,iBAAiB,kBAAkB,MAAM;AAE/C,mBAAe,QAAQ,WAAS,cAAc,OAAO,GAAG,CAAC;AAEzD,QAAI,QAAQ,EAAE,IAAI,QAAQ,UAAU;AACpC,WAAO;AAAA,EACT;AAEA,WAAS,kBAAkB,QAAQ;AACjC,WAAO,OAAO,OAAO,OAAO,OAAK,EAAE,aAAa,EAAE,IAAI,MAAM,wBAAwB,CAAC;AAAA,EACvF;AAEA,WAAS,cAAc,OAAO,KAAK;AACjC,UAAM,OAAO,cAAc,KAAK;AAEhC,UAAM,kBAAkB,kCAAkC,gBAAgB,KAAK,CAAC;AAChF,UAAM,MAAM,cAAc,eAAe;AACzC,QAAI,SAAS,KAAK;AAChB,UAAI,QAAQ,KAAK,aAAa,IAAI,SAAS,GAAG,GAAG;AACjD;AAAA,IACF;AAEA,QAAI,sCAAsC,KAAK,GAAG;AAChD,UAAI,QAAQ,KAAK,kCAAkC,IAAI,GAAG;AAC1D;AAAA,IACF;AACA;AAAA,EACF;AAGA,WAAS,kCAAkC,OAAO;AAChD,UAAM,UAAU,QAAQ,QAAM,qCAAqC,EAAE,CAAC;AACtE,WAAO;AAAA,EACT;AAEA,WAAS,qCAAqC,UAAU;AACtD,QAAI,SAAS,SAAS,KAAK;AACzB;AAAA,IACF;AACA,aAAS,QAAQ,8BAA8B,SAAS,KAAK;AAAA,EAC/D;AAEF;AAGA,MAAM,oBAAoB,CAAC,UAAU,SAAS,aAAa,aAAa,OAAO,WAAW,WAAW,OAAO,WAAW,WAAW,OAAO,OAAO,WAAW,WAAW,WAAW,SAAS;AAE1L,SAAS,8BAA8B,OAAO;AAE5C,QAAM,MAAM,MAAM,QAAQ,OAAO,EAAE,EAChC,QAAQ,SAAS,GAAG,EACpB,QAAQ,cAAc,IAAI,EAC1B,QAAQ,2BAA2B,MAAM;AAG5C,MAAI,kBAAkB,SAAS,GAAG,GAAG;AACnC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,sCAAsC,OAAO;AACpD,SAAO,MAAM,UAAU,KAAK,QAAM,yCAAyC,EAAE,CAAC;AAChF;AAEA,SAAS,yCAAyC,UAAU;AAC1D,MAAI,SAAS,SAAS,KAAK;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,WAAW,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EAC9E;AAEA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EACxD;AAEA,MAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,GAAG;AACxC,WAAO,CAAC,CAAC,WAAW,SAAS,EAAE,SAAS,SAAS,KAAK;AAAA,EACxD;AAEA,MAAI,SAAS,MAAM,QAAQ,QAAQ,MAAM,GAAG;AAC1C,WAAO,CAAC,CAAC,aAAa,WAAW,EAAE,SAAS,SAAS,KAAK;AAAA,EAC5D;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,3 @@
1
- import clone from "clone";
2
1
  import { fieldHasSubfield, fieldToString } from "./utils.js";
3
2
  import { sortByTag, sortAlphabetically, fieldOrderComparator as globalFieldOrderComparator } from "@natlibfi/marc-record/dist/marcFieldSort.js";
4
3
  import { isValidSubfield8 } from "./subfield8Utils.js";
@@ -17,7 +16,7 @@ export default function() {
17
16
  }
18
17
  function validate(record) {
19
18
  const res = { message: [] };
20
- const fields = record.fields.map((f) => clone(f));
19
+ const fields = record.fields.map((f) => structuredClone(f));
21
20
  fields.sort(fieldOrderComparator);
22
21
  const relocatedFields = fields.filter((f, i) => fieldToString(f) !== fieldToString(record.fields[i]));
23
22
  if (relocatedFields.length > 0) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/sortFields.js"],
4
- "sourcesContent": ["// Taken from project marc-record-js, file marcSortFields.js as this contains more and more Melinda-specific rules.\n\nimport clone from 'clone';\n//import createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString} from './utils.js';\nimport {sortByTag, sortAlphabetically, fieldOrderComparator as globalFieldOrderComparator} from '@natlibfi/marc-record/dist/marcFieldSort.js';\nimport {isValidSubfield8} from './subfield8Utils.js';\nimport {fieldGetUnambiguousOccurrenceNumber, fieldGetUnambiguousTag} from './subfield6Utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortFields');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\nconst BIG_BAD_NUMBER = 999999999;\nexport default function () {\n\n return {\n description: 'Sort fields using both generic and Melinda specific rules',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n\n record.fields.sort(fieldOrderComparator);\n\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n const fields = record.fields.map(f => clone(f));\n fields.sort(fieldOrderComparator);\n\n\n const relocatedFields = fields.filter((f, i) => fieldToString(f) !== fieldToString(record.fields[i]));\n\n if (relocatedFields.length > 0) {\n res.message.push(`${relocatedFields.length} field(s) in new places`);\n }\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n}\n\n\nconst relatorTermScore = { // Here bigger is better\n // NB! This is exportable as field internal $e sorting in marc-record-validators-js uses this.\n // NB! The more abstract, the earlier it appears.\n // Note that terms with same abstraction level might also have order preferences\n // We should 1) check the order of these, and 2) add translations (support Swedish at the very least)\n // work/teos > expression/ekspressio > manifestation/manifestaatin,\n // Work https://finto.fi/mts/fi/page/m1298\n 's\u00E4velt\u00E4j\u00E4': 100, 'composer': 100,\n 'kirjoittaja': 100, 'author': 100,\n 'libretisti': 100,\n 'sarjakuvantekij\u00E4': 100, 'soitonoppaan tekij\u00E4': 100,\n 'kartantekij\u00E4': 99,\n 'taiteilija': 98,\n 'designer': 90,\n 'sanoittaja': 90,\n 'k\u00E4sikirjoittaja': 90,\n\n 'kuvaaja': 89, 'valokuvaaja': 89,\n 'kokoaja': 86,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 84, 'gratulaation kohde': 84,\n 'julkaisija': 82,\n 'tuottaja': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'sovittaja': 79, 'arranger': 79,\n 'toimittaja': 78, 'editor': 78,\n 'kuvittaja': 77,\n 'esipuheen kirjoittaja': 76,\n 'alkusanojen kirjoittaja': 75, 'loppusanojen kirjoittaja': 75,\n\n 'esitt\u00E4j\u00E4': 74,\n 'johtaja': 73, // orkesterinjohtaja\n 'editointi': 71, // for music, editor/toimittaja is another thing\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n 'lukija': 61, 'kertoja': 61,\n // Manifestation level: https://finto.fi/mts/fi/page/m491\n 'graafinen suunnittelija': 50,\n 'kustantaja': 41\n // Item level: https://finto.fi/mts/fi/page/m1157\n};\n\nconst relatorTermScoreBk = {\n // https://finto.fi/mts/fi/page/m34 100-81\n 'libretisti': 100, 'sarjakuvantekij\u00E4': 100,\n 'kirjoittaja': 99, 'author': 99, 'soitonoppaan tekij\u00E4': 99,\n 'kuvaaja': 98, 'valokuvaaja': 98,\n 'kokoaja': 86, 'designer': 86,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 84, 'gratulaation kohde': 84,\n 's\u00E4velt\u00E4j\u00E4': 83, // if 300$e has CD etc\n 'sanoittaja': 82,\n 'julkaisija': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'toimittaja': 78,\n 'kuvittaja': 77,\n 'esipuheen kirjoittaja': 76,\n 'alkusanojen kirjoittaja': 75, 'loppusanojen kirjoittaja': 75,\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n 'sovittaja': 50,\n // manifestaatio\n 'graafinen suunnittelija': 40,\n // kappale/item\n 'sitoja': 25,\n 'gratulaation kirjoittaja': 24\n};\n\nconst relatorTermScoreMu = {\n 's\u00E4velt\u00E4j\u00E4': 100,\n 'sanoittaja': 99,\n 'soitonoppaan tekij\u00E4': 98,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'sovittaja': 79,\n 'johtaja': 78,\n 'esitt\u00E4j\u00E4': 77,\n 'lukija': 76,\n 'miksaaja': 75,\n // manifestaatio\n 'esitt\u00E4j\u00E4 (manifestaatio)': 69,\n\n 'graafinen suunnittelija': 50,\n 'valmistaja': 41,\n 'jakaja': 40\n // kappale/item\n\n};\n\nconst relatorTermScoreVm = { // Visual Material\n // Work\n 'ohjaaja': 100,\n 'kirjoittaja': 99, 'author': 99, // Here we assume that film/whatever is based on author's book\n 'k\u00E4sikirjoittaja': 98, 'designer': 98,\n 'kuvaaja': 89,\n 's\u00E4velt\u00E4j\u00E4': 86, // Volatile. John Williams?\n 'alkuper\u00E4isidean luoja': 85,\n 'julkaisija': 82,\n 'tuottaja': 81,\n // Expression\n 'leikkaaja': 80,\n 'sovittaja': 79,\n 'johtaja': 78,\n 'esitt\u00E4j\u00E4': 77,\n 'lukija': 76,\n 'miksaaja': 75,\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n // Manifestation\n 'kaivertaja': 60,\n 'graafinen suunnittelija': 59,\n 'kustantaja': 42,\n 'elokuvan jakelija': 41, 'jakaja': 41\n\n // Item\n};\n\nfunction normalizeValue(value) {\n // Removing last punc char is good enough for our purposes.\n // We don't handle abbreviations here etc.\n // Brackets should not happen either, should they?\n return value.replace(/[.,]$/u, '');\n}\n\nfunction scoreRelatorTermBk(normalizedValue) {\n if (normalizedValue in relatorTermScoreBk) {\n return relatorTermScoreBk[normalizedValue];\n }\n return 0;\n}\n\nfunction scoreRelatorTermMu(normalizedValue) {\n if (normalizedValue in relatorTermScoreMu) {\n return relatorTermScoreMu[normalizedValue];\n }\n return 0;\n}\n\nfunction scoreRelatorTermVm(normalizedValue) {\n if (normalizedValue in relatorTermScoreVm) {\n return relatorTermScoreVm[normalizedValue];\n }\n return 0;\n}\n\nexport function scoreRelatorTerm(value, typeOfMaterial = undefined) {\n // NB! We are currently using type of material only for sorting relator terms, not 7XX fields!!!\n const normalizedValue = normalizeValue(value);\n if (typeOfMaterial === 'BK') { // Books\n return scoreRelatorTermBk(normalizedValue);\n }\n if (typeOfMaterial === 'MU') { // Music (NB: audio books should be BK in this context!)\n return scoreRelatorTermMu(normalizedValue);\n }\n if (typeOfMaterial === 'VM') { // video material\n return scoreRelatorTermVm(normalizedValue);\n }\n if (normalizedValue in relatorTermScore) {\n return relatorTermScore[normalizedValue];\n }\n return 0;\n}\n\nexport function fieldOrderComparator(fieldA, fieldB) {\n\n //const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];\n\n const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortBySubfield6, preferFenniKeep, sortByFieldLinkAndSequenceNumber, sortByHacks];\n //const sorterFunctions = [sortByIndexTerms, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];\n\n return globalFieldOrderComparator(fieldA, fieldB, sorterFunctions);\n}\n\nfunction sortByIndexTerms(fieldA, fieldB) {\n\n const indexTermFields = ['600', '610', '611', '630', '648', '650', '651', '652', '653', '654', '655', '656', '657', '658', '659', '662'];\n\n function scoreInd2(val) {\n const ind2Score = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 8, '5': 5, '6': 6, '7': 7};\n\n if (val in ind2Score) {\n return ind2Score[val];\n }\n return 9;\n }\n\n // ATM this is not needed.\n // You may need this, if you change compare function order in sorterFunctions\n // istanbul ignore next\n if (fieldA.tag !== fieldB.tag) {\n return 0;\n }\n\n if (!indexTermFields.includes(fieldA.tag)) {\n return 0;\n }\n\n // Puts ind2=4 last\n if (scoreInd2(fieldA.ind2) > scoreInd2(fieldB.ind2)) {\n return 1;\n }\n if (scoreInd2(fieldA.ind2) < scoreInd2(fieldB.ind2)) {\n return -1;\n }\n\n function scoreDictionary(dictionary) {\n const dictionarySortIndex = {\n 'yso/fin': 0,\n 'yso/swe': 1,\n 'yso/eng': 2,\n 'slm/fin': 0.1,\n 'slm/swe': 1.1,\n 'kauno/fin': 2.1,\n 'kauno/swe': 2.2,\n 'kaunokki': 4,\n 'bella': 5\n };\n\n if (dictionary in dictionarySortIndex) {\n return dictionarySortIndex[dictionary];\n }\n return BIG_BAD_NUMBER;\n }\n\n const dictionaryA = selectFirstValue(fieldA, '2');\n const dictionaryB = selectFirstValue(fieldB, '2');\n\n const dictScoreA = scoreDictionary(dictionaryA);\n const dictScoreB = scoreDictionary(dictionaryB);\n // Use priority order for listed dictionaries:\n if (dictScoreA > dictScoreB) {\n return 1;\n }\n if (dictScoreA < dictScoreB) {\n return -1;\n }\n // Unlisted dictionaries: sort $2 value alphabetically:\n //if (dictScoreA === BIG_BAD_NUMBER) {\n if (dictionaryA > dictionaryB) {\n return 1;\n }\n if (dictionaryA < dictionaryB) {\n return -1;\n }\n //}\n return 0;\n}\n\n\nfunction preferKeep(fieldA, fieldB, keepOwner = 'FENNI') {\n const hasKeepA = fieldHasSubfield(fieldA, '9', `${keepOwner}<KEEP>`);\n const hasKeepB = fieldHasSubfield(fieldB, '9', `${keepOwner}<KEEP>`);\n\n if (hasKeepA && !hasKeepB) {\n return -1;\n }\n if (!hasKeepA && hasKeepB) {\n return 1;\n }\n\n return 0;\n}\n\n\nfunction preferFenniKeep(fieldA, fieldB) {\n const fenniPreference = preferKeep(fieldA, fieldB, 'FENNI');\n if (fenniPreference !== 0) {\n return fenniPreference;\n }\n const violaPreference = preferKeep(fieldA, fieldB, 'VIOLA');\n if (violaPreference !== 0) {\n return violaPreference;\n }\n return preferKeep(fieldA, fieldB, 'FIKKA');\n}\n\nfunction sortByRelatorTerm(fieldA, fieldB) {\n //if (!['600', '610', '611', '630', '700', '710', '711', '730', '800', '810', '811', '830'].includes(fieldA.tag)) {\n if (!['700', '710', '711', '730'].includes(fieldA.tag)) {\n return 0;\n }\n\n function fieldGetMaxRelatorTermScore(field) {\n if (!field.subfields) {\n return -2;\n }\n // If field has $t, it's a teos-nimeke-auktoriteetti, and thus meaningless. These should follow all $t-less fields...\n if (fieldHasSubfield(field, 't')) {\n return -2;\n }\n const relatorSubfieldCode = ['611', '711', '811'].includes(field.tag) ? 'j' : 'e';\n const e = field.subfields.filter(sf => sf.code === relatorSubfieldCode);\n if (e.length === 0) { // No $e is still better than having a $t\n return -1;\n }\n const scores = e.map(sf => scoreRelatorTerm(sf.value));\n //debugDev(`RELATOR SCORE FOR '${fieldToString(field)}': ${scores.join(', ')}`);\n return Math.max(...scores);\n }\n\n const scoreA = fieldGetMaxRelatorTermScore(fieldA);\n const scoreB = fieldGetMaxRelatorTermScore(fieldB);\n\n if (scoreA < scoreB) {\n return 1;\n }\n if (scoreA > scoreB) {\n return -1;\n }\n return 0;\n}\n\n\nfunction fieldGetMinLinkAndSequenceNumber(field) {\n if (!field.subfields) {\n return BIG_BAD_NUMBER;\n }\n const relevantSubfields = field.subfields.filter(sf => isValidSubfield8(sf));\n // If val is something like \"1.2\\x\" parseFloat() would give a syntax erro because of hex-like escape sequnce (at least on Chrome). Thus remove tail:\n const scores = relevantSubfields.map(sf => parseFloat(sf.value.replace(/\\\\.*$/u, '')));\n if (scores.length === 0) {\n return BIG_BAD_NUMBER;\n }\n return Math.min(...scores);\n}\n\nfunction sortByFieldLinkAndSequenceNumber(fieldA, fieldB) { // Sort by subfield $8 that is...\n const scoreA = fieldGetMinLinkAndSequenceNumber(fieldA);\n const scoreB = fieldGetMinLinkAndSequenceNumber(fieldB);\n //debugDev(` sf-8-A-score for '${fieldToString(fieldA)}: ${scoreA}`);\n //debugDev(` sf-8-B-score for '${fieldToString(fieldB)}: ${scoreB}`);\n if (scoreA === scoreB) {\n return 0;\n }\n if (scoreB === 0) {\n return 1;\n }\n if (scoreA === 0) {\n return -1;\n }\n if (scoreA > scoreB) { // smaller is better\n return 1;\n }\n return -1;\n}\n\nfunction sortByHacks(fieldA, fieldB) {\n // Place for various hackier rules\n\n if (fieldA.tag === fieldB.tag) {\n if (fieldA.tag === '041') {\n // If we have multiple f041, we want to put the $2-less first; Values from $2 whatever can not be copied to 008/35-37. (See sync-language.js.)\n const a2 = fieldA.subfields.some(sf => sf.code === '2');\n const b2 = fieldB.subfields.some(sf => sf.code === '2');\n if (a2 && !b2) {\n return 1;\n }\n if (b2 && !a2) {\n return -1;\n }\n }\n\n }\n return 0;\n}\n\nfunction sortBySubfield6(fieldA, fieldB) { // Sort by subfield $6, ex-sortByOccurrenceNumber...\n if (fieldA.tag !== '880' || fieldB.tag !== '880') {\n return 0;\n }\n\n function compareLinkingTags() {\n const tagStringA = fieldGetUnambiguousTag(fieldA);\n const tagStringB = fieldGetUnambiguousTag(fieldB);\n if (tagStringA === tagStringB || !tagStringA || !tagStringB) {\n return 0;\n }\n if (tagStringA > tagStringB) {\n return 1;\n }\n return -1;\n }\n\n function compareOccurrenceNumbers() {\n const stringA = fieldGetUnambiguousOccurrenceNumber(fieldA);\n const stringB = fieldGetUnambiguousOccurrenceNumber(fieldB);\n if (stringA === stringB) { // No action required here\n return 0;\n }\n\n // Handle expections: no occurrence number, occurrence number '00':\n if (!stringB || stringB === '00') {\n if (!stringA || stringA === '00') {\n return 0;\n }\n return -1;\n }\n if (!stringA || stringA === '00') {\n return 1;\n }\n\n // NB! We need compare ints as occurrence number can exceed 99 and be a three-digit value!\n const scoreA = parseInt(stringA, 10);\n const scoreB = parseInt(stringB, 10);\n\n if (scoreA > scoreB) { // smaller is better, thus '00' is the best\n return 1;\n }\n return -1;\n }\n\n const linkingTagComparisonResult = compareLinkingTags();\n if (linkingTagComparisonResult !== 0) {\n return linkingTagComparisonResult;\n }\n\n return compareOccurrenceNumbers();\n}\n\n\nfunction selectFirstValue(field, subcode) {\n return field.subfields\n .filter(subfield => subcode === subfield.code)\n .map(subfield => subfield.value)\n .slice(0, 1);\n}\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Taken from project marc-record-js, file marcSortFields.js as this contains more and more Melinda-specific rules.\n\n\n//import createDebugLogger from 'debug';\nimport {fieldHasSubfield, fieldToString} from './utils.js';\nimport {sortByTag, sortAlphabetically, fieldOrderComparator as globalFieldOrderComparator} from '@natlibfi/marc-record/dist/marcFieldSort.js';\nimport {isValidSubfield8} from './subfield8Utils.js';\nimport {fieldGetUnambiguousOccurrenceNumber, fieldGetUnambiguousTag} from './subfield6Utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortFields');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\nconst BIG_BAD_NUMBER = 999999999;\nexport default function () {\n\n return {\n description: 'Sort fields using both generic and Melinda specific rules',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n\n record.fields.sort(fieldOrderComparator);\n\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n const fields = record.fields.map(f => structuredClone(f));\n fields.sort(fieldOrderComparator);\n\n\n const relocatedFields = fields.filter((f, i) => fieldToString(f) !== fieldToString(record.fields[i]));\n\n if (relocatedFields.length > 0) {\n res.message.push(`${relocatedFields.length} field(s) in new places`);\n }\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n}\n\n\nconst relatorTermScore = { // Here bigger is better\n // NB! This is exportable as field internal $e sorting in marc-record-validators-js uses this.\n // NB! The more abstract, the earlier it appears.\n // Note that terms with same abstraction level might also have order preferences\n // We should 1) check the order of these, and 2) add translations (support Swedish at the very least)\n // work/teos > expression/ekspressio > manifestation/manifestaatin,\n // Work https://finto.fi/mts/fi/page/m1298\n 's\u00E4velt\u00E4j\u00E4': 100, 'composer': 100,\n 'kirjoittaja': 100, 'author': 100,\n 'libretisti': 100,\n 'sarjakuvantekij\u00E4': 100, 'soitonoppaan tekij\u00E4': 100,\n 'kartantekij\u00E4': 99,\n 'taiteilija': 98,\n 'designer': 90,\n 'sanoittaja': 90,\n 'k\u00E4sikirjoittaja': 90,\n\n 'kuvaaja': 89, 'valokuvaaja': 89,\n 'kokoaja': 86,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 84, 'gratulaation kohde': 84,\n 'julkaisija': 82,\n 'tuottaja': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'sovittaja': 79, 'arranger': 79,\n 'toimittaja': 78, 'editor': 78,\n 'kuvittaja': 77,\n 'esipuheen kirjoittaja': 76,\n 'alkusanojen kirjoittaja': 75, 'loppusanojen kirjoittaja': 75,\n\n 'esitt\u00E4j\u00E4': 74,\n 'johtaja': 73, // orkesterinjohtaja\n 'editointi': 71, // for music, editor/toimittaja is another thing\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n 'lukija': 61, 'kertoja': 61,\n // Manifestation level: https://finto.fi/mts/fi/page/m491\n 'graafinen suunnittelija': 50,\n 'kustantaja': 41\n // Item level: https://finto.fi/mts/fi/page/m1157\n};\n\nconst relatorTermScoreBk = {\n // https://finto.fi/mts/fi/page/m34 100-81\n 'libretisti': 100, 'sarjakuvantekij\u00E4': 100,\n 'kirjoittaja': 99, 'author': 99, 'soitonoppaan tekij\u00E4': 99,\n 'kuvaaja': 98, 'valokuvaaja': 98,\n 'kokoaja': 86, 'designer': 86,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 84, 'gratulaation kohde': 84,\n 's\u00E4velt\u00E4j\u00E4': 83, // if 300$e has CD etc\n 'sanoittaja': 82,\n 'julkaisija': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'toimittaja': 78,\n 'kuvittaja': 77,\n 'esipuheen kirjoittaja': 76,\n 'alkusanojen kirjoittaja': 75, 'loppusanojen kirjoittaja': 75,\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n 'sovittaja': 50,\n // manifestaatio\n 'graafinen suunnittelija': 40,\n // kappale/item\n 'sitoja': 25,\n 'gratulaation kirjoittaja': 24\n};\n\nconst relatorTermScoreMu = {\n 's\u00E4velt\u00E4j\u00E4': 100,\n 'sanoittaja': 99,\n 'soitonoppaan tekij\u00E4': 98,\n 'alkuper\u00E4isidean luoja': 85,\n 'teoksella kunnioitettu': 81,\n // expression: https://finto.fi/mts/fi/page/m153\n 'sovittaja': 79,\n 'johtaja': 78,\n 'esitt\u00E4j\u00E4': 77,\n 'lukija': 76,\n 'miksaaja': 75,\n // manifestaatio\n 'esitt\u00E4j\u00E4 (manifestaatio)': 69,\n\n 'graafinen suunnittelija': 50,\n 'valmistaja': 41,\n 'jakaja': 40\n // kappale/item\n\n};\n\nconst relatorTermScoreVm = { // Visual Material\n // Work\n 'ohjaaja': 100,\n 'kirjoittaja': 99, 'author': 99, // Here we assume that film/whatever is based on author's book\n 'k\u00E4sikirjoittaja': 98, 'designer': 98,\n 'kuvaaja': 89,\n 's\u00E4velt\u00E4j\u00E4': 86, // Volatile. John Williams?\n 'alkuper\u00E4isidean luoja': 85,\n 'julkaisija': 82,\n 'tuottaja': 81,\n // Expression\n 'leikkaaja': 80,\n 'sovittaja': 79,\n 'johtaja': 78,\n 'esitt\u00E4j\u00E4': 77,\n 'lukija': 76,\n 'miksaaja': 75,\n 'k\u00E4\u00E4nt\u00E4j\u00E4': 70,\n // Manifestation\n 'kaivertaja': 60,\n 'graafinen suunnittelija': 59,\n 'kustantaja': 42,\n 'elokuvan jakelija': 41, 'jakaja': 41\n\n // Item\n};\n\nfunction normalizeValue(value) {\n // Removing last punc char is good enough for our purposes.\n // We don't handle abbreviations here etc.\n // Brackets should not happen either, should they?\n return value.replace(/[.,]$/u, '');\n}\n\nfunction scoreRelatorTermBk(normalizedValue) {\n if (normalizedValue in relatorTermScoreBk) {\n return relatorTermScoreBk[normalizedValue];\n }\n return 0;\n}\n\nfunction scoreRelatorTermMu(normalizedValue) {\n if (normalizedValue in relatorTermScoreMu) {\n return relatorTermScoreMu[normalizedValue];\n }\n return 0;\n}\n\nfunction scoreRelatorTermVm(normalizedValue) {\n if (normalizedValue in relatorTermScoreVm) {\n return relatorTermScoreVm[normalizedValue];\n }\n return 0;\n}\n\nexport function scoreRelatorTerm(value, typeOfMaterial = undefined) {\n // NB! We are currently using type of material only for sorting relator terms, not 7XX fields!!!\n const normalizedValue = normalizeValue(value);\n if (typeOfMaterial === 'BK') { // Books\n return scoreRelatorTermBk(normalizedValue);\n }\n if (typeOfMaterial === 'MU') { // Music (NB: audio books should be BK in this context!)\n return scoreRelatorTermMu(normalizedValue);\n }\n if (typeOfMaterial === 'VM') { // video material\n return scoreRelatorTermVm(normalizedValue);\n }\n if (normalizedValue in relatorTermScore) {\n return relatorTermScore[normalizedValue];\n }\n return 0;\n}\n\nexport function fieldOrderComparator(fieldA, fieldB) {\n\n //const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];\n\n const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortBySubfield6, preferFenniKeep, sortByFieldLinkAndSequenceNumber, sortByHacks];\n //const sorterFunctions = [sortByIndexTerms, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];\n\n return globalFieldOrderComparator(fieldA, fieldB, sorterFunctions);\n}\n\nfunction sortByIndexTerms(fieldA, fieldB) {\n\n const indexTermFields = ['600', '610', '611', '630', '648', '650', '651', '652', '653', '654', '655', '656', '657', '658', '659', '662'];\n\n function scoreInd2(val) {\n const ind2Score = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 8, '5': 5, '6': 6, '7': 7};\n\n if (val in ind2Score) {\n return ind2Score[val];\n }\n return 9;\n }\n\n // ATM this is not needed.\n // You may need this, if you change compare function order in sorterFunctions\n // istanbul ignore next\n if (fieldA.tag !== fieldB.tag) {\n return 0;\n }\n\n if (!indexTermFields.includes(fieldA.tag)) {\n return 0;\n }\n\n // Puts ind2=4 last\n if (scoreInd2(fieldA.ind2) > scoreInd2(fieldB.ind2)) {\n return 1;\n }\n if (scoreInd2(fieldA.ind2) < scoreInd2(fieldB.ind2)) {\n return -1;\n }\n\n function scoreDictionary(dictionary) {\n const dictionarySortIndex = {\n 'yso/fin': 0,\n 'yso/swe': 1,\n 'yso/eng': 2,\n 'slm/fin': 0.1,\n 'slm/swe': 1.1,\n 'kauno/fin': 2.1,\n 'kauno/swe': 2.2,\n 'kaunokki': 4,\n 'bella': 5\n };\n\n if (dictionary in dictionarySortIndex) {\n return dictionarySortIndex[dictionary];\n }\n return BIG_BAD_NUMBER;\n }\n\n const dictionaryA = selectFirstValue(fieldA, '2');\n const dictionaryB = selectFirstValue(fieldB, '2');\n\n const dictScoreA = scoreDictionary(dictionaryA);\n const dictScoreB = scoreDictionary(dictionaryB);\n // Use priority order for listed dictionaries:\n if (dictScoreA > dictScoreB) {\n return 1;\n }\n if (dictScoreA < dictScoreB) {\n return -1;\n }\n // Unlisted dictionaries: sort $2 value alphabetically:\n //if (dictScoreA === BIG_BAD_NUMBER) {\n if (dictionaryA > dictionaryB) {\n return 1;\n }\n if (dictionaryA < dictionaryB) {\n return -1;\n }\n //}\n return 0;\n}\n\n\nfunction preferKeep(fieldA, fieldB, keepOwner = 'FENNI') {\n const hasKeepA = fieldHasSubfield(fieldA, '9', `${keepOwner}<KEEP>`);\n const hasKeepB = fieldHasSubfield(fieldB, '9', `${keepOwner}<KEEP>`);\n\n if (hasKeepA && !hasKeepB) {\n return -1;\n }\n if (!hasKeepA && hasKeepB) {\n return 1;\n }\n\n return 0;\n}\n\n\nfunction preferFenniKeep(fieldA, fieldB) {\n const fenniPreference = preferKeep(fieldA, fieldB, 'FENNI');\n if (fenniPreference !== 0) {\n return fenniPreference;\n }\n const violaPreference = preferKeep(fieldA, fieldB, 'VIOLA');\n if (violaPreference !== 0) {\n return violaPreference;\n }\n return preferKeep(fieldA, fieldB, 'FIKKA');\n}\n\nfunction sortByRelatorTerm(fieldA, fieldB) {\n //if (!['600', '610', '611', '630', '700', '710', '711', '730', '800', '810', '811', '830'].includes(fieldA.tag)) {\n if (!['700', '710', '711', '730'].includes(fieldA.tag)) {\n return 0;\n }\n\n function fieldGetMaxRelatorTermScore(field) {\n if (!field.subfields) {\n return -2;\n }\n // If field has $t, it's a teos-nimeke-auktoriteetti, and thus meaningless. These should follow all $t-less fields...\n if (fieldHasSubfield(field, 't')) {\n return -2;\n }\n const relatorSubfieldCode = ['611', '711', '811'].includes(field.tag) ? 'j' : 'e';\n const e = field.subfields.filter(sf => sf.code === relatorSubfieldCode);\n if (e.length === 0) { // No $e is still better than having a $t\n return -1;\n }\n const scores = e.map(sf => scoreRelatorTerm(sf.value));\n //debugDev(`RELATOR SCORE FOR '${fieldToString(field)}': ${scores.join(', ')}`);\n return Math.max(...scores);\n }\n\n const scoreA = fieldGetMaxRelatorTermScore(fieldA);\n const scoreB = fieldGetMaxRelatorTermScore(fieldB);\n\n if (scoreA < scoreB) {\n return 1;\n }\n if (scoreA > scoreB) {\n return -1;\n }\n return 0;\n}\n\n\nfunction fieldGetMinLinkAndSequenceNumber(field) {\n if (!field.subfields) {\n return BIG_BAD_NUMBER;\n }\n const relevantSubfields = field.subfields.filter(sf => isValidSubfield8(sf));\n // If val is something like \"1.2\\x\" parseFloat() would give a syntax erro because of hex-like escape sequnce (at least on Chrome). Thus remove tail:\n const scores = relevantSubfields.map(sf => parseFloat(sf.value.replace(/\\\\.*$/u, '')));\n if (scores.length === 0) {\n return BIG_BAD_NUMBER;\n }\n return Math.min(...scores);\n}\n\nfunction sortByFieldLinkAndSequenceNumber(fieldA, fieldB) { // Sort by subfield $8 that is...\n const scoreA = fieldGetMinLinkAndSequenceNumber(fieldA);\n const scoreB = fieldGetMinLinkAndSequenceNumber(fieldB);\n //debugDev(` sf-8-A-score for '${fieldToString(fieldA)}: ${scoreA}`);\n //debugDev(` sf-8-B-score for '${fieldToString(fieldB)}: ${scoreB}`);\n if (scoreA === scoreB) {\n return 0;\n }\n if (scoreB === 0) {\n return 1;\n }\n if (scoreA === 0) {\n return -1;\n }\n if (scoreA > scoreB) { // smaller is better\n return 1;\n }\n return -1;\n}\n\nfunction sortByHacks(fieldA, fieldB) {\n // Place for various hackier rules\n\n if (fieldA.tag === fieldB.tag) {\n if (fieldA.tag === '041') {\n // If we have multiple f041, we want to put the $2-less first; Values from $2 whatever can not be copied to 008/35-37. (See sync-language.js.)\n const a2 = fieldA.subfields.some(sf => sf.code === '2');\n const b2 = fieldB.subfields.some(sf => sf.code === '2');\n if (a2 && !b2) {\n return 1;\n }\n if (b2 && !a2) {\n return -1;\n }\n }\n\n }\n return 0;\n}\n\nfunction sortBySubfield6(fieldA, fieldB) { // Sort by subfield $6, ex-sortByOccurrenceNumber...\n if (fieldA.tag !== '880' || fieldB.tag !== '880') {\n return 0;\n }\n\n function compareLinkingTags() {\n const tagStringA = fieldGetUnambiguousTag(fieldA);\n const tagStringB = fieldGetUnambiguousTag(fieldB);\n if (tagStringA === tagStringB || !tagStringA || !tagStringB) {\n return 0;\n }\n if (tagStringA > tagStringB) {\n return 1;\n }\n return -1;\n }\n\n function compareOccurrenceNumbers() {\n const stringA = fieldGetUnambiguousOccurrenceNumber(fieldA);\n const stringB = fieldGetUnambiguousOccurrenceNumber(fieldB);\n if (stringA === stringB) { // No action required here\n return 0;\n }\n\n // Handle expections: no occurrence number, occurrence number '00':\n if (!stringB || stringB === '00') {\n if (!stringA || stringA === '00') {\n return 0;\n }\n return -1;\n }\n if (!stringA || stringA === '00') {\n return 1;\n }\n\n // NB! We need compare ints as occurrence number can exceed 99 and be a three-digit value!\n const scoreA = parseInt(stringA, 10);\n const scoreB = parseInt(stringB, 10);\n\n if (scoreA > scoreB) { // smaller is better, thus '00' is the best\n return 1;\n }\n return -1;\n }\n\n const linkingTagComparisonResult = compareLinkingTags();\n if (linkingTagComparisonResult !== 0) {\n return linkingTagComparisonResult;\n }\n\n return compareOccurrenceNumbers();\n}\n\n\nfunction selectFirstValue(field, subcode) {\n return field.subfields\n .filter(subfield => subcode === subfield.code)\n .map(subfield => subfield.value)\n .slice(0, 1);\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,3 @@
1
- import clone from "clone";
2
1
  import { fieldToString } from "./utils.js";
3
2
  import { fieldFixPunctuation } from "./punctuation2.js";
4
3
  import { scoreRelatorTerm } from "./sortFields.js";
@@ -21,7 +20,7 @@ export default function() {
21
20
  const res = { message: [] };
22
21
  const typeOfMaterial = recordToTypeOfMaterial(record);
23
22
  record.fields.forEach((field) => {
24
- const clonedField = clone(field);
23
+ const clonedField = structuredClone(field);
25
24
  sortAdjacentRelatorTerms(clonedField, typeOfMaterial);
26
25
  const clonedFieldAsString = fieldToString(clonedField);
27
26
  const fieldAsString = fieldToString(field);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/sortRelatorTerms.js"],
4
- "sourcesContent": ["// Validator/fixer for sorting $e relator term subfields\n//\n// Author(s): Nicholas Volk\n\nimport clone from 'clone';\n//import createDebugLogger from 'debug';\nimport {fieldToString} from './utils.js';\nimport {fieldFixPunctuation} from './punctuation2.js';\nimport {scoreRelatorTerm} from './sortFields.js';\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortRelatorTerms');\n//const debugData = debug.extend('data');\n\nconst WORST_WORK = 81;\n\n\nexport default function () {\n\n return {\n description: 'Sort adjacent $e subfields in field [1678][01]0',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n const typeOfMaterial = recordToTypeOfMaterial(record);\n\n record.fields.forEach(field => {\n sortAdjacentRelatorTerms(field, typeOfMaterial);\n });\n\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n const typeOfMaterial = recordToTypeOfMaterial(record);\n\n record.fields.forEach(field => {\n const clonedField = clone(field);\n sortAdjacentRelatorTerms(clonedField, typeOfMaterial);\n const clonedFieldAsString = fieldToString(clonedField);\n const fieldAsString = fieldToString(field);\n if (fieldAsString !== clonedFieldAsString) {\n res.message.push(`${fieldAsString} => ${clonedFieldAsString}`);\n }\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n}\n\n\nfunction recordToTypeOfMaterial(record) {\n if (!record.leader) {\n return undefined;\n }\n\n if (record.leader.charAt(6) === 'i') { // Audio books should follow rules of a book, I guess...\n return 'BK';\n }\n\n return record.getTypeOfMaterial();\n}\n\nexport function tagToRelatorTermSubfieldCode(tag) {\n if (['100', '110', '600', '610', '700', '710', '720', '751', '752', '800', '810'].includes(tag)) {\n return 'e';\n }\n if (['111', '611', '711', '811'].includes(tag)) {\n return 'j';\n }\n return '?'; // No need to complain. Nothing is found.\n}\n\nfunction isRelatorTermTag(tag) {\n // NV: 111/711, 751 and 752 are very rare\n return ['e', 'j'].includes(tagToRelatorTermSubfieldCode(tag));\n}\n\nfunction swapRelatorTermSubfields(field, typeOfMaterial = undefined) {\n if (!field.subfields) {\n return;\n }\n\n const subfieldCode = tagToRelatorTermSubfieldCode(field.tag);\n\n //console.log(`Processing ${fieldToString(field)}`); // eslint-disable-line no-console\n\n const swapPosition = field.subfields.findIndex((subfield, index) => isSwappable(subfield, index));\n\n if (swapPosition > 0) {\n swapRelatorTermPair(swapPosition);\n swapRelatorTermSubfields(field, typeOfMaterial); // uh, evil recursion...\n return;\n }\n\n //console.log(`END ${fieldToString(field)}`); // eslint-disable-line no-console\n\n function swapRelatorTermPair(index) {\n //console.log(` SWAP`); // eslint-disable-line no-console\n\n // Swap:\n const tmp = field.subfields[index - 1];\n field.subfields[index - 1] = field.subfields[index];\n field.subfields[index] = tmp;\n fieldFixPunctuation(field);\n return true;\n }\n\n function isSwappable(sf, index) {\n // NB! we should fix 'e' to 'e' or 'j'....\n if (index === 0 || sf.code !== subfieldCode) {\n return false;\n }\n const currScore = scoreRelatorTerm(sf.value, typeOfMaterial);\n if (currScore === 0) {\n return false;\n }\n const prevSubfield = field.subfields[index - 1];\n if (prevSubfield.code !== subfieldCode) {\n return false;\n }\n const prevScore = scoreRelatorTerm(prevSubfield.value, typeOfMaterial);\n // console.log(`PREV: ${prevSubfield.value}/${prevScore}, CURR: ${sf.value}/${currScore}`); // eslint-disable-line no-console\n // If this subfield maps to a Work, then subfields can be swapped, even if we don't have a score for the prev subfield!\n if (prevScore === 0 && currScore < WORST_WORK) {\n return false;\n }\n return currScore > prevScore;\n }\n\n}\n\nexport function sortAdjacentRelatorTerms(field, typeOfMaterial = undefined) {\n if (!field.subfields || !isRelatorTermTag(field.tag)) {\n return field;\n }\n swapRelatorTermSubfields(field, typeOfMaterial);\n\n return field;\n}\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Validator/fixer for sorting $e relator term subfields\n//\n// Author(s): Nicholas Volk\n\n\n//import createDebugLogger from 'debug';\nimport {fieldToString} from './utils.js';\nimport {fieldFixPunctuation} from './punctuation2.js';\nimport {scoreRelatorTerm} from './sortFields.js';\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortRelatorTerms');\n//const debugData = debug.extend('data');\n\nconst WORST_WORK = 81;\n\n\nexport default function () {\n\n return {\n description: 'Sort adjacent $e subfields in field [1678][01]0',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n const typeOfMaterial = recordToTypeOfMaterial(record);\n\n record.fields.forEach(field => {\n sortAdjacentRelatorTerms(field, typeOfMaterial);\n });\n\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n const typeOfMaterial = recordToTypeOfMaterial(record);\n\n record.fields.forEach(field => {\n const clonedField = structuredClone(field);\n sortAdjacentRelatorTerms(clonedField, typeOfMaterial);\n const clonedFieldAsString = fieldToString(clonedField);\n const fieldAsString = fieldToString(field);\n if (fieldAsString !== clonedFieldAsString) {\n res.message.push(`${fieldAsString} => ${clonedFieldAsString}`);\n }\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n}\n\n\nfunction recordToTypeOfMaterial(record) {\n if (!record.leader) {\n return undefined;\n }\n\n if (record.leader.charAt(6) === 'i') { // Audio books should follow rules of a book, I guess...\n return 'BK';\n }\n\n return record.getTypeOfMaterial();\n}\n\nexport function tagToRelatorTermSubfieldCode(tag) {\n if (['100', '110', '600', '610', '700', '710', '720', '751', '752', '800', '810'].includes(tag)) {\n return 'e';\n }\n if (['111', '611', '711', '811'].includes(tag)) {\n return 'j';\n }\n return '?'; // No need to complain. Nothing is found.\n}\n\nfunction isRelatorTermTag(tag) {\n // NV: 111/711, 751 and 752 are very rare\n return ['e', 'j'].includes(tagToRelatorTermSubfieldCode(tag));\n}\n\nfunction swapRelatorTermSubfields(field, typeOfMaterial = undefined) {\n if (!field.subfields) {\n return;\n }\n\n const subfieldCode = tagToRelatorTermSubfieldCode(field.tag);\n\n //console.log(`Processing ${fieldToString(field)}`); // eslint-disable-line no-console\n\n const swapPosition = field.subfields.findIndex((subfield, index) => isSwappable(subfield, index));\n\n if (swapPosition > 0) {\n swapRelatorTermPair(swapPosition);\n swapRelatorTermSubfields(field, typeOfMaterial); // uh, evil recursion...\n return;\n }\n\n //console.log(`END ${fieldToString(field)}`); // eslint-disable-line no-console\n\n function swapRelatorTermPair(index) {\n //console.log(` SWAP`); // eslint-disable-line no-console\n\n // Swap:\n const tmp = field.subfields[index - 1];\n field.subfields[index - 1] = field.subfields[index];\n field.subfields[index] = tmp;\n fieldFixPunctuation(field);\n return true;\n }\n\n function isSwappable(sf, index) {\n // NB! we should fix 'e' to 'e' or 'j'....\n if (index === 0 || sf.code !== subfieldCode) {\n return false;\n }\n const currScore = scoreRelatorTerm(sf.value, typeOfMaterial);\n if (currScore === 0) {\n return false;\n }\n const prevSubfield = field.subfields[index - 1];\n if (prevSubfield.code !== subfieldCode) {\n return false;\n }\n const prevScore = scoreRelatorTerm(prevSubfield.value, typeOfMaterial);\n // console.log(`PREV: ${prevSubfield.value}/${prevScore}, CURR: ${sf.value}/${currScore}`); // eslint-disable-line no-console\n // If this subfield maps to a Work, then subfields can be swapped, even if we don't have a score for the prev subfield!\n if (prevScore === 0 && currScore < WORST_WORK) {\n return false;\n }\n return currScore > prevScore;\n }\n\n}\n\nexport function sortAdjacentRelatorTerms(field, typeOfMaterial = undefined) {\n if (!field.subfields || !isRelatorTermTag(field.tag)) {\n return field;\n }\n swapRelatorTermSubfields(field, typeOfMaterial);\n\n return field;\n}\n\n"],
5
+ "mappings": "AAMA,SAAQ,qBAAoB;AAC5B,SAAQ,2BAA0B;AAClC,SAAQ,wBAAuB;AAI/B,MAAM,aAAa;AAGnB,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAC9C,UAAM,iBAAiB,uBAAuB,MAAM;AAEpD,WAAO,OAAO,QAAQ,WAAS;AAC7B,+BAAyB,OAAO,cAAc;AAAA,IAChD,CAAC;AAED,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,MAAM,EAAC,SAAS,CAAC,EAAC;AAExB,UAAM,iBAAiB,uBAAuB,MAAM;AAEpD,WAAO,OAAO,QAAQ,WAAS;AAC7B,YAAM,cAAc,gBAAgB,KAAK;AACzC,+BAAyB,aAAa,cAAc;AACpD,YAAM,sBAAsB,cAAc,WAAW;AACrD,YAAM,gBAAgB,cAAc,KAAK;AACzC,UAAI,kBAAkB,qBAAqB;AACzC,YAAI,QAAQ,KAAK,GAAG,aAAa,OAAO,mBAAmB,EAAE;AAAA,MAC/D;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,EAAE,IAAI,QAAQ,UAAU;AACpC,WAAO;AAAA,EACT;AACF;AAGA,SAAS,uBAAuB,QAAQ;AACtC,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,OAAO,OAAO,CAAC,MAAM,KAAK;AACnC,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,kBAAkB;AAClC;AAEO,gBAAS,6BAA6B,KAAK;AAChD,MAAI,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AAC/F,WAAO;AAAA,EACT;AACA,MAAI,CAAC,OAAO,OAAO,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,KAAK;AAE7B,SAAO,CAAC,KAAK,GAAG,EAAE,SAAS,6BAA6B,GAAG,CAAC;AAC9D;AAEA,SAAS,yBAAyB,OAAO,iBAAiB,QAAW;AACnE,MAAI,CAAC,MAAM,WAAW;AACpB;AAAA,EACF;AAEA,QAAM,eAAe,6BAA6B,MAAM,GAAG;AAI3D,QAAM,eAAe,MAAM,UAAU,UAAU,CAAC,UAAU,UAAU,YAAY,UAAU,KAAK,CAAC;AAEhG,MAAI,eAAe,GAAG;AACpB,wBAAoB,YAAY;AAChC,6BAAyB,OAAO,cAAc;AAC9C;AAAA,EACF;AAIA,WAAS,oBAAoB,OAAO;AAIlC,UAAM,MAAM,MAAM,UAAU,QAAQ,CAAC;AACrC,UAAM,UAAU,QAAQ,CAAC,IAAI,MAAM,UAAU,KAAK;AAClD,UAAM,UAAU,KAAK,IAAI;AACzB,wBAAoB,KAAK;AACzB,WAAO;AAAA,EACT;AAEA,WAAS,YAAY,IAAI,OAAO;AAE9B,QAAI,UAAU,KAAK,GAAG,SAAS,cAAc;AAC3C,aAAO;AAAA,IACT;AACA,UAAM,YAAY,iBAAiB,GAAG,OAAO,cAAc;AAC3D,QAAI,cAAc,GAAG;AACnB,aAAO;AAAA,IACT;AACA,UAAM,eAAe,MAAM,UAAU,QAAQ,CAAC;AAC9C,QAAI,aAAa,SAAS,cAAc;AACtC,aAAO;AAAA,IACT;AACA,UAAM,YAAY,iBAAiB,aAAa,OAAO,cAAc;AAGrE,QAAI,cAAc,KAAK,YAAY,YAAY;AAC7C,aAAO;AAAA,IACT;AACA,WAAO,YAAY;AAAA,EACrB;AAEF;AAEO,gBAAS,yBAAyB,OAAO,iBAAiB,QAAW;AAC1E,MAAI,CAAC,MAAM,aAAa,CAAC,iBAAiB,MAAM,GAAG,GAAG;AACpD,WAAO;AAAA,EACT;AACA,2BAAyB,OAAO,cAAc;AAE9C,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,3 @@
1
- import clone from "clone";
2
1
  import createDebugLogger from "debug";
3
2
  import { fieldToString, nvdebug } from "./utils.js";
4
3
  const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:sortSubfields");
@@ -33,7 +32,7 @@ export default function(defaultTagPattern) {
33
32
  const res = { message: [] };
34
33
  const relevantFields = getRelevantFields(record, tagPattern);
35
34
  relevantFields.forEach((field) => {
36
- const clonedField = clone(field);
35
+ const clonedField = structuredClone(field);
37
36
  sortAdjacentSubfields(clonedField);
38
37
  const clonedFieldAsString = fieldToString(clonedField);
39
38
  const fieldAsString = fieldToString(field);