UNPKG

20.6 kBJavaScriptView Raw
1var field = require('./field');
2var MarcVariableField = field.MarcVariableField;
3var MarcControlField = field.MarcControlField;
4var MarcDataField = field.MarcDataField;
5var MarcSubfield = field.MarcSubfield;
6
7/*
8 * Represents a MARC record.
9 */
10function MarcRecord(record) {
11 if (!(this instanceof MarcRecord)) {
12 return new MarcRecord(record);
13 }
14
15 if (record instanceof MarcRecord) {
16 this.leader = record.leader;
17 this.fields = [];
18
19 for (var fieldNo = 0; fieldNo < record.fields.length; fieldNo++) {
20 var field = record.fields[fieldNo];
21 if (field instanceof MarcControlField) {
22 this.addVariableField(new MarcControlField(field));
23 } else {
24 this.addVariableField(new MarcDataField(field));
25 }
26 }
27 } else {
28 this.leader = MarcRecord.DEFAULT_LEADER;
29 this.fields = record instanceof Array ? record : [];
30 }
31}
32
33/*
34 * Constants.
35 */
36MarcRecord.DEFAULT_LEADER = ' nam 22 450 ';
37
38/*
39 * Returns new record created from the source format.
40 */
41MarcRecord.parse = function(srcRecord) {
42 if (srcRecord instanceof Object) {
43 return MarcRecord.parseJson(srcRecord);
44 }
45 if (typeof(srcRecord) === 'string') {
46 if (/^[ \t\r\n]*\{/.test(srcRecord)) {
47 return MarcRecord.parseJson(JSON.parse(srcRecord));
48 }
49 return MarcRecord.parseText(srcRecord);
50 }
51 throw new Error('invalid record');
52}
53
54/*
55 * Returns new record created from the JSON object.
56 */
57MarcRecord.parseJson = function(jsonRecord) {
58 if (!(jsonRecord instanceof Object)
59 || !(jsonRecord.fields instanceof Array))
60 {
61 throw new Error('invalid record');
62 }
63
64 // Create the new record.
65 var record = new MarcRecord();
66 record.leader =
67 typeof(jsonRecord.leader) === 'string' ? jsonRecord.leader : null;
68 for (var i = 0; i < jsonRecord.fields.length; i++) {
69 record.fields.push(MarcVariableField.parseJson(jsonRecord.fields[i]));
70 }
71 return record;
72}
73
74/*
75 * Returns new record created from the text string.
76 */
77MarcRecord.parseText = function(textRecord) {
78 if (typeof(textRecord) !== 'string') {
79 throw new Error('invalid record');
80 }
81
82 // Create the new record.
83 var record = new MarcRecord();
84
85 var textFields = textRecord.split('\n');
86 for (var fieldNo = 0; fieldNo < textFields.length; fieldNo++) {
87 var textField = textFields[fieldNo];
88 if (textField.indexOf('000 ') === 0) {
89 record.leader = textField.slice(4);
90 } else if (textField !== '') {
91 record.fields.push(MarcVariableField.parseText(textField));
92 }
93 }
94 return record;
95}
96
97/*
98 * Returns copy of the specified record.
99 */
100MarcRecord.clone = function(record) {
101 if (record instanceof MarcRecord) {
102 return new MarcRecord(record);
103 }
104 return null;
105}
106
107/*
108 * Returns copy of the record.
109 */
110MarcRecord.prototype.clone = function() {
111 return MarcRecord.clone(this);
112}
113
114/*
115 * Replaces content of the record.
116 */
117MarcRecord.prototype.assign = function(record) {
118 this.leader = record.leader;
119 this.fields = record.fields;
120}
121
122/*
123 * Returns true if the records are equal.
124 */
125MarcRecord.equals = function(record1, record2, opts) {
126 if (opts === true) {
127 // For compatibility with the old argument "weakMode".
128 opts = {ignoreOrder: true};
129 }
130 opts = opts || {};
131
132 if (record1 instanceof MarcRecord && record2 instanceof MarcRecord) {
133 if (record1.leader.slice(5, 12) !== record2.leader.slice(5, 12)
134 || record1.leader.slice(17) !== record2.leader.slice(17))
135 {
136 return false;
137 }
138 }
139
140 if (record1 instanceof MarcRecord) {
141 var fields1 = record1.fields;
142 } else if (record1 instanceof Array) {
143 var fields1 = record1;
144 } else {
145 return false;
146 }
147
148 if (record2 instanceof MarcRecord) {
149 var fields2 = !opts.ignoreOrder ? record2.fields : record2.fields.slice();
150 } else if (record2 instanceof Array) {
151 var fields2 = !opts.ignoreOrder ? record2 : record2.slice();
152 } else {
153 return false;
154 }
155
156 if (fields1.length !== fields2.length) {
157 return false;
158 }
159
160 if (!opts.ignoreOrder) {
161 for (var i = 0; i < fields1.length; i++) {
162 if (!fields1[i].equals(fields2[i], opts)) {
163 return false;
164 }
165 }
166 } else {
167 for (var i = 0; i < fields1.length; i++) {
168 for (var j = 0; j < fields2.length; j++) {
169 if (fields1[i].equals(fields2[j], opts)) {
170 break;
171 }
172 }
173 if (j === fields2.length) {
174 return false;
175 }
176 fields2.splice(j, 1);
177 }
178 }
179
180 return true;
181}
182
183/*
184 * Returns true if the records are equal.
185 */
186MarcRecord.prototype.equals = function(record, opts) {
187 return MarcRecord.equals(this, record, opts || {});
188}
189
190/*
191 * Returns difference between two records.
192 */
193MarcRecord.diff = function(record1, record2, opts) {
194 opts = opts || {};
195
196 if (record1 instanceof MarcRecord && record2 instanceof MarcRecord) {
197 if (record1.leader.slice(5, 12) !== record2.leader.slice(5, 12)
198 || record1.leader.slice(17) !== record2.leader.slice(17))
199 {
200 return "leaders is not equal: ["
201 + record1.leader + "] [" + record2.leader + "]";
202 }
203 }
204
205 if (record1 instanceof MarcRecord) {
206 var fields1 = record1.fields;
207 } else if (record1 instanceof Array) {
208 var fields1 = record1;
209 } else {
210 return "record 1 is not MarcRecord";
211 }
212
213 if (record2 instanceof MarcRecord) {
214 var fields2 = !opts.ignoreOrder ? record2.fields : record2.fields.slice();
215 } else if (record2 instanceof Array) {
216 var fields2 = !opts.ignoreOrder ? record2 : record2.slice();
217 } else {
218 return "record 2 is not MarcRecord";
219 }
220
221 if (fields1.length !== fields2.length) {
222 return "records have different number of fields: "
223 + fields1.length + " " + fields2.length;
224 }
225
226 if (!opts.ignoreOrder) {
227 for (var i = 0; i < fields1.length; i++) {
228 if (!fields1[i].equals(fields2[i], opts)) {
229 return "Field [" + fields1[i].toString() + "] not found in record2";
230 }
231 }
232 } else {
233 for (var i = 0; i < fields1.length; i++) {
234 for (var j = 0; j < fields2.length; j++) {
235 if (fields1[i].equals(fields2[j], opts)) {
236 break;
237 }
238 }
239 if (j === fields2.length) {
240 return "Field [" + fields1[i].toString() + "] not found in record2";
241 }
242 fields2.splice(j, 1);
243 }
244 }
245
246 return null;
247}
248
249/*
250 * Returns difference between two records.
251 */
252MarcRecord.prototype.diff = function(record, opts) {
253 return MarcRecord.diff(this, record, opts || {});
254}
255
256/*
257 * Returns number of fields in the record.
258 */
259MarcRecord.prototype.size = function() {
260 return this.fields.length;
261}
262
263/*
264 * Returns true if the record does not contains fields.
265 */
266MarcRecord.prototype.empty = function() {
267 return (this.fields.length === 0);
268}
269
270/*
271 * Clears all data in the record.
272 */
273MarcRecord.prototype.clear = function() {
274 this.leader = MarcRecord.DEFAULT_LEADER;
275 this.fields.length = 0;
276}
277
278/*
279 * Removes fields and subfields not containing actual data.
280 */
281MarcRecord.prototype.trim = function() {
282 for (var fieldNo = this.fields.length - 1; fieldNo >= 0; fieldNo--) {
283 var field = this.fields[fieldNo];
284 if (field instanceof MarcDataField) {
285 field.trim();
286 }
287 if (field.empty()) {
288 this.fields.splice(fieldNo, 1);
289 }
290 }
291}
292
293/*
294 * Returns the position of the variable field in the record.
295 */
296MarcRecord.prototype.getVariableFieldIndex = function(variableField) {
297 return this.fields.indexOf(variableField);
298}
299
300/*
301 * Adds a variable field.
302 */
303MarcRecord.prototype.addVariableField = function(variableField) {
304 this.fields.push(variableField);
305}
306
307/*
308 * Adds a variable field when it is not empty.
309 */
310MarcRecord.prototype.addNonEmptyVariableField = function(variableField) {
311 if (!variableField.empty()) {
312 this.fields.push(variableField);
313 }
314}
315
316/*
317 * Inserts a variable field at the specified position.
318 */
319MarcRecord.prototype.insertVariableField = function(index, variableField) {
320 if (index < 0 || index > this.fields.length) {
321 throw new Error('invalid position specified');
322 }
323 this.fields.splice(index, 0, variableField);
324}
325
326/*
327 * Removes a list of variable fields.
328 */
329MarcRecord.prototype.removeVariableFields = function(variableFields) {
330 for (var i = 0; i < variableFields.length; i++) {
331 this.removeVariableField(variableFields[i]);
332 }
333}
334
335/*
336 * Removes a variable field.
337 */
338MarcRecord.prototype.removeVariableField = function(variableField) {
339 if (!(variableField instanceof MarcVariableField)) {
340 var index = Number(variableField);
341 if (isNaN(index) || index < 0 || index >= this.fields.length) {
342 throw new Error('invalid field specified: '
343 + JSON.stringify(variableField));
344 }
345 this.fields.splice(index, 1);
346 return;
347 }
348
349 for (;;) {
350 var index = this.fields.indexOf(variableField);
351 if (index < 0) {
352 break;
353 }
354 this.fields.splice(index, 1);
355 }
356}
357
358/*
359 * Adds or replaces a subfield.
360 */
361MarcRecord.prototype.setSubfield = function(tag, ind1, ind2, subfield, opts) {
362 var field = this.getDataField(tag, ind1, ind2, opts);
363 if (field === null) {
364 throw new Error('field "' + tag + '" not found');
365 }
366 field.setSubfield(subfield, opts);
367}
368
369/*
370 * Returns a list of variable fields.
371 */
372MarcRecord.prototype.getVariableFields = function(tags) {
373 if (!tags) {
374 return this.fields.slice();
375 }
376
377 var tagList = tags instanceof Array ? tags : [tags];
378 var fields = [];
379 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
380 var field = this.fields[fieldNo];
381 for (var i in tagList) {
382 var tag = tagList[i];
383 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
384 fields.push(field);
385 break;
386 }
387 }
388 }
389 return fields;
390}
391
392/*
393 * Returns a variable field.
394 */
395MarcRecord.prototype.getVariableField = function(tags) {
396 if (!tags) {
397 return this.fields.length > 0 ? this.fields[0] : null;
398 }
399
400 var tagList = tags instanceof Array ? tags : [tags];
401 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
402 var field = this.fields[fieldNo];
403 for (var i in tagList) {
404 var tag = tagList[i];
405 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
406 return field;
407 }
408 }
409 }
410 return null;
411}
412
413/*
414 * Returns a list of control fields.
415 */
416MarcRecord.prototype.getControlFields = function(tags) {
417 var tagList = !tags ? null : (tags instanceof Array ? tags : [tags]);
418 var fields = [];
419 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
420 var field = this.fields[fieldNo];
421 if (!(field instanceof MarcControlField)) {
422 continue;
423 }
424 if (!tagList) {
425 fields.push(field);
426 continue;
427 }
428 for (var i in tagList) {
429 var tag = tagList[i];
430 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
431 fields.push(field);
432 break;
433 }
434 }
435 }
436 return fields;
437}
438
439/*
440 * Returns a control field.
441 */
442MarcRecord.prototype.getControlField = function(tags) {
443 var fields = this.getControlFields(tags);
444 return fields.length > 0 ? fields[0] : null;
445}
446
447/*
448 * Returns a list of data fields.
449 */
450MarcRecord.prototype.getDataFields = function(tags, ind1, ind2, opts) {
451 opts = opts || {};
452 var tagList = !tags ? null : (tags instanceof Array ? tags : [tags]);
453 var fields = [];
454 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
455 var field = this.fields[fieldNo];
456 if (!(field instanceof MarcDataField)) {
457 continue;
458 }
459 if (opts.normalizeIndicators) {
460 if (ind1 && ind1.replace('#', ' ') !== field.ind1.replace('#', ' ')
461 || ind2 && ind2.replace('#', ' ') !== field.ind2.replace('#', ' '))
462 {
463 continue;
464 }
465 } else {
466 if (ind1 && ind1 !== field.ind1 || ind2 && ind2 !== field.ind2) {
467 continue;
468 }
469 }
470 if (!tagList) {
471 fields.push(field);
472 continue;
473 }
474 for (var i in tagList) {
475 var tag = tagList[i];
476 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
477 fields.push(field);
478 break;
479 }
480 }
481 }
482 return fields;
483}
484
485/*
486 * Returns a data field.
487 */
488MarcRecord.prototype.getDataField = function(tags, ind1, ind2, opts) {
489 var fields = this.getDataFields(tags, ind1, ind2, opts);
490 return fields.length > 0 ? fields[0] : null;
491}
492
493/*
494 * Returns data of the first control field, found by field tag.
495 */
496MarcRecord.prototype.getControlFieldData = function(tags) {
497 var field = this.getVariableField(tags);
498 if (!(field && field instanceof MarcControlField)) {
499 return null;
500 }
501 return field.data;
502}
503
504/*
505 * Returns the control number field or null if no control.
506 */
507MarcRecord.prototype.getControlNumberField = function() {
508 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
509 var field = this.fields[fieldNo];
510 if (field instanceof MarcControlField && field.tag === '001') {
511 return field;
512 }
513 }
514 return null;
515}
516
517/*
518 * Returns the control number or null if no control number is available.
519 */
520MarcRecord.prototype.getControlNumber = function() {
521 var field = this.getControlNumberField();
522 return field ? field.data : null;
523}
524
525/*
526 * Returns first subfield, found by subfield codes in the first
527 * specified field.
528 */
529MarcRecord.prototype.getSubfield = function(tags, codes) {
530 if (!tags) {
531 throw new Error('tags must be specified');
532 }
533
534 var tagList = tags instanceof Array ? tags : [tags];
535 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
536 var field = this.fields[fieldNo];
537 for (var i in tagList) {
538 var tag = tagList[i];
539 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
540 return field.getSubfield(codes);
541 }
542 }
543 }
544 return null;
545}
546
547/*
548 * Returns data of the first subfield, found by subfield codes in the first
549 * specified field.
550 */
551MarcRecord.prototype.getSubfieldData = function(tags, codes) {
552 if (!tags) {
553 throw new Error('tags must be specified');
554 }
555
556 var tagList = tags instanceof Array ? tags : [tags];
557 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
558 var field = this.fields[fieldNo];
559 for (var i in tagList) {
560 var tag = tagList[i];
561 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
562 return field.getSubfieldData(codes);
563 }
564 }
565 }
566 return null;
567}
568
569/*
570 * Returns first regular subfield, found by subfield codes and data pattern
571 * in the first specified field.
572 */
573MarcRecord.prototype.getRegularSubfield = function(tags, codes, pattern) {
574 if (!tags) {
575 throw new Error('tags must be specified');
576 }
577
578 var tagList = tags instanceof Array ? tags : [tags];
579 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
580 var field = this.fields[fieldNo];
581 for (var i in tagList) {
582 var tag = tagList[i];
583 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
584 return field.getRegularSubfield(codes, pattern);
585 }
586 }
587 }
588 return null;
589}
590
591/*
592 * Returns data of first regular subfield, found by subfield codes
593 * and data pattern in the first specified field.
594 */
595MarcRecord.prototype.getRegularSubfieldData = function(tags, codes, pattern) {
596 if (!tags) {
597 throw new Error('tags must be specified');
598 }
599
600 var tagList = tags instanceof Array ? tags : [tags];
601 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
602 var field = this.fields[fieldNo];
603 for (var i in tagList) {
604 var tag = tagList[i];
605 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
606 return field.getRegularSubfieldData(codes, pattern);
607 }
608 }
609 }
610 return null;
611}
612
613/*
614 * Returns control fields, found by field tags and data pattern.
615 */
616MarcRecord.prototype.findControlFields = function(tags, pattern) {
617 if (!tags) {
618 throw new Error('tags must be specified');
619 }
620
621 var tagList = tags instanceof Array ? tags : [tags];
622 var fields = [];
623 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
624 var field = this.fields[fieldNo];
625 if (!(field instanceof MarcControlField)) {
626 continue;
627 }
628
629 for (var i in tagList) {
630 var tag = tagList[i];
631 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
632 if (!pattern
633 || (pattern instanceof RegExp && pattern.test(field.data))
634 || (field.data === pattern))
635 {
636 fields.push(field);
637 }
638 }
639 }
640 }
641 return fields;
642}
643
644/*
645 * Returns data fields, found by field tags, subfield codes and data pattern.
646 */
647MarcRecord.prototype.findDataFields = function(tags, ind1, ind2, codes, pattern, opts) {
648 if (!tags) {
649 throw new Error('tags must be specified');
650 }
651
652 opts = opts || {};
653 var tagList = tags instanceof Array ? tags : [tags];
654 var fields = [];
655 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
656 var field = this.fields[fieldNo];
657 if (!(field instanceof MarcDataField)) {
658 continue;
659 }
660 if (opts.normalizeIndicators) {
661 if (ind1 && ind1.replace('#', ' ') !== field.ind1.replace('#', ' ')
662 || ind2 && ind2.replace('#', ' ') !== field.ind2.replace('#', ' '))
663 {
664 continue;
665 }
666 } else {
667 if (ind1 && ind1 !== field.ind1 || ind2 && ind2 !== field.ind2) {
668 continue;
669 }
670 }
671
672 for (var i in tagList) {
673 var tag = tagList[i];
674 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
675 if (field.getRegularSubfield(codes, pattern)) {
676 fields.push(field);
677 }
678 }
679 }
680 }
681 return fields;
682}
683
684/*
685 * Returns subfields, found by field tags, subfield codes and data pattern.
686 */
687MarcRecord.prototype.findSubfields = function(tags, ind1, ind2, codes, pattern, opts) {
688 if (!tags) {
689 throw new Error('tags must be specified');
690 }
691
692 opts = opts || {};
693 var tagList = tags instanceof Array ? tags : [tags];
694 var subfields = [];
695 for (var fieldNo = 0; fieldNo < this.fields.length; fieldNo++) {
696 var field = this.fields[fieldNo];
697 if (!(field instanceof MarcDataField)) {
698 continue;
699 }
700 if (opts.normalizeIndicators) {
701 if (ind1 && ind1.replace('#', ' ') !== field.ind1.replace('#', ' ')
702 || ind2 && ind2.replace('#', ' ') !== field.ind2.replace('#', ' '))
703 {
704 continue;
705 }
706 } else {
707 if (ind1 && ind1 !== field.ind1 || ind2 && ind2 !== field.ind2) {
708 continue;
709 }
710 }
711
712 for (var i in tagList) {
713 var tag = tagList[i];
714 if (tag instanceof RegExp && tag.test(field.tag) || tag === field.tag) {
715 var foundSubfields = field.getRegularSubfields(codes, pattern);
716 if (foundSubfields.length > 0) {
717 Array.prototype.push.apply(subfields, foundSubfields);
718 }
719 }
720 }
721 }
722 return subfields;
723}
724
725/*
726 * Returns the Leader.
727 */
728MarcRecord.prototype.getLeader = function() {
729 return this.leader;
730}
731
732/*
733 * Sets the Leader.
734 */
735MarcRecord.prototype.setLeader = function(leader) {
736 this.leader = leader;
737}
738
739/*
740 * Reorders fields according to its tags or callback function.
741 */
742MarcRecord.prototype.sort = function(callback) {
743 if (callback && callback instanceof Function) {
744 this.fields.sort(callback);
745 return;
746 }
747
748 var indexes = {};
749 for (var i = 0; i < this.fields.length; i++) {
750 indexes[this.fields[i]] = i;
751 }
752
753 this.fields.sort(function(a, b) {
754 // We don't want to reorder fields with the same tags.
755 if (a.tag === b.tag) {
756 return indexes[a] - indexes[b];
757 }
758 return a.tag < b.tag ? -1 : 1;
759 });
760}
761
762/*
763 * Calls a callback for each element of the record.
764 */
765MarcRecord.prototype.walk = function(callback) {
766 for (var fieldNo in this.fields) {
767 var field = this.fields[fieldNo];
768 callback(field, this);
769 if (field instanceof MarcDataField) {
770 field.walk(callback);
771 }
772 }
773}
774
775/*
776 * Returns a string representation of this record.
777 */
778MarcRecord.prototype.toString = function() {
779 var textRecord = '000 ' + this.leader;
780 for (var fieldNo in this.fields) {
781 textRecord += '\n' + this.fields[fieldNo].toString();
782 }
783 return textRecord;
784}
785
786/*
787 * Returns an array of embedded fields, copied from specified fields.
788 */
789MarcRecord.toEmbeddedFields = function(fields) {
790 var subfields = [];
791 for (var fieldNo = 0; fieldNo < fields.length; fieldNo++) {
792 var field = fields[fieldNo];
793 subfields.push(MarcSubfield('1', field.clone()));
794 }
795 return subfields;
796}
797
798/*
799 * Returns an array of embedded fields, copied from fields of this record.
800 */
801MarcRecord.prototype.toEmbeddedFields = function() {
802 return MarcRecord.toEmbeddedFields(this.fields);
803}
804
805module.exports = {
806 MarcRecord: MarcRecord
807};