LP1841823 Marc flat editor repair slashes (Angular)
[working/Evergreen.git] / Open-ILS / src / eg2 / src / assets / js / marcrecord.js
1 /* ---------------------------------------------------------------------------
2  * Copyright (C) 2009-2015  Equinox Software, Inc.
3  * Mike Rylander <miker@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * ---------------------------------------------------------------------------
15  */
16
17 /*
18  * Copy of file from Open-ILS/web/js/ui/default/staff/marcrecord.js
19  *
20  * This copy of the the MARC21 library heavily modified by
21  * Bill Erickson <berickxx@gmail.com> 2019 circa Evergreen 3.3.
22  *
23  * 1. All jquery dependencies have been replaced with Vanilla JS.
24  * 2. Many features from the original have been removed (for now,
25  *    anyway) since they were not needed at the time and would have
26  *    required additional jquery porting work.
27  * 
28  * Code is otherwise unchanged.
29  */
30
31 /**
32  * As an external dependency, this JS is loaded on every Angular page.
33  * We could migrate it into the Angular app as Typescript so it's only
34  * loaded when needed (e.g. in the marc editor component).
35  */
36
37 var MARC21 = {
38
39     Record : function(kwargs) {
40         if (!kwargs) kwargs = {};
41
42         this.generate008 = function () {
43             var f;
44             var s;
45             var orig008 = '                                        ';
46             var now = new Date();
47             var y = now.getUTCFullYear().toString().substr(2,2);
48             var m = now.getUTCMonth() + 1;
49             if (m < 10) m = '0' + m;
50             var d = now.getUTCDate();
51             if (d < 10) d = '0' + d;
52
53
54             if (f = this.field('008',true)[0]) {
55                 orig008 = f.data;
56             }
57         
58             /* lang code from 041a */
59             var lang = orig008.substr(35, 3);
60             
61             if (f = this.field('041',true)[0]) {
62                 if (s = f.subfield('a',true)[0]) {
63                     if(s[1]) lang = s[1];
64                 }
65             }
66         
67             /* country code from 044a */
68             var country = orig008.substr(15, 3);
69             if (f = this.field('044',true)[0]) {
70                 if (s = f.subfield('a',true)[0]) {
71                     if(s[1]) country = s[1];
72                 }
73             }
74             while (country.length < 3) country = country + ' ';
75             if (country.length > 3) country = country.substr(0,3);
76         
77             /* date1 from 260c */
78             var date1 = now.getUTCFullYear().toString();
79             if (f = this.field('260',true)[0]) {
80                 if (s = f.subfield('c',true)[0]) {
81                     if (s[1]) {
82                         var tmpd = s[1];
83                         tmpd = tmpd.replace(/[^0-9]/g, '');
84                         if (tmpd.match(/^\d\d\d\d/)) {
85                             date1 = tmpd.substr(0, 4);
86                         }
87                     }
88                 }
89             }
90         
91             var date2 = orig008.substr(11, 4);
92             var datetype = orig008.substr(6, 1);
93             var modded = orig008.substr(38, 1);
94             var catsrc = orig008.substr(39, 1);
95         
96             return '' + y + m + d + datetype + date1 + date2 + country + '                 ' + lang + modded + catsrc;
97         
98         }
99
100         this.title = function () { return this.subfield('245','a')[1] }
101
102         this.field = function (spec, wantarray) {
103             var list = this.fields.filter(function (f) {
104                 if (f.tag.match(spec)) return true;
105                 return false;
106             });
107
108             if (!wantarray && list.length == 1) return list[0];
109             return list;
110         }
111
112         this.subfield = function (spec, code) {
113             var f = this.field(spec, true)[0];
114             if (f) return f.subfield(code)
115             return null;
116         }
117
118         this.appendFields = function () {
119             var me = this;
120             Array.prototype.slice.call(arguments).forEach( function (f) { f.position = me.fields.length; me.fields.push( f ) } );
121         }
122
123         this.deleteField = function (f) { return this.deleteFields(f) },
124
125         this.insertOrderedFields = function () {
126             var me = this;
127             for (var i = 0; i < arguments.length; i++) {
128                 var f = arguments[i];
129                 var done = false;
130                 for (var j = 0; j < this.fields.length; j++) {
131                     if (f.tag < this.fields[j].tag) {
132                         this.insertFieldsBefore(this.fields[j], f);
133                         done = true;
134                         break;
135                     }
136                 }
137                 if (!done) this.appendFields(f);
138             }
139         }
140
141         this.insertFieldsBefore = function (target) {
142             var args = Array.prototype.slice.call(arguments);
143             args.splice(0,1);
144             var me = this;
145             for (var j = 0; j < this.fields.length; j++) {
146                 if (target === this.fields[j]) {
147                     args.forEach( function (f) {
148                         f.record = me;
149                         me.fields.splice(j++,0,f);
150                     });
151                     break;
152                 }
153             }
154             for (var j = 0; j < this.fields.length; j++) {
155                 this.fields[j].position = j;
156             }
157         }
158
159         this.insertFieldsAfter = function (target) {
160             var args = Array.prototype.slice.call(arguments);
161             args.splice(0,1);
162             var me = this;
163             for (var j = 0; j < this.fields.length; j++) {
164                 if (target === this.fields[j]) {
165                     args.forEach( function (f) {
166                         f.record = me;
167                         me.fields.splice(++j,0,f);
168                     });
169                     break;
170                 }
171             }
172             for (var j = 0; j < this.fields.length; j++) {
173                 this.fields[j].position = j;
174             }
175         }
176
177         this.deleteFields = function () {
178             var me = this;
179             var counter = 0;
180             for ( var i in arguments ) {
181                 var f = arguments[i];
182                 for (var j = 0; j < me.fields.length; j++) {
183                     if (f === me.fields[j]) {
184                         me.fields[j].record = null;
185                         me.fields.splice(j,1);
186                         counter++
187                         break;
188                     }
189                 }
190             }
191             for (var j = 0; j < this.fields.length; j++) {
192                 this.fields[j].position = j;
193             }
194             return counter;
195         }
196
197         this.fromXmlString = function (mxml) {
198             var xmlDoc = new DOMParser().parseFromString(mxml, "text/xml");
199             this.fromXmlDocument(xmlDoc.getElementsByTagName('record')[0]);
200         }
201
202         this.fromXmlDocument = function (mxml) {
203             var me = this;
204             var ldr =  mxml.getElementsByTagName('leader')[0];
205             me.leader = (ldr ? ldr.textContent : '') || '00000cam a2200205Ka 4500';
206
207             var cfNodes = mxml.getElementsByTagName('controlfield');
208             for (var idx = 0; idx < cfNodes.length; idx++) {
209                 var cf = cfNodes[idx];
210                 me.fields.push(
211                     new MARC21.Field({
212                           record : me,
213                           tag    : cf.getAttribute('tag'),
214                           data   : cf.textContent
215                     })
216                 );
217             }
218
219             var dfNodes = mxml.getElementsByTagName('datafield');
220             for (var idx = 0; idx < dfNodes.length; idx++) {
221                 var df = dfNodes[idx];
222
223                 var sfNodes = df.getElementsByTagName('subfield');
224                 var subfields = [];
225                 for (var idx2 = 0; idx2 < sfNodes.length; idx2++) {
226                     var sf = sfNodes[idx2];
227                     subfields.push(
228                         [sf.getAttribute('code'), sf.textContent, idx2]);
229                 }
230
231                 me.fields.push(
232                     new MARC21.Field({
233                         record    : me,
234                         tag       : df.getAttribute('tag'),
235                         ind1      : df.getAttribute('ind1'),
236                         ind2      : df.getAttribute('ind2'),
237                         subfields : subfields
238                     })
239                 );
240             }
241
242             for (var j = 0; j < this.fields.length; j++) {
243                 this.fields[j].position = j;
244             }
245
246             me.ready = true;
247         }
248
249         this.toXmlDocument = function () {
250
251             var doc = new DOMParser().parseFromString(
252                 '<record xmlns="http://www.loc.gov/MARC21/slim"/>', 'text/xml');
253
254             var rec_node = doc.getElementsByTagName('record')[0];
255
256             var ldr = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'leader');
257             ldr.textContent = this.leader;
258             rec_node.appendChild( ldr );
259
260             this.fields.forEach(function (f) {
261                 var element = f.isControlfield() ? 'controlfield' : 'datafield';
262                 var f_node = doc.createElementNS( 'http://www.loc.gov/MARC21/slim', element );
263                 f_node.setAttribute('tag', f.tag);
264                 
265                 if (f.isControlfield()) {
266                     if (f.data) f_node.textContent = f.data;
267                 } else {
268                     f_node.setAttribute('ind1', f.indicator(1));
269                     f_node.setAttribute('ind2', f.indicator(2));
270                     f.subfields.forEach( function (sf) {
271                         var sf_node = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'subfield');
272                         sf_node.setAttribute('code', sf[0]);
273                         sf_node.textContent = sf[1];
274                         f_node.appendChild(sf_node);
275                     });
276                 }
277
278                 rec_node.appendChild(f_node);
279             });
280
281             return doc;
282         }
283
284         this.toXmlString = function () {
285             return (new XMLSerializer()).serializeToString( this.toXmlDocument() );
286         }
287
288         this.fromBreaker = function (marctxt) {
289             var me = this;
290
291             function cf_line_data (l) { return l.substring(4) || '' };
292             function df_line_data (l) { return l.substring(6) || '' };
293             function line_tag (l) { return l.substring(0,3) };
294             function df_ind1 (l) { return l.substring(4,5).replace('\\',' ') };
295             function df_ind2 (l) { return l.substring(5,6).replace('\\',' ') };
296             function isControlField (l) {
297                 var x = line_tag(l);
298                 return (x == 'LDR' || x < '010') ? true : false;
299             }
300             
301             var lines = marctxt.replace(/^=/gm,'').split('\n');
302             lines.forEach(function (current_line, ind) {
303
304                 if (current_line.match(/^#/)) {
305                     // skip comment lines
306                 } else if (isControlField(current_line)) {
307                     if (line_tag(current_line) == 'LDR') {
308                         me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500';
309                     } else {
310                         me.fields.push(
311                             new MARC21.Field({
312                                 record : me,
313                                 tag    : line_tag(current_line),
314                                 data   : cf_line_data(current_line).replace(/\\/g, ' ')
315                             })
316                         );
317                     }
318                 } else {
319                     if (current_line.substring(4,5) == me.delimiter) // add indicators if they were left out
320                         current_line = current_line.substring(0,3) + ' \\\\' + current_line.substring(4);
321
322                     var data = df_line_data(current_line);
323                     if (!(data.substring(0,1) == me.delimiter)) data = me.delimiter + 'a' + data;
324
325                     var local_delimiter = me.delimiter;
326                     if (data.indexOf('\u2021') > -1)
327                         local_delimiter = '\u2021';
328
329                     var sf_list = data.split(local_delimiter);
330                     sf_list.shift();
331
332                     me.fields.push(
333                         new MARC21.Field({
334                                 record    : me,
335                                 tag       : line_tag(current_line),
336                                 ind1      : df_ind1(current_line),
337                                 ind2      : df_ind2(current_line),
338                                 subfields : sf_list.map( function (sf, i) {
339                                                 var sf_data = sf.substring(1);
340                                                 if (local_delimiter == '$') sf_data = sf_data.replace(/\{dollar\}/g, '$');
341                                                 return [ sf.substring(0,1), sf_data, i ];
342                                             })
343                         })
344                     );
345                 }
346             });
347
348             for (var j = 0; j < this.fields.length; j++) {
349                 this.fields[j].position = j;
350             }
351
352             me.ready = true;
353             return this;
354         }
355
356         this.pruneEmptyFieldsAndSubfields = function() {
357             var me = this;
358             var fields_to_remove = [];
359             for (var i = 0; i < this.fields.length; i++) {
360                 var f = this.fields[i];
361                 if (f.isControlfield()) {
362                     if (!f.data){
363                         fields_to_remove.push(f);
364                     }
365                 } else {
366                     f.pruneEmptySubfields();
367                     if (f.isEmptyDatafield()) {
368                         fields_to_remove.push(f);
369                     }
370                 }
371             }
372             fields_to_remove.forEach(function(f) {
373                 me.deleteField(f);
374             });
375         }
376
377         this.toBreaker = function () {
378
379             var me = this;
380             var mtxt = '=LDR ' + this.leader + '\n';
381
382             mtxt += this.fields.map( function (f) {
383                 if (f.isControlfield()) {
384                     if (f.data) return '=' + f.tag + ' ' + f.data.replace(/ /g, '\\');
385                     return '=' + f.tag;
386                 } else {
387                     return '=' + f.tag + ' ' +
388                         f.indicator(1).replace(' ','\\') + 
389                         f.indicator(2).replace(' ','\\') + 
390                         f.subfields.map( function (sf) {
391                             var sf_data = sf[1];
392                             if (me.delimiter == '$') sf_data = sf_data.replace(/\$/g, '{dollar}');
393                             return me.delimiter + sf[0] + sf_data;
394                         }).join('');
395                 }
396             }).join('\n');
397
398             return mtxt;
399         }
400
401         this.recordType = function () {
402         
403             var _t = this.leader.substr(MARC21.Record._ff_pos.Type.ldr.BKS.start, MARC21.Record._ff_pos.Type.ldr.BKS.len);
404             var _b = this.leader.substr(MARC21.Record._ff_pos.BLvl.ldr.BKS.start, MARC21.Record._ff_pos.BLvl.ldr.BKS.len);
405         
406             for (var t in MARC21.Record._recType) {
407                 if (_t.match(MARC21.Record._recType[t].Type) && _b.match(MARC21.Record._recType[t].BLvl)) {
408                     return t;
409                 }
410             }
411             return 'BKS'; // default
412         }
413         
414         this.extractFixedField = function (field, dflt) {
415         if (!MARC21.Record._ff_pos[field]) return null;
416         
417             var _l = this.leader;
418             var _8 = this.field('008').data;
419             var _6 = this.field('006').data;
420         
421             var rtype = this.recordType();
422         
423             var val;
424         
425             if (MARC21.Record._ff_pos[field].ldr && _l) {
426                 if (MARC21.Record._ff_pos[field].ldr[rtype]) {
427                     val = _l.substr(
428                         MARC21.Record._ff_pos[field].ldr[rtype].start,
429                         MARC21.Record._ff_pos[field].ldr[rtype].len
430                     );
431                 }
432             } else if (MARC21.Record._ff_pos[field]._8 && _8) {
433                 if (MARC21.Record._ff_pos[field]._8[rtype]) {
434                     val = _8.substr(
435                         MARC21.Record._ff_pos[field]._8[rtype].start,
436                         MARC21.Record._ff_pos[field]._8[rtype].len
437                     );
438                 }
439             }
440         
441             if (!val && MARC21.Record._ff_pos[field]._6 && _6) {
442                 if (MARC21.Record._ff_pos[field]._6[rtype]) {
443                     val = _6.substr(
444                         MARC21.Record._ff_pos[field]._6[rtype].start,
445                         MARC21.Record._ff_pos[field]._6[rtype].len
446                     );
447                 }
448             }
449     
450             if (!val && dflt) {
451                 val = '';
452                 var d;
453                 var p;
454                 if (MARC21.Record._ff_pos[field].ldr && MARC21.Record._ff_pos[field].ldr[rtype]) {
455                     d = MARC21.Record._ff_pos[field].ldr[rtype].def;
456                     p = 'ldr';
457                 }
458     
459                 if (MARC21.Record._ff_pos[field]._8 && MARC21.Record._ff_pos[field]._8[rtype]) {
460                     d = MARC21.Record._ff_pos[field]._8[rtype].def;
461                     p = '_8';
462                 }
463     
464                 if (!val && MARC21.Record._ff_pos[field]._6 && MARC21.Record._ff_pos[field]._6[rtype]) {
465                     d = MARC21.Record._ff_pos[field]._6[rtype].def;
466                     p = '_6';
467                 }
468     
469                 if (p) {
470                     for (var j = 0; j < MARC21.Record._ff_pos[field][p][rtype].len; j++) {
471                         val += d;
472                     }
473                 } else {
474                     val = null;
475                 }
476             }
477     
478             return val;
479         }
480     
481         this.setFixedField = function (field, value) {
482             if (!MARC21.Record._ff_pos[field]) return null;
483         
484             var _l = this.leader;
485             var _8 = this.field('008').data;
486             var _6 = this.field('006').data;
487         
488             var rtype = this.recordType();
489         
490             var done = false;
491             if (MARC21.Record._ff_pos[field].ldr && _l) {
492                 if (MARC21.Record._ff_pos[field].ldr[rtype]) { // It's in the leader
493                     if (value.length > MARC21.Record._ff_pos[field].ldr[rtype].len)
494                         value = value.substr(0, MARC21.Record._ff_pos[field].ldr[rtype].len);
495                     while (value.length < MARC21.Record._ff_pos[field].ldr[rtype].len)
496                         value += MARC21.Record._ff_pos[field].ldr[rtype].def;
497                     this.leader =
498                         _l.substring(0, MARC21.Record._ff_pos[field].ldr[rtype].start) +
499                         value +
500                         _l.substring(
501                             MARC21.Record._ff_pos[field].ldr[rtype].start
502                             + MARC21.Record._ff_pos[field].ldr[rtype].len
503                         );
504                     done = true;
505                 }
506             } else if (MARC21.Record._ff_pos[field]._8 && _8) {
507                 if (MARC21.Record._ff_pos[field]._8[rtype]) { // Nope, it's in the 008
508                     if (value.length > MARC21.Record._ff_pos[field]._8[rtype].len)
509                         value = value.substr(0, MARC21.Record._ff_pos[field]._8[rtype].len);
510                     while (value.length < MARC21.Record._ff_pos[field]._8[rtype].len)
511                         value += MARC21.Record._ff_pos[field]._8[rtype].def;
512
513                     // first ensure that 008 is padded to appropriate length
514                     var f008_length = (rtype in MARC21.Record._ff_lengths['008']) ?
515                         MARC21.Record._ff_lengths['008'][rtype] :
516                         MARC21.Record._ff_lengths['008']['default'];
517                     if (_8.length < f008_length) {
518                         for (var i = _8.length; i < f008_length; i++) {
519                             _8 += ' ';
520                         }
521                     }
522                     this.field('008').update(
523                         _8.substring(0, MARC21.Record._ff_pos[field]._8[rtype].start) +
524                         value +
525                         _8.substring(
526                             MARC21.Record._ff_pos[field]._8[rtype].start
527                             + MARC21.Record._ff_pos[field]._8[rtype].len
528                         )
529                     );
530                     done = true;
531                 }
532             }
533         
534             if (!done && MARC21.Record._ff_pos[field]._6 && _6) {
535                 if (MARC21.Record._ff_pos[field]._6[rtype]) { // ok, maybe the 006?
536                     if (value.length > MARC21.Record._ff_pos[field]._6[rtype].len)
537                         value = value.substr(0, MARC21.Record._ff_pos[field]._6[rtype].len);
538                     while (value.length < MARC21.Record._ff_pos[field]._6[rtype].len)
539                         value += MARC21.Record._ff_pos[field]._6[rtype].def;
540                     this.field('006').update(
541                         _6.substring(0, MARC21.Record._ff_pos[field]._6[rtype].start) +
542                         value +
543                         _6.substring(
544                             MARC21.Record._ff_pos[field]._6[rtype].start
545                             + MARC21.Record._ff_pos[field]._6[rtype].len
546                         )
547                     );
548                 }
549             }
550     
551             return value;
552         }
553
554         this.ready = false;
555         this.fields = [];
556         this.delimiter = MARC21.Record.delimiter ? MARC21.Record.delimiter : '\u2021';
557         this.leader = '00000cam a2200205Ka 4500';
558
559         if (kwargs.delimiter) this.delimiter = kwargs.delimiter;
560         if (kwargs.onLoad) this.onLoad = kwargs.onLoad;
561
562         if (kwargs.url) {
563             //this.fromXmlURL(kwargs.url);
564         } else if (kwargs.marcxml) {
565             this.fromXmlString(kwargs.marcxml);
566             if (this.onLoad) this.onLoad();
567         } else if (kwargs.xml) {
568             this.fromXmlDocument(kwargs.xml);
569             if (this.onLoad) this.onLoad();
570         } else if (kwargs.marcbreaker) {
571             this.fromBreaker(kwargs.marcbreaker);
572             if (this.onLoad) this.onLoad();
573         }
574
575         if (kwargs.rtype == 'AUT') {
576             this.setFixedField('Type','z');
577         }
578     },
579
580     Field : function (kwargs) {
581         if (!kwargs) kwargs = {};
582
583         this.subfield = function (code, wantarray) {
584             var list = this.subfields.filter( function (s) {
585                 if (s[0] == code) return true; return false;
586             });
587             if (!wantarray && list.length == 1) return list[0];
588             return list;
589         }
590
591         this.addSubfields = function () {
592             for (var i = 0; i < arguments.length; i++) {
593                 var code = arguments[i];
594                 var value = arguments[++i];
595                 this.subfields.push( [ code, value ] );
596             }
597         }
598
599         this.deleteExactSubfields = function () {
600             var me = this;
601             var counter = 0;
602             var done = false;
603             for ( var i in arguments ) {
604                 var f = arguments[i];
605                 for (var j = 0; j < me.subfields.length; j++) {
606                     if (f === me.subfields[j]) {
607                         me.subfields.splice(j,1);
608                         counter++
609                         j++;
610                         done = true;
611                     }
612                     if (done && me.subfields[j])
613                         me.subfields[j][2] -= 1;
614                 }
615             }
616             return counter;
617         }
618
619
620         this.deleteSubfields = function (c) {
621             return this.deleteSubfield( { code : c } );
622         }
623
624         this.deleteSubfield = function (args) {
625             var me = this;
626             if (!Array.isArray( args.code )) {
627                 args.code = [ args.code ];
628             }
629
630             if (args.pos && !Array.isArray( args.pos )) {
631                 args.pos = [ args.pos ];
632             }
633
634             for (var i = 0; i < args.code.length; i++) {
635                 var sub_pos = {};
636                 for (var j = 0; j < me.subfields.length; j++) {
637                     if (me.subfields[j][0] == args.code[i]) {
638
639                         if (!sub_pos[args.code[i]]) sub_pos[args.code[j]] = 0;
640                         else sub_pos[args.code[i]]++;
641
642                         if (args.pos) {
643                             for (var k = 0; k < args.pos.length; k++) {
644                                 if (sub_pos[args.code[i]] == args.pos[k]) me.subfields.splice(j,1);
645                             }
646                         } else if (args.match && me.subfields[j][1].match( args.match )) {
647                             me.subfields.splice(j,1);
648                         } else {
649                             me.subfields.splice(j,1);
650                         }
651                     }
652                 }
653             }
654         }
655
656         this.update = function ( args ) {
657             if (this.isControlfield()) {
658                 this.data = args;
659             } else {
660                 if (args.ind1) this.ind1 = args.ind1;
661                 if (args.ind2) this.ind2 = args.ind2;
662                 if (args.tag) this.tag = args.tag;
663
664                 for (var i in args) {
665                     if (i == 'tag' || i == 'ind1' || i == 'ind2') continue;
666                     var done = 0;
667                     this.subfields.forEach( function (f) {
668                         if (!done && f[0] == i) {
669                             f[1] = args[i];
670                             done = 1;
671                         }
672                     });
673                 }
674             }
675         }
676
677         this.isControlfield = function () {
678             return this.tag < '010' ? true : false;
679         }
680
681         this.pruneEmptySubfields = function () {
682             if (this.isControlfield()) return;
683             var me = this;
684             var subfields_to_remove = [];
685             this.subfields.forEach( function(f) {
686                 if (f[1] == '') {
687                     subfields_to_remove.push(f);
688                 }
689             });
690             subfields_to_remove.forEach(function(f) {
691                 me.deleteExactSubfields(f);
692             });
693         }
694         this.isEmptyDatafield = function () {
695             if (this.isControlfield()) return false;
696             var isEmpty = true;
697             this.subfields.forEach( function(f) {
698                 if (isEmpty && f[1] != '') {
699                     isEmpty = false;
700                 }
701             });
702             return isEmpty;
703         }
704
705         this.indicator = function (num, value) {
706             if (value !== undefined) {
707                 if (num == 1) this.ind1 = value;
708                 else if (num == 2) this.ind2 = value;
709                 else { this.error = true; return null; }
710             }
711             if (num == 1) return this.ind1;
712             else if (num == 2) return this.ind2;
713             else { this.error = true; return null; }
714         }
715
716         this.error = false; 
717         this.record = null; // MARC record pointer
718         this.tag = ''; // MARC tag
719         this.ind1 = ' '; // MARC indicator 1
720         this.ind2 = ' '; // MARC indicator 2
721         this.data = ''; // MARC data for a controlfield element
722         this.subfields = []; // list of MARC subfields for a datafield element
723
724         this.position = kwargs.position;
725         this.record = kwargs.record;
726         this.tag = kwargs.tag;
727         this.ind1 = kwargs.ind1 || ' ';
728         this.ind2 = kwargs.ind2 || ' ';
729         this.data = kwargs.data;
730
731         if (kwargs.subfields) this.subfields = kwargs.subfields;
732         else this.subfields = [];
733
734     }
735 };
736
737 MARC21.Record._recType = {
738     BKS : { Type : /[at]{1}/,    BLvl : /[acdm]{1}/ },
739     SER : { Type : /[a]{1}/,    BLvl : /[bsi]{1}/ },
740     VIS : { Type : /[gkro]{1}/,    BLvl : /[abcdmsi]{1}/ },
741     MIX : { Type : /[p]{1}/,    BLvl : /[cdi]{1}/ },
742     MAP : { Type : /[ef]{1}/,    BLvl : /[abcdmsi]{1}/ },
743     SCO : { Type : /[cd]{1}/,    BLvl : /[abcdmsi]{1}/ },
744     REC : { Type : /[ij]{1}/,    BLvl : /[abcdmsi]{1}/ },
745     COM : { Type : /[m]{1}/,    BLvl : /[abcdmsi]{1}/ },
746     AUT : { Type : /[z]{1}/,    BLvl : /.{1}/ },
747     MFHD : { Type : /[uvxy]{1}/,  BLvl : /.{1}/ }
748 };
749
750 MARC21.Record._ff_lengths = {
751     '008' : {
752         default : 40,
753         MFHD    : 32
754     }
755 }
756
757 MARC21.Record._ff_pos = {
758     AccM : {
759         _8 : {
760             SCO : {start: 24, len : 6, def : ' ' },
761             REC : {start: 24, len : 6, def : ' ' }
762         },
763         _6 : {
764             SCO : {start: 7, len : 6, def : ' ' },
765             REC : {start: 7, len : 6, def : ' ' }
766         }
767     },
768     Alph : {
769         _8 : {
770             SER : {start : 33, len : 1, def : ' ' }
771         },
772         _6 : {
773             SER : {start : 16, len : 1, def : ' ' }
774         }
775     },
776     Audn : {
777         _8 : {
778             BKS : {start : 22, len : 1, def : ' ' },
779             SER : {start : 22, len : 1, def : ' ' },
780             VIS : {start : 22, len : 1, def : ' ' },
781             SCO : {start : 22, len : 1, def : ' ' },
782             REC : {start : 22, len : 1, def : ' ' },
783             COM : {start : 22, len : 1, def : ' ' }
784         },
785         _6 : {
786             BKS : {start : 5, len : 1, def : ' ' },
787             SER : {start : 5, len : 1, def : ' ' },
788             VIS : {start : 5, len : 1, def : ' ' },
789             SCO : {start : 5, len : 1, def : ' ' },
790             REC : {start : 5, len : 1, def : ' ' },
791             COM : {start : 5, len : 1, def : ' ' }
792         }
793     },
794     Biog : {
795         _8 : {
796             BKS : {start : 34, len : 1, def : ' ' }
797         },
798         _6 : {
799             BKS : {start : 17, len : 1, def : ' ' }
800         }
801     },
802     BLvl : {
803         ldr : {
804             BKS : {start : 7, len : 1, def : 'm' },
805             SER : {start : 7, len : 1, def : 's' },
806             VIS : {start : 7, len : 1, def : 'm' },
807             MIX : {start : 7, len : 1, def : 'c' },
808             MAP : {start : 7, len : 1, def : 'm' },
809             SCO : {start : 7, len : 1, def : 'm' },
810             REC : {start : 7, len : 1, def : 'm' },
811             COM : {start : 7, len : 1, def : 'm' }
812         }
813     },
814     Comp : {
815         _8 : {
816             SCO : {start : 18, len : 2, def : 'uu'},
817             REC : {start : 18, len : 2, def : 'uu'}
818         },
819         _6 : {
820             SCO : {start : 1, len : 2, def : 'uu'},
821             REC : {start : 1, len : 2, def : 'uu'}
822         },
823     },
824     Conf : {
825         _8 : {
826             BKS : {start : 29, len : 1, def : '0' },
827             SER : {start : 29, len : 1, def : '0' }
828         },
829         _6 : {
830             BKS : {start : 11, len : 1, def : '0' },
831             SER : {start : 11, len : 1, def : '0' }
832         }
833     },
834     Cont : {
835         _8 : {
836             BKS : {start : 24, len : 4, def : ' ' },
837             SER : {start : 25, len : 3, def : ' ' }
838         },
839         _6 : {
840             BKS : {start : 7, len : 4, def : ' ' },
841             SER : {start : 8, len : 3, def : ' ' }
842         }
843     },
844     CrTp : {
845         _8 : {
846             MAP : {start: 25, len : 1, def : 'a' }
847         },
848         _6 : { 
849             MAP : {start : 8, len : 1, def : 'a' }
850         }
851     },
852     Ctrl : {
853         ldr : {
854             BKS : {start : 8, len : 1, def : ' ' },
855             SER : {start : 8, len : 1, def : ' ' },
856             VIS : {start : 8, len : 1, def : ' ' },
857             MIX : {start : 8, len : 1, def : ' ' },
858             MAP : {start : 8, len : 1, def : ' ' },
859             SCO : {start : 8, len : 1, def : ' ' },
860             REC : {start : 8, len : 1, def : ' ' },
861             COM : {start : 8, len : 1, def : ' ' }
862         }
863     },
864     Ctry : {
865             _8 : {
866                 BKS : {start : 15, len : 3, def : ' ' },
867                 SER : {start : 15, len : 3, def : ' ' },
868                 VIS : {start : 15, len : 3, def : ' ' },
869                 MIX : {start : 15, len : 3, def : ' ' },
870                 MAP : {start : 15, len : 3, def : ' ' },
871                 SCO : {start : 15, len : 3, def : ' ' },
872                 REC : {start : 15, len : 3, def : ' ' },
873                 COM : {start : 15, len : 3, def : ' ' }
874             }
875         },
876     Date1 : {
877         _8 : {
878             BKS : {start : 7, len : 4, def : ' ' },
879             SER : {start : 7, len : 4, def : ' ' },
880             VIS : {start : 7, len : 4, def : ' ' },
881             MIX : {start : 7, len : 4, def : ' ' },
882             MAP : {start : 7, len : 4, def : ' ' },
883             SCO : {start : 7, len : 4, def : ' ' },
884             REC : {start : 7, len : 4, def : ' ' },
885             COM : {start : 7, len : 4, def : ' ' }
886         }
887     },
888     Date2 : {
889         _8 : {
890             BKS : {start : 11, len : 4, def : ' ' },
891             SER : {start : 11, len : 4, def : '9' },
892             VIS : {start : 11, len : 4, def : ' ' },
893             MIX : {start : 11, len : 4, def : ' ' },
894             MAP : {start : 11, len : 4, def : ' ' },
895             SCO : {start : 11, len : 4, def : ' ' },
896             REC : {start : 11, len : 4, def : ' ' },
897             COM : {start : 11, len : 4, def : ' ' }
898         }
899     },
900     Desc : {
901         ldr : {
902             BKS : {start : 18, len : 1, def : ' ' },
903             SER : {start : 18, len : 1, def : ' ' },
904             VIS : {start : 18, len : 1, def : ' ' },
905             MIX : {start : 18, len : 1, def : ' ' },
906             MAP : {start : 18, len : 1, def : ' ' },
907             SCO : {start : 18, len : 1, def : ' ' },
908             REC : {start : 18, len : 1, def : ' ' },
909             COM : {start : 18, len : 1, def : ' ' }
910         }
911     },
912     DtSt : {
913         _8 : {
914             BKS : {start : 6, len : 1, def : ' ' },
915             SER : {start : 6, len : 1, def : 'c' },
916             VIS : {start : 6, len : 1, def : ' ' },
917             MIX : {start : 6, len : 1, def : ' ' },
918             MAP : {start : 6, len : 1, def : ' ' },
919             SCO : {start : 6, len : 1, def : ' ' },
920             REC : {start : 6, len : 1, def : ' ' },
921             COM : {start : 6, len : 1, def : ' ' }
922         }
923     },
924     ELvl : {
925         ldr : {
926             BKS : {start : 17, len : 1, def : ' ' },
927             SER : {start : 17, len : 1, def : ' ' },
928             VIS : {start : 17, len : 1, def : ' ' },
929             MIX : {start : 17, len : 1, def : ' ' },
930             MAP : {start : 17, len : 1, def : ' ' },
931             SCO : {start : 17, len : 1, def : ' ' },
932             REC : {start : 17, len : 1, def : ' ' },
933             COM : {start : 17, len : 1, def : ' ' },
934             AUT : {start : 17, len : 1, def : 'n' },
935             MFHD : {start : 17, len : 1, def : 'u' }
936         }
937     },
938     EntW : {
939         _8 : {
940             SER : {start : 24, len : 1, def : ' '}
941         },
942         _6 : {
943             SER : {start : 7, len : 1, def : ' '}
944         }
945     },
946     Fest : {
947         _8 : {
948             BKS : {start : 30, len : 1, def : '0' }
949         },
950         _6 : {
951             BKS : {start : 13, len : 1, def : '0' }
952         }
953     },
954     File : {
955         _8 : {
956             COM : {start: 26, len : 1, def : 'u' }
957         },
958         _6 : {
959             COM : {start: 9, len : 1, def : 'u' }
960         }
961     },
962     FMus : {
963         _8 : {
964             SCO : {start : 20, len : 1, def : 'u'},
965             REC : {start : 20, len : 1, def : 'n'}
966         },
967         _6 : {
968             SCO : {start : 3, len : 1, def : 'u'},
969             REC : {start : 3, len : 1, def : 'n'}
970         },
971     },
972     Form : {
973         _8 : {
974             BKS : {start : 23, len : 1, def : ' ' },
975             SER : {start : 23, len : 1, def : ' ' },
976             VIS : {start : 29, len : 1, def : ' ' },
977             MIX : {start : 23, len : 1, def : ' ' },
978             MAP : {start : 29, len : 1, def : ' ' },
979             SCO : {start : 23, len : 1, def : ' ' },
980             REC : {start : 23, len : 1, def : ' ' }
981         },
982         _6 : {
983             BKS : {start : 6, len : 1, def : ' ' },
984             SER : {start : 6, len : 1, def : ' ' },
985             VIS : {start : 12, len : 1, def : ' ' },
986             MIX : {start : 6, len : 1, def : ' ' },
987             MAP : {start : 12, len : 1, def : ' ' },
988             SCO : {start : 6, len : 1, def : ' ' },
989             REC : {start : 6, len : 1, def : ' ' }
990         }
991     },
992     Freq : {
993         _8 : {
994             SER : {start : 18, len : 1, def : ' '}
995         },
996         _6 : {
997             SER : {start : 1, len : 1, def : ' '}
998         }
999     },
1000     GPub : {
1001         _8 : {
1002             BKS : {start : 28, len : 1, def : ' ' },
1003             SER : {start : 28, len : 1, def : ' ' },
1004             VIS : {start : 28, len : 1, def : ' ' },
1005             MAP : {start : 28, len : 1, def : ' ' },
1006             COM : {start : 28, len : 1, def : ' ' }
1007         },
1008         _6 : {
1009             BKS : {start : 11, len : 1, def : ' ' },
1010             SER : {start : 11, len : 1, def : ' ' },
1011             VIS : {start : 11, len : 1, def : ' ' },
1012             MAP : {start : 11, len : 1, def : ' ' },
1013             COM : {start : 11, len : 1, def : ' ' }
1014         }
1015     },
1016     Ills : {
1017         _8 : {
1018             BKS : {start : 18, len : 4, def : ' ' }
1019         },
1020         _6 : {
1021             BKS : {start : 1, len : 4, def : ' ' }
1022         }
1023     },
1024     Indx : {
1025         _8 : {
1026             BKS : {start : 31, len : 1, def : '0' },
1027             MAP : {start : 31, len : 1, def : '0' }
1028         },
1029         _6 : {
1030             BKS : {start : 14, len : 1, def : '0' },
1031             MAP : {start : 14, len : 1, def : '0' }
1032         }
1033     },
1034     Item : {
1035         ldr : {
1036             MFHD : {start : 18, len : 1, def : 'i' }
1037         }
1038     },
1039     Lang : {
1040         _8 : {
1041             BKS : {start : 35, len : 3, def : ' ' },
1042             SER : {start : 35, len : 3, def : ' ' },
1043             VIS : {start : 35, len : 3, def : ' ' },
1044             MIX : {start : 35, len : 3, def : ' ' },
1045             MAP : {start : 35, len : 3, def : ' ' },
1046             SCO : {start : 35, len : 3, def : ' ' },
1047             REC : {start : 35, len : 3, def : ' ' },
1048             COM : {start : 35, len : 3, def : ' ' }
1049         }
1050     },
1051     LitF : {
1052         _8 : {
1053             BKS : {start : 33, len : 1, def : '0' }
1054         },
1055         _6 : {
1056             BKS : {start : 16, len : 1, def : '0' }
1057         }
1058     },
1059     LTxt : {
1060         _8 : {
1061             SCO : {start : 30, len : 2, def : 'n'},
1062             REC : {start : 30, len : 2, def : ' '}
1063         },
1064         _6 : {
1065             SCO : {start : 13, len : 2, def : 'n'},
1066             REC : {start : 13, len : 2, def : ' '}
1067         },
1068     },
1069     MRec : {
1070         _8 : {
1071             BKS : {start : 38, len : 1, def : ' ' },
1072             SER : {start : 38, len : 1, def : ' ' },
1073             VIS : {start : 38, len : 1, def : ' ' },
1074             MIX : {start : 38, len : 1, def : ' ' },
1075             MAP : {start : 38, len : 1, def : ' ' },
1076             SCO : {start : 38, len : 1, def : ' ' },
1077             REC : {start : 38, len : 1, def : ' ' },
1078             COM : {start : 38, len : 1, def : ' ' }
1079         }
1080     },
1081     Orig : {
1082         _8 : {
1083             SER : {start : 22, len : 1, def : ' '}
1084         },
1085         _6 : {
1086             SER : {start: 5, len : 1, def: ' '}
1087         }
1088     },
1089     Part : {
1090         _8 : {
1091             SCO : {start : 21, len : 1, def : ' '},
1092             REC : {start : 21, len : 1, def : 'n'}
1093         },
1094         _6 : {
1095             SCO : {start : 4, len : 1, def : ' '},
1096             REC : {start : 4, len : 1, def : 'n'}
1097         },
1098     },
1099     Proj : {
1100         _8 : {
1101             MAP : {start : 22, len : 2, def : ' ' }
1102         },
1103         _6 : {
1104             MAP: {start : 5, len : 2, def : ' ' }
1105         }
1106     },
1107     RecStat : {
1108         ldr : {
1109             BKS : {start : 5, len : 1, def : 'n' },
1110             SER : {start : 5, len : 1, def : 'n' },
1111             VIS : {start : 5, len : 1, def : 'n' },
1112             MIX : {start : 5, len : 1, def : 'n' },
1113             MAP : {start : 5, len : 1, def : 'n' },
1114             SCO : {start : 5, len : 1, def : 'n' },
1115             REC : {start : 5, len : 1, def : 'n' },
1116             COM : {start : 5, len : 1, def : 'n' },
1117             MFHD: {start : 5, len : 1, def : 'n' },
1118             AUT : {start : 5, len : 1, def : 'n' }
1119         }
1120     },
1121     Regl : {
1122         _8 : {
1123             SER : {start : 19, len : 1, def : ' '}
1124         },
1125         _6 : {
1126             SER : {start : 2, len : 1, def : ' '}
1127         }
1128     },
1129     Relf : {
1130         _8 : {
1131             MAP : {start: 18, len : 4, def : ' '}
1132         },
1133         _6 : {
1134             MAP : {start: 1, len : 4, def : ' '}
1135         }
1136     },
1137     'S/L' : {
1138         _8 : {
1139             SER : {start : 34, len : 1, def : '0' }
1140         },
1141         _6 : {
1142             SER : {start : 17, len : 1, def : '0' }
1143         }
1144     },
1145     SpFM : {
1146         _8 : {
1147             MAP : {start: 33, len : 2, def : ' ' }
1148         },
1149         _6 : {
1150             MAP : {start: 16, len : 2, def : ' '}
1151         }
1152     },
1153     Srce : {
1154         _8 : {
1155             BKS : {start : 39, len : 1, def : 'd' },
1156             SER : {start : 39, len : 1, def : 'd' },
1157             VIS : {start : 39, len : 1, def : 'd' },
1158             SCO : {start : 39, len : 1, def : 'd' },
1159             REC : {start : 39, len : 1, def : 'd' },
1160             COM : {start : 39, len : 1, def : 'd' },
1161             MFHD : {start : 39, len : 1, def : 'd' },
1162             "AUT" : {"start" : 39, "len" : 1, "def" : 'd' }
1163         }
1164     },
1165     SrTp : {
1166         _8 : {
1167             SER : {start : 21, len : 1, def : ' '}
1168         },
1169         _6 : {
1170             SER : {start : 4, len : 1, def : ' '}
1171         }
1172     },
1173     Tech : {
1174         _8 : {
1175             VIS : {start : 34, len : 1, def : ' '}
1176         },
1177         _6 : {
1178             VIS : {start : 17, len : 1, def : ' '}
1179         }
1180     },
1181     Time : {
1182         _8 : {
1183             VIS : {start : 18, len : 3, def : ' '}
1184         },
1185         _6 : {
1186             VIS : {start : 1, len : 3, def : ' '}
1187         }
1188     },
1189     TMat : {
1190         _8 : {
1191             VIS : {start : 33, len : 1, def : ' ' }
1192         },
1193         _6 : {
1194             VIS : {start : 16, len : 1, def : ' ' }
1195         }
1196     },
1197     TrAr : {
1198         _8 : {
1199             SCO : {start : 33, len : 1, def : ' ' },
1200             REC : {start : 33, len : 1, def : 'n' }
1201         },
1202         _6 : {
1203             SCO : {start : 16, len : 1, def : ' ' },
1204             REC : {start : 16, len : 1, def : 'n' }
1205         }
1206     },
1207     Type : {
1208         ldr : {
1209             BKS : {start : 6, len : 1, def : 'a' },
1210             SER : {start : 6, len : 1, def : 'a' },
1211             VIS : {start : 6, len : 1, def : 'g' },
1212             MIX : {start : 6, len : 1, def : 'p' },
1213             MAP : {start : 6, len : 1, def : 'e' },
1214             SCO : {start : 6, len : 1, def : 'c' },
1215             REC : {start : 6, len : 1, def : 'i' },
1216             COM : {start : 6, len : 1, def : 'm' },
1217             AUT : {start : 6, len : 1, def : 'z' },
1218             MFHD : {start : 6, len : 1, def : 'y' }
1219         }
1220     },
1221     "GeoDiv" : {
1222          "_8" : {
1223              "AUT" : {"start" : 6, "len" : 1, "def" : ' ' }
1224          }
1225      },
1226      "Roman" : {
1227          "_8" : {
1228              "AUT" : {"start" : 7, "len" : 1, "def" : ' ' }
1229          }
1230      },
1231      "CatLang" : {
1232          "_8" : {
1233              "AUT" : {"start" : 8, "len" : 1, "def" : ' ' }
1234          }
1235      },
1236      "Kind" : {
1237          "_8" : {
1238              "AUT" : {"start" : 9, "len" : 1, "def" : ' ' }
1239          }
1240      },
1241      "Rules" : {
1242          "_8" : {
1243              "AUT" : {"start" : 10, "len" : 1, "def" : ' ' }
1244          }
1245      },
1246      "Subj" : {
1247          "_8" : {
1248              "AUT" : {"start" : 11, "len" : 1, "def" : ' ' }
1249          }
1250      },
1251      "Series" : {
1252          "_8" : {
1253              "AUT" : {"start" : 12, "len" : 1, "def" : ' ' }
1254          }
1255      },
1256      "SerNum" : {
1257          "_8" : {
1258              "AUT" : {"start" : 13, "len" : 1, "def" : ' ' }
1259          }
1260      },
1261      "NameUse" : {
1262          "_8" : {
1263              "AUT" : {"start" : 14, "len" : 1, "def" : ' ' }
1264          }
1265      },
1266      "SubjUse" : {
1267          "_8" : {
1268              "AUT" : {"start" : 15, "len" : 1, "def" : ' ' }
1269          }
1270      },
1271      "SerUse" : {
1272          "_8" : {
1273              "AUT" : {"start" : 16, "len" : 1, "def" : ' ' }
1274          }
1275      },
1276      "TypeSubd" : {
1277          "_8" : {
1278              "AUT" : {"start" : 17, "len" : 1, "def" : ' ' }
1279          }
1280      },
1281      "GovtAgn" : {
1282          "_8" : {
1283              "AUT" : {"start" : 28, "len" : 1, "def" : ' ' }
1284          }
1285      },
1286      "RefStatus" : {
1287          "_8" : {
1288              "AUT" : {"start" : 29, "len" : 1, "def" : ' ' }
1289          }
1290      },
1291      "UpdStatus" : {
1292          "_8" : {
1293              "AUT" : {"start" : 31, "len" : 1, "def" : ' ' }
1294          }
1295      },
1296      "Name" : {
1297          "_8" : {
1298              "AUT" : {"start" : 32, "len" : 1, "def" : ' ' }
1299          }
1300      },
1301      "Status" : {
1302          "_8" : {
1303              "AUT" : {"start" : 33, "len" : 1, "def" : ' ' }
1304          }
1305      },
1306      "ModRec" : {
1307          "_8" : {
1308              "AUT" : {"start" : 38, "len" : 1, "def" : ' ' }
1309          }
1310      },
1311      "Source" : {
1312          "_8" : {
1313              "AUT" : {"start" : 39, "len" : 1, "def" : ' ' }
1314          }
1315      }
1316 };
1317