]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/marcedit.js
change the Swap Editor button to a persisted checkbox
[Evergreen.git] / Open-ILS / xul / staff_client / server / cat / marcedit.js
1 // vim: et:sw=4:ts=4:
2 var xmlDeclaration = /^<\?xml version[^>]+?>/;
3
4 var serializer = new XMLSerializer();
5 var marcns = new Namespace("http://www.loc.gov/MARC21/slim");
6 var gw = new Namespace("http://opensrf.org/-/namespaces/gateway/v1");
7 var xulns = new Namespace("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
8 default xml namespace = marcns;
9
10 var tooltip_hash = {};
11 var current_focus;
12 var _record;
13 var _record_type;
14 var bib_data;
15
16 var xml_record;
17
18 var context_menus;
19 var tag_menu;
20 var p;
21 var auth_pages = {};
22 var show_auth_menu = false;
23
24 function $(id) { return document.getElementById(id); }
25
26 function mangle_005() {
27     var now = new Date();
28     var y = now.getUTCFullYear();
29
30     var m = now.getUTCMonth() + 1;
31     if (m < 10) m = '0' + m;
32     
33     var d = now.getUTCDate();
34     if (d < 10) d = '0' + d;
35     
36     var H = now.getUTCHours();
37     if (H < 10) H = '0' + H;
38     
39     var M = now.getUTCMinutes();
40     if (M < 10) M = '0' + M;
41     
42     var S = now.getUTCSeconds();
43     if (S < 10) S = '0' + S;
44     
45
46     var stamp = '' + y + m + d + H + M + S + '.0';
47     createControlField('005',stamp);
48
49 }
50
51 function createControlField (tag,data) {
52     // first, remove the old field, if any;
53     for (var i in xml_record.controlfield.(@tag == tag)) delete xml_record.controlfield.(@tag == tag)[i];
54
55     var cf = <controlfield tag="" xmlns="http://www.loc.gov/MARC21/slim">{ data }</controlfield>;
56     cf.@tag = tag;
57
58     // then, find the right position and insert it
59     var done = 0;
60     var cfields = xml_record.controlfield;
61     var base = Number(tag.substring(2));
62     for (var i in cfields) {
63         var t = Number(cfields[i].@tag.toString().substring(2));
64         if (t > base) {
65             xml_record.insertChildBefore( cfields[i], cf );
66             done = 1
67             break;
68         }
69     }
70
71     if (!done) xml_record.insertChildBefore( xml_record.datafield[0], cf );
72
73     return cf;
74 }
75
76 function xml_escape_unicode ( str ) {
77     return str.replace(
78         /([\u0080-\ufffe])/g,
79         function (r,s) { return "&#x" + s.charCodeAt(0).toString(16) + ";"; }
80     );
81 }
82
83 function wrap_long_fields (node) {
84     var text_size = dojo.attr(node, 'size');
85     var hard_width = 100; 
86     if (text_size > hard_width) {
87         dojo.attr(node, 'multiline', 'true');
88         dojo.attr(node, 'cols', hard_width);
89         var text_rows = (text_size / hard_width) + 1;
90         dojo.attr(node, 'rows', text_rows);
91     }
92 }
93
94 function set_flat_editor (useFlatText) {
95
96     dojo.require('MARC.Record');
97
98     var xe = $('xul-editor');
99     var te = $('text-editor');
100
101     if (useFlatText) {
102         if (xe.hidden) { return; }
103         te.hidden = false;
104         xe.hidden = true;
105     } else {
106         if (te.hidden) { return; }
107         te.hidden = true;
108         xe.hidden = false;
109     }
110
111     if (te.hidden) {
112         // get the marcxml from the text box
113         var xml_string = new MARC.Record({
114             marcbreaker : $('text-editor-box').value,
115             delimiter : '$'
116         }).toXmlString();
117
118         // reset the xml record and rerender it
119         xml_record = new XML( xml_string );
120         loadRecord(xml_record);
121     } else {
122         var xml_string = xml_record.toXMLString();
123
124         // push the xml record into the textbox
125         var rec = new MARC.Record ({ delimiter : '$', marcxml : xml_string });
126         $('text-editor-box').value = rec.toBreaker();
127     }
128 }
129
130 function my_init() {
131     try {
132
133         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
134         if (typeof JSAN == 'undefined') { throw( $("commonStrings").getString('common.jsan.missing') ); }
135         JSAN.errorLevel = "die"; // none, warn, or die
136         JSAN.addRepository('/xul/server/');
137
138         // Fake xulG for standalone...
139         try {
140             window.xulG.record;
141         } catch (e) {
142             window.xulG = {};
143             window.xulG.record = {};
144             window.xulG.save = {};
145
146             window.xulG.save.label = $('catStrings').getString('staff.cat.marcedit.save.label');
147             window.xulG.save.func = function (r) { alert(r); }
148
149             var cgi = new CGI();
150             var _rid = cgi.param('record');
151             if (_rid) {
152                 window.xulG.record.id = _rid;
153                 window.xulG.record.url = '/opac/extras/supercat/retrieve/marcxml/record/' + _rid;
154             }
155         }
156
157         // End faking part...
158
159         /* Check for an explicitly passed record type
160          * This is not the same as the fixed-field record type; we can't trust
161          * the fixed fields when making modifications to the attributes for a
162          * given record (in particular, config.bib_source only applies for bib
163          * records, but an auth or MFHD record with the same ID and bad fixed
164          * fields could trample the config.bib_source value for the
165          * corresponding bib record if we're not careful.
166          *
167          * are = authority record
168          * sre = serial record (MFHD)
169          * bre = bibliographic record
170          */
171         if (!window.xulG.record.rtype) {
172             var cgi = new CGI();
173             window.xulG.record.rtype = cgi.param('rtype') || false;
174         }
175
176         document.getElementById('save-button').setAttribute('label', window.xulG.save.label);
177         document.getElementById('save-button').setAttribute('oncommand',
178             'if ($("xul-editor").hidden) set_flat_editor(false); ' +
179             'mangle_005(); ' + 
180             'var xml_string = xml_escape_unicode( xml_record.toXMLString() ); ' + 
181             'save_attempt( xml_string ); ' +
182             'loadRecord(xml_record);'
183         );
184
185         if (window.xulG.record.url) {
186             var req =  new XMLHttpRequest();
187             req.open('POST',window.xulG.record.url,false);
188             req.send(null);
189             window.xulG.record.marc = req.responseText.replace(xmlDeclaration, '');
190         }
191
192         xml_record = new XML( window.xulG.record.marc );
193         if (xml_record..record[0]) xml_record = xml_record..record[0];
194
195         // Get the tooltip xml all async like
196         req =  new XMLHttpRequest();
197
198         // Set a default locale in case preferences fail us
199         var locale = "en-US";
200
201         // Try to get the locale from our preferences
202         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
203         try {
204             const Cc = Components.classes;
205             const Ci = Components.interfaces;
206             locale = Cc["@mozilla.org/preferences-service;1"].
207                 getService(Ci.nsIPrefBranch).
208                 getCharPref("general.useragent.locale");
209         }
210         catch (e) { }
211
212         // TODO: We should send a HEAD request to check for the existence of the desired file
213         // then fall back to the default locale if preferred locale is not necessary;
214         // however, for now we have a simplistic check:
215         //
216         // we currently have translations for only two locales; in the absence of a
217         // valid locale, default to the almighty en-US
218         if (locale != 'en-US' && locale != 'fr-CA') {
219             locale = 'en-US';
220         }
221
222         // grab the right tooltip based on MARC type
223         var tooltip_doc = 'marcedit-tooltips.xml';
224         switch (window.xulG.record.rtype) {
225             case 'bre':
226                 tooltip_doc = 'marcedit-tooltips.xml';
227                 break; 
228             case 'are':
229                 tooltip_doc = 'marcedit-tooltips-authority.xml';
230                 locale = 'en-US'; // FIXME - note TODO above; at moment only en-US has this
231                 break; 
232             case 'sre':
233                 tooltip_doc = 'marcedit-tooltips-mfhd.xml';
234                 locale = 'en-US'; // FIXME - note TODO above; at moment only en-US has this
235                 break; 
236             default: 
237                 tooltip_doc = 'marcedit-tooltips.xml';
238         }
239
240         // Get the locale-specific tooltips
241         req.open('GET','/xul/server/locale/' + locale + '/' + tooltip_doc,true);
242
243         context_menus = createComplexXULElement('popupset');
244         document.documentElement.appendChild( context_menus );
245
246         tag_menu = createPopup({position : 'after_start', id : 'tags_popup'});
247         context_menus.appendChild( tag_menu );
248
249         tag_menu.appendChild(
250             createMenuitem(
251                 { label : $('catStrings').getString('staff.cat.marcedit.add_row.label'),
252                   oncommand : 
253                     'var e = document.createEvent("KeyEvents");' +
254                     'e.initKeyEvent("keypress",1,1,null,1,0,0,0,13,0);' +
255                     'current_focus.inputField.dispatchEvent(e);'
256                  }
257             )
258         );
259
260         tag_menu.appendChild(
261             createMenuitem(
262                 { label : $('catStrings').getString('staff.cat.marcedit.insert_row.label'),
263                   oncommand : 
264                     'var e = document.createEvent("KeyEvents");' +
265                     'e.initKeyEvent("keypress",1,1,null,1,0,1,0,13,0);' +
266                     'current_focus.inputField.dispatchEvent(e);'
267                  }
268             )
269         );
270
271         tag_menu.appendChild(
272             createMenuitem(
273                 { label : $('catStrings').getString('staff.cat.marcedit.remove_row.label'),
274                   oncommand : 
275                     'var e = document.createEvent("KeyEvents");' +
276                     'e.initKeyEvent("keypress",1,1,null,1,0,0,0,46,0);' +
277                     'current_focus.inputField.dispatchEvent(e);'
278                 }
279             )
280         );
281
282         tag_menu.appendChild( createComplexXULElement( 'separator' ) );
283
284         tag_menu.appendChild(
285             createMenuitem(
286                 { label : $('catStrings').getString('staff.cat.marcedit.replace_006.label'),
287                   oncommand : 
288                     'var e = document.createEvent("KeyEvents");' +
289                     'e.initKeyEvent("keypress",1,1,null,1,0,0,0,64,0);' +
290                     'current_focus.inputField.dispatchEvent(e);'
291                  }
292             )
293         );
294
295         tag_menu.appendChild(
296             createMenuitem(
297                 { label : $('catStrings').getString('staff.cat.marcedit.replace_007.label'),
298                   oncommand : 
299                     'var e = document.createEvent("KeyEvents");' +
300                     'e.initKeyEvent("keypress",1,1,null,1,0,0,0,65,0);' +
301                     'current_focus.inputField.dispatchEvent(e);'
302                 }
303             )
304         );
305
306         tag_menu.appendChild(
307             createMenuitem(
308                 { label : $('catStrings').getString('staff.cat.marcedit.replace_008.label'),
309                   oncommand : 
310                     'var e = document.createEvent("KeyEvents");' +
311                     'e.initKeyEvent("keypress",1,1,null,1,0,0,0,66,0);' +
312                     'current_focus.inputField.dispatchEvent(e);'
313                 }
314             )
315         );
316
317         tag_menu.appendChild( createComplexXULElement( 'separator' ) );
318
319         p = createComplexXULElement('popupset');
320         document.documentElement.appendChild( p );
321
322         req.onreadystatechange = function () {
323             if (req.readyState == 4) {
324                 bib_data = new XML( req.responseText.replace(xmlDeclaration, '') );
325                 genToolTips();
326             }
327         }
328         req.send(null);
329
330         loadRecord(xml_record);
331
332         if (! xulG.fast_add_item) {
333             document.getElementById('fastItemAdd_checkbox').hidden = true;
334         }
335         document.getElementById('fastItemAdd_textboxes').hidden = document.getElementById('fastItemAdd_checkbox').hidden || !document.getElementById('fastItemAdd_checkbox').checked;
336
337         // Only show bib sources for bib records that already exist in the database
338         if (xulG.record.rtype == 'bre' && xulG.record.id) {
339             dojo.require('openils.PermaCrud');
340             var authtoken = ses();
341             // Retrieve the current record attributes
342             var bib = new openils.PermaCrud({"authtoken": authtoken}).retrieve('bre', xulG.record.id);
343
344             // Remember the current bib source of the record
345             xulG.record.bre = bib;
346
347             buildBibSourceList(authtoken, xulG.record.id);
348         }
349
350     } catch(E) {
351         alert('FIXME, MARC Editor, my_init: ' + E);
352     }
353 }
354
355
356 function createComplexHTMLElement (e, attrs, objects, text) {
357     var l = document.createElementNS('http://www.w3.org/1999/xhtml',e);
358
359     if (attrs) {
360         for (var i in attrs) l.setAttribute(i,attrs[i]);
361     }
362
363     if (objects) {
364         for ( var i in objects ) l.appendChild( objects[i] );
365     }
366
367     if (text) {
368         l.appendChild( document.createTextNode(text) )
369     }
370
371     return l;
372 }
373
374 function createComplexXULElement (e, attrs, objects) {
375     var l = document.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',e);
376
377     if (attrs) {
378         for (var i in attrs) {
379             if (typeof attrs[i] == 'function') {
380                 l.addEventListener( i, attrs[i], true );
381             } else {
382                 l.setAttribute(i,attrs[i]);
383             }
384         }
385     } 
386
387     if (objects) {
388         for ( var i in objects ) l.appendChild( objects[i] );
389     }
390
391     return l;
392 }
393
394 function createDescription (attrs) {
395     return createComplexXULElement('description', attrs, Array.prototype.slice.apply(arguments, [1]) );
396 }
397
398 function createTooltip (attrs) {
399     return createComplexXULElement('tooltip', attrs, Array.prototype.slice.apply(arguments, [1]) );
400 }
401
402 function createLabel (attrs) {
403     return createComplexXULElement('label', attrs, Array.prototype.slice.apply(arguments, [1]) );
404 }
405
406 function createVbox (attrs) {
407     return createComplexXULElement('vbox', attrs, Array.prototype.slice.apply(arguments, [1]) );
408 }
409
410 function createHbox (attrs) {
411     return createComplexXULElement('hbox', attrs, Array.prototype.slice.apply(arguments, [1]) );
412 }
413
414 function createRow (attrs) {
415     return createComplexXULElement('row', attrs, Array.prototype.slice.apply(arguments, [1]) );
416 }
417
418 function createTextbox (attrs) {
419     return createComplexXULElement('textbox', attrs, Array.prototype.slice.apply(arguments, [1]) );
420 }
421
422 function createMenu (attrs) {
423     return createComplexXULElement('menu', attrs, Array.prototype.slice.apply(arguments, [1]) );
424 }
425
426 function createMenuPopup (attrs) {
427     return createComplexXULElement('menupopup', attrs, Array.prototype.slice.apply(arguments, [1]) );
428 }
429
430 function createPopup (attrs) {
431     return createComplexXULElement('popup', attrs, Array.prototype.slice.apply(arguments, [1]) );
432 }
433
434 function createMenuitem (attrs) {
435     return createComplexXULElement('menuitem', attrs, Array.prototype.slice.apply(arguments, [1]) );
436 }
437
438 function createCheckbox (attrs) {
439     return createComplexXULElement('checkbox', attrs, Array.prototype.slice.apply(arguments, [1]) );
440 }
441
442 // Find the next textbox that we can use for a focus point
443 // For control fields, use the first editable text box
444 // For data fields, focus on the first subfield text box
445 function setFocusToNextTag (row, direction) {
446     var keep_looking = true;
447     while (keep_looking && (direction == 'up' ? row = row.previousSibling : row = row.nextSibling)) {
448         // Is it a datafield?
449         dojo.query('hbox hbox textbox', row).forEach(function(node, index, arr) {
450             node.focus();
451             keep_looking = false;
452         });
453
454         // No, it's a control field; use the first textbox
455         if (keep_looking) {
456             dojo.query('textbox', row).forEach(function(node, index, arr) {
457                 node.focus();
458                 keep_looking = false;
459             });
460         }
461     }
462
463     return true;
464 }
465
466
467 function createMARCTextbox (element,attrs) {
468
469     var box = createComplexXULElement('textbox', attrs, Array.prototype.slice.apply(arguments, [2]) );
470     box.onkeypress = function (event) {
471         var root_node;
472         var node = element;
473         while(node = node.parent()) {
474             root_node = node;
475         }
476
477         var row = event.target;
478         while (row.tagName != 'row') row = row.parentNode;
479
480         if (element.nodeKind() == 'attribute') element[0]=box.value;
481         else element.setChildren( box.value );
482
483         if (element.localName() != 'controlfield') {
484             if ((event.charCode == 100 || event.charCode == 105) && event.ctrlKey) { // ctrl+d or ctrl+i
485
486                 var index_sf, target, move_data;
487                 if (element.localName() == 'subfield') {
488                     index_sf = element;
489                     target = event.target.parentNode;
490
491                     var start = event.target.selectionStart;
492                     var end = event.target.selectionEnd - event.target.selectionStart ?
493                             event.target.selectionEnd :
494                             event.target.value.length;
495
496                     move_data = event.target.value.substring(start,end);
497                     event.target.value = event.target.value.substring(0,start) + event.target.value.substring(end);
498                     event.target.setAttribute('size', event.target.value.length + 2);
499     
500                     element.setChildren( event.target.value );
501
502                 } else if (element.localName() == 'code') {
503                     index_sf = element.parent();
504                     target = event.target.parentNode;
505                 } else if (element.localName() == 'tag' || element.localName() == 'ind1' || element.localName() == 'ind2') {
506                     index_sf = element.parent().children()[element.parent().children().length() - 1];
507                     target = event.target.parentNode.lastChild.lastChild;
508                 }
509
510                 var sf = <subfield code="" xmlns="http://www.loc.gov/MARC21/slim">{ move_data }</subfield>;
511
512                 index_sf.parent().insertChildAfter( index_sf, sf );
513
514                 var new_sf = marcSubfield(sf);
515
516                 if (target === target.parentNode.lastChild) {
517                     target.parentNode.appendChild( new_sf );
518                 } else {
519                     target.parentNode.insertBefore( new_sf, target.nextSibling );
520                 }
521
522                 new_sf.firstChild.nextSibling.focus();
523
524                 event.preventDefault();
525                 return false;
526
527             } else if (event.keyCode == 13 || event.keyCode == 77) {
528                 if (event.ctrlKey) { // ctrl+enter
529
530                     var index;
531                     if (element.localName() == 'subfield') index = element.parent();
532                     if (element.localName() == 'code') index = element.parent().parent();
533                     if (element.localName() == 'tag') index = element.parent();
534                     if (element.localName() == 'ind1') index = element.parent();
535                     if (element.localName() == 'ind2') index = element.parent();
536
537                     var df = <datafield tag="" ind1="" ind2="" xmlns="http://www.loc.gov/MARC21/slim"><subfield code="" /></datafield>;
538
539                     if (event.shiftKey) { // ctrl+shift+enter
540                         index.parent().insertChildBefore( index, df );
541                     } else {
542                         index.parent().insertChildAfter( index, df );
543                     }
544
545                     var new_df = marcDatafield(df);
546
547                     if (row.parentNode.lastChild === row) {
548                         row.parentNode.appendChild( new_df );
549                     } else {
550                         if (event.shiftKey) { // ctrl+shift+enter
551                             row.parentNode.insertBefore( new_df, row );
552                         } else {
553                             row.parentNode.insertBefore( new_df, row.nextSibling );
554                         }
555                     }
556
557                     new_df.firstChild.focus();
558
559                     event.preventDefault();
560                     return false;
561
562                 } else if (event.shiftKey) {
563                     if (row.previousSibling.className.match('marcDatafieldRow'))
564                         row.previousSibling.firstChild.focus();
565                 } else {
566                     row.nextSibling.firstChild.focus();
567                 }
568
569             } else if (event.keyCode == 38 || event.keyCode == 40) { // up-arrow or down-arrow
570                 if (event.ctrlKey) { // CTRL key: copy the field
571                     var index;
572                     if (element.localName() == 'subfield') index = element.parent();
573                     if (element.localName() == 'code') index = element.parent().parent();
574                     if (element.localName() == 'tag') index = element.parent();
575                     if (element.localName() == 'ind1') index = element.parent();
576                     if (element.localName() == 'ind2') index = element.parent();
577
578                     var copyField = index.copy();
579
580                     if (event.keyCode == 38) { // ctrl+up-arrow
581                         index.parent().insertChildBefore( index, copyField );
582                     } else {
583                         index.parent().insertChildAfter( index, copyField );
584                     }
585
586                     var new_df = marcDatafield(copyField);
587
588                     if (row.parentNode.lastChild === row) {
589                         row.parentNode.appendChild( new_df );
590                     } else {
591                         if (event.keyCode == 38) { // ctrl+up-arrow
592                             row.parentNode.insertBefore( new_df, row );
593                         } else { // ctrl+down-arrow
594                             row.parentNode.insertBefore( new_df, row.nextSibling );
595                         }
596                     }
597
598                     new_df.firstChild.focus();
599
600                     event.preventDefault();
601
602                     return false;
603                 } else {
604                     if (event.keyCode == 38) {
605                         return setFocusToNextTag(row, 'up');
606                     }
607                     if (event.keyCode == 40) {
608                         return setFocusToNextTag(row, 'down');
609                     }
610                     return false;
611                 }
612
613             } else if (event.keyCode == 46 && event.ctrlKey) { // ctrl+del
614
615                 var index;
616                 if (element.localName() == 'subfield') index = element.parent();
617                 if (element.localName() == 'code') index = element.parent().parent();
618                 if (element.localName() == 'tag') index = element.parent();
619                 if (element.localName() == 'ind1') index = element.parent();
620                 if (element.localName() == 'ind2') index = element.parent();
621
622                 for (var i in index.parent().children()) {
623                     if (index === index.parent().children()[i]) {
624                         delete index.parent().children()[i];
625                         break;
626                     }
627                 }
628
629                 row.previousSibling.firstChild.focus();
630                 row.parentNode.removeChild(row);
631
632                 event.preventDefault();
633                 return false;
634
635             } else if (event.keyCode == 46 && event.shiftKey) { // shift+del
636
637                 var index;
638                 if (element.localName() == 'subfield') index = element;
639                 if (element.localName() == 'code') index = element.parent();
640
641                 if (index) {
642                     for (var i in index.parent().children()) {
643                         if (index === index.parent().children()[i]) {
644                             delete index.parent().children()[i];
645                             break;
646                         }
647                     }
648
649                     if (event.target.parentNode === event.target.parentNode.parentNode.lastChild) {
650                         event.target.parentNode.previousSibling.lastChild.focus();
651                     } else {
652                         event.target.parentNode.nextSibling.firstChild.nextSibling.focus();
653                     }
654
655                     event.target.parentNode.parentNode.removeChild(event.target.parentNode);
656
657                     event.preventDefault();
658                     return false;
659                 }
660             } else if (event.keyCode == 64 && event.ctrlKey) { // ctrl + F6
661                 createControlField('006','                                        ');
662                 loadRecord(xml_record);
663             } else if (event.keyCode == 65 && event.ctrlKey) { // ctrl + F7
664                 createControlField('007','                                        ');
665                 loadRecord(xml_record);
666             } else if (event.keyCode == 66 && event.ctrlKey) { // ctrl + F8
667                 createControlField('008','                                        ');
668                 loadRecord(xml_record);
669             }
670
671             return true;
672
673         } else { // event on a control field
674             if (event.keyCode == 38) { 
675                 return setFocusToNextTag(row, 'up'); 
676             } else if (event.keyCode == 40) { 
677                 return setFocusToNextTag(row, 'down');
678             }
679         }
680     };
681
682     box.addEventListener(
683         'keypress', 
684         function () {
685             if (element.nodeKind() == 'attribute') element[0]=box.value;
686             else element.setChildren( box.value );
687             return true;
688         },
689         false
690     );
691
692     box.addEventListener(
693         'change', 
694         function () {
695             if (element.nodeKind() == 'attribute') element[0]=box.value;
696             else element.setChildren( box.value );
697             return true;
698         },
699         false
700     );
701
702     box.addEventListener(
703         'keypress', 
704         function () {
705             if (element.nodeKind() == 'attribute') element[0]=box.value;
706             else element.setChildren( box.value );
707             return true;
708         },
709         true
710     );
711
712     box.addEventListener(
713         'keyup', 
714         function () {
715             if (element.localName() == 'controlfield')
716                 eval('fillFixedFields(xml_record);');
717         },
718         true
719     );
720
721     return box;
722 }
723
724 var rec_type = {
725     BKS : { Type : /[at]{1}/,    BLvl : /[acdm]{1}/ },
726     SER : { Type : /[a]{1}/,    BLvl : /[bs]{1}/ },
727     VIS : { Type : /[gkro]{1}/,    BLvl : /[abcdms]{1}/ },
728     MIX : { Type : /[p]{1}/,    BLvl : /[cd]{1}/ },
729     MAP : { Type : /[ef]{1}/,    BLvl : /[abcdms]{1}/ },
730     SCO : { Type : /[cd]{1}/,    BLvl : /[abcdms]{1}/ },
731     REC : { Type : /[ij]{1}/,    BLvl : /[abcdms]{1}/ },
732     COM : { Type : /[m]{1}/,    BLvl : /[abcdms]{1}/ },
733     AUT : { Type : /[z]{1}/,    BLvl : /.{1}/ },
734     MFHD : { Type : /[uvxy]{1}/,  BLvl : /.{1}/ }
735 };
736
737 var ff_pos = {
738     TrAr : {
739         _8 : {
740             SCO : {start : 33, len : 1, def : ' ' },
741             REC : {start : 33, len : 1, def : 'n' }
742         },
743         _6 : {
744             SCO : {start : 16, len : 1, def : ' ' },
745             REC : {start : 16, len : 1, def : 'n' }
746         }
747     },
748     TMat : {
749         _8 : {
750             VIS : {start : 33, len : 1, def : ' ' }
751         },
752         _6 : {
753             VIS : {start : 16, len : 1, def : ' ' }
754         }
755     },
756     Time : {
757         _8 : {
758             VIS : {start : 18, len : 3, def : ' ' }
759         },
760         _6 : {
761             VIS : {start : 1, len : 3, def : ' ' }
762         }
763     },
764     Tech : {
765         _8 : {
766             VIS : {start : 34, len : 1, def : 'n' }
767         },
768         _6 : {
769             VIS : {start : 17, len : 1, def : 'n' }
770         }
771     },
772     SrTp : {
773         _8 : {
774             SER : {start : 21, len : 1, def : ' ' }
775         },
776         _6 : {
777             SER : {start : 4, len : 1, def : ' ' }
778         }
779     },
780     Srce : {
781         _8 : {
782             BKS : {start : 39, len : 1, def : 'd' },
783             SER : {start : 39, len : 1, def : 'd' },
784             VIS : {start : 39, len : 1, def : 'd' },
785             MIX : {start : 39, len : 1, def : 'd' },
786             MAP : {start : 39, len : 1, def : 'd' },
787             SCO : {start : 39, len : 1, def : 'd' },
788             REC : {start : 39, len : 1, def : 'd' },
789             COM : {start : 39, len : 1, def : 'd' }
790         }
791     },
792     SpFm : {
793         _8 : {
794             MAP : {start : 33, len : 2, def : ' ' }
795         },
796         _6 : {
797             MAP : {start : 16, len : 2, def : ' ' }
798         }
799     },
800     Relf : {
801         _8 : {
802             MAP : {start : 18, len : 4, def : ' ' }
803         },
804         _6 : {
805             MAP : {start : 1, len : 4, def : ' ' }
806         }
807     },
808     Regl : {
809         _8 : {
810             SER : {start : 19, len : 1, def : ' ' }
811         },
812         _6 : {
813             SER : {start : 2, len : 1, def : ' ' }
814         }
815     },
816     Proj : {
817         _8 : {
818             MAP : {start : 22, len : 2, def : ' ' }
819         },
820         _6 : {
821             MAP : {start : 5, len : 2, def : ' ' }
822         }
823     },
824     Part : {
825         _8 : {
826             SCO : {start : 21, len : 1, def : ' ' },
827             REC : {start : 21, len : 1, def : 'n' }
828         },
829         _6 : {
830             SCO : {start : 4, len : 1, def : ' ' },
831             REC : {start : 4, len : 1, def : 'n' }
832         }
833     },
834     Orig : {
835         _8 : {
836             SER : {start : 22, len : 1, def : ' ' }
837         },
838         _6 : {
839             SER : {start : 5, len : 1, def : ' ' }
840         }
841     },
842     LTxt : {
843         _8 : {
844             SCO : {start : 30, len : 2, def : ' ' },
845             REC : {start : 30, len : 2, def : ' ' }
846         },
847         _6 : {
848             SCO : {start : 13, len : 2, def : ' ' },
849             REC : {start : 13, len : 2, def : ' ' }
850         }
851     },
852     Freq : {
853         _8 : {
854             SER : {start : 18, len : 1, def : ' ' }
855         },
856         _6 : {
857             SER : {start : 1, len : 1, def : ' ' }
858         }
859     },
860     FMus : {
861         _8 : {
862             SCO : {start : 20, len : 1, def : ' ' },
863             REC : {start : 20, len : 1, def : 'n' }
864         },
865         _6 : {
866             SCO : {start : 3, len : 1, def : ' ' },
867             REC : {start : 3, len : 1, def : 'n' }
868         }
869     },
870     File : {
871         _8 : {
872             COM : {start : 26, len : 1, def : 'u' }
873         },
874         _6 : {
875             COM : {start : 9, len : 1, def : 'u' }
876         }
877     },
878     EntW : {
879         _8 : {
880             SER : {start : 24, len : 1, def : ' ' }
881         },
882         _6 : {
883             SER : {start : 7, len : 1, def : ' ' }
884         }
885     },
886     AccM : {
887         _8 : {
888             SCO : {start : 24, len : 6, def : ' ' },
889             REC : {start : 24, len : 6, def : ' ' }
890         },
891         _6 : {
892             SCO : {start : 7, len : 6, def : ' ' },
893             REC : {start : 7, len : 6, def : ' ' }
894         }
895     },
896     Comp : {
897         _8 : {
898             SCO : {start : 18, len : 2, def : ' ' },
899             REC : {start : 18, len : 2, def : ' ' }
900         },
901         _6 : {
902             SCO : {start : 1, len : 2, def : ' ' },
903             REC : {start : 1, len : 2, def : ' ' }
904         }
905     },
906     CrTp : {
907         _8 : {
908             MAP : {start : 25, len : 1, def : ' ' }
909         },
910         _6 : {
911             MAP : {start : 8, len : 1, def : ' ' }
912         }
913     },
914     Ctry : {
915         _8 : {
916             BKS : {start : 15, len : 3, def : ' ' },
917             SER : {start : 15, len : 3, def : ' ' },
918             VIS : {start : 15, len : 3, def : ' ' },
919             MIX : {start : 15, len : 3, def : ' ' },
920             MAP : {start : 15, len : 3, def : ' ' },
921             SCO : {start : 15, len : 3, def : ' ' },
922             REC : {start : 15, len : 3, def : ' ' },
923             COM : {start : 15, len : 3, def : ' ' }
924         }
925     },
926     Lang : {
927         _8 : {
928             BKS : {start : 35, len : 3, def : ' ' },
929             SER : {start : 35, len : 3, def : ' ' },
930             VIS : {start : 35, len : 3, def : ' ' },
931             MIX : {start : 35, len : 3, def : ' ' },
932             MAP : {start : 35, len : 3, def : ' ' },
933             SCO : {start : 35, len : 3, def : ' ' },
934             REC : {start : 35, len : 3, def : ' ' },
935             COM : {start : 35, len : 3, def : ' ' }
936         }
937     },
938     MRec : {
939         _8 : {
940             BKS : {start : 38, len : 1, def : ' ' },
941             SER : {start : 38, len : 1, def : ' ' },
942             VIS : {start : 38, len : 1, def : ' ' },
943             MIX : {start : 38, len : 1, def : ' ' },
944             MAP : {start : 38, len : 1, def : ' ' },
945             SCO : {start : 38, len : 1, def : ' ' },
946             REC : {start : 38, len : 1, def : ' ' },
947             COM : {start : 38, len : 1, def : ' ' }
948         }
949     },
950     DtSt : {
951         _8 : {
952             BKS : {start : 6, len : 1, def : ' ' },
953             SER : {start : 6, len : 1, def : 'c' },
954             VIS : {start : 6, len : 1, def : ' ' },
955             MIX : {start : 6, len : 1, def : ' ' },
956             MAP : {start : 6, len : 1, def : ' ' },
957             SCO : {start : 6, len : 1, def : ' ' },
958             REC : {start : 6, len : 1, def : ' ' },
959             COM : {start : 6, len : 1, def : ' ' }
960         }
961     },
962     Type : {
963         ldr : {
964             BKS : {start : 6, len : 1, def : 'a' },
965             SER : {start : 6, len : 1, def : 'a' },
966             VIS : {start : 6, len : 1, def : 'g' },
967             MIX : {start : 6, len : 1, def : 'p' },
968             MAP : {start : 6, len : 1, def : 'e' },
969             SCO : {start : 6, len : 1, def : 'c' },
970             REC : {start : 6, len : 1, def : 'i' },
971             COM : {start : 6, len : 1, def : 'm' },
972             AUT : {start : 6, len : 1, def : 'z' },
973             MFHD : {start : 6, len : 1, def : 'y' }
974         }
975     },
976     Ctrl : {
977         ldr : {
978             BKS : {start : 8, len : 1, def : ' ' },
979             SER : {start : 8, len : 1, def : ' ' },
980             VIS : {start : 8, len : 1, def : ' ' },
981             MIX : {start : 8, len : 1, def : ' ' },
982             MAP : {start : 8, len : 1, def : ' ' },
983             SCO : {start : 8, len : 1, def : ' ' },
984             REC : {start : 8, len : 1, def : ' ' },
985             COM : {start : 8, len : 1, def : ' ' }
986         }
987     },
988     BLvl : {
989         ldr : {
990             BKS : {start : 7, len : 1, def : 'm' },
991             SER : {start : 7, len : 1, def : 's' },
992             VIS : {start : 7, len : 1, def : 'm' },
993             MIX : {start : 7, len : 1, def : 'c' },
994             MAP : {start : 7, len : 1, def : 'm' },
995             SCO : {start : 7, len : 1, def : 'm' },
996             REC : {start : 7, len : 1, def : 'm' },
997             COM : {start : 7, len : 1, def : 'm' }
998         }
999     },
1000     Desc : {
1001         ldr : {
1002             BKS : {start : 18, len : 1, def : ' ' },
1003             SER : {start : 18, len : 1, def : ' ' },
1004             VIS : {start : 18, len : 1, def : ' ' },
1005             MIX : {start : 18, len : 1, def : ' ' },
1006             MAP : {start : 18, len : 1, def : ' ' },
1007             SCO : {start : 18, len : 1, def : ' ' },
1008             REC : {start : 18, len : 1, def : ' ' },
1009             COM : {start : 18, len : 1, def : 'i' }
1010         }
1011     },
1012     Item : {
1013         ldr : {
1014             MFHD : {start : 18, len : 1, def : 'i' }
1015         }
1016     },
1017     ELvl : {
1018         ldr : {
1019             BKS : {start : 17, len : 1, def : ' ' },
1020             SER : {start : 17, len : 1, def : ' ' },
1021             VIS : {start : 17, len : 1, def : ' ' },
1022             MIX : {start : 17, len : 1, def : ' ' },
1023             MAP : {start : 17, len : 1, def : ' ' },
1024             SCO : {start : 17, len : 1, def : ' ' },
1025             REC : {start : 17, len : 1, def : ' ' },
1026             COM : {start : 17, len : 1, def : ' ' },
1027             AUT : {start : 17, len : 1, def : 'n' },
1028             MFHD : {start : 17, len : 1, def : 'u' }
1029         }
1030     },
1031     Indx : {
1032         _8 : {
1033             BKS : {start : 31, len : 1, def : '0' },
1034             MAP : {start : 31, len : 1, def : '0' }
1035         },
1036         _6 : {
1037             BKS : {start : 14, len : 1, def : '0' },
1038             MAP : {start : 14, len : 1, def : '0' }
1039         }
1040     },
1041     Date1 : {
1042         _8 : {
1043             BKS : {start : 7, len : 4, def : ' ' },
1044             SER : {start : 7, len : 4, def : ' ' },
1045             VIS : {start : 7, len : 4, def : ' ' },
1046             MIX : {start : 7, len : 4, def : ' ' },
1047             MAP : {start : 7, len : 4, def : ' ' },
1048             SCO : {start : 7, len : 4, def : ' ' },
1049             REC : {start : 7, len : 4, def : ' ' },
1050             COM : {start : 7, len : 4, def : ' ' }
1051         }
1052     },
1053     Date2 : {
1054         _8 : {
1055             BKS : {start : 11, len : 4, def : ' ' },
1056             SER : {start : 11, len : 4, def : '9' },
1057             VIS : {start : 11, len : 4, def : ' ' },
1058             MIX : {start : 11, len : 4, def : ' ' },
1059             MAP : {start : 11, len : 4, def : ' ' },
1060             SCO : {start : 11, len : 4, def : ' ' },
1061             REC : {start : 11, len : 4, def : ' ' },
1062             COM : {start : 11, len : 4, def : ' ' }
1063         }
1064     },
1065     LitF : {
1066         _8 : {
1067             BKS : {start : 33, len : 1, def : '0' }
1068         },
1069         _6 : {
1070             BKS : {start : 16, len : 1, def : '0' }
1071         }
1072     },
1073     Biog : {
1074         _8 : {
1075             BKS : {start : 34, len : 1, def : ' ' }
1076         },
1077         _6 : {
1078             BKS : {start : 17, len : 1, def : ' ' }
1079         }
1080     },
1081     Ills : {
1082         _8 : {
1083             BKS : {start : 18, len : 4, def : ' ' }
1084         },
1085         _6 : {
1086             BKS : {start : 1, len : 4, def : ' ' }
1087         }
1088     },
1089     Fest : {
1090         _8 : {
1091             BKS : {start : 30, len : 1, def : '0' }
1092         },
1093         _6 : {
1094             BKS : {start : 13, len : 1, def : '0' }
1095         }
1096     },
1097     Conf : {
1098         _8 : {
1099             BKS : {start : 29, len : 1, def : '0' },
1100             SER : {start : 29, len : 1, def : '0' }
1101         },
1102         _6 : {
1103             BKS : {start : 12, len : 1, def : '0' },
1104             SER : {start : 12, len : 1, def : '0' }
1105         }
1106     },
1107     Cont : {
1108         _8 : {
1109             BKS : {start : 24, len : 4, def : ' ' },
1110             SER : {start : 25, len : 3, def : ' ' }
1111         },
1112         _6 : {
1113             BKS : {start : 7, len : 4, def : ' ' },
1114             SER : {start : 8, len : 3, def : ' ' }
1115         }
1116     },
1117     GPub : {
1118         _8 : {
1119             BKS : {start : 28, len : 1, def : ' ' },
1120             SER : {start : 28, len : 1, def : ' ' },
1121             VIS : {start : 28, len : 1, def : ' ' },
1122             MAP : {start : 28, len : 1, def : ' ' },
1123             COM : {start : 28, len : 1, def : ' ' }
1124         },
1125         _6 : {
1126             BKS : {start : 11, len : 1, def : ' ' },
1127             SER : {start : 11, len : 1, def : ' ' },
1128             VIS : {start : 11, len : 1, def : ' ' },
1129             MAP : {start : 11, len : 1, def : ' ' },
1130             COM : {start : 11, len : 1, def : ' ' }
1131         }
1132     },
1133     Audn : {
1134         _8 : {
1135             BKS : {start : 22, len : 1, def : ' ' },
1136             SER : {start : 22, len : 1, def : ' ' },
1137             VIS : {start : 22, len : 1, def : ' ' },
1138             SCO : {start : 22, len : 1, def : ' ' },
1139             REC : {start : 22, len : 1, def : ' ' },
1140             COM : {start : 22, len : 1, def : ' ' }
1141         },
1142         _6 : {
1143             BKS : {start : 5, len : 1, def : ' ' },
1144             SER : {start : 5, len : 1, def : ' ' },
1145             VIS : {start : 5, len : 1, def : ' ' },
1146             SCO : {start : 5, len : 1, def : ' ' },
1147             REC : {start : 5, len : 1, def : ' ' },
1148             COM : {start : 5, len : 1, def : ' ' }
1149         }
1150     },
1151     Form : {
1152         _8 : {
1153             BKS : {start : 23, len : 1, def : ' ' },
1154             SER : {start : 23, len : 1, def : ' ' },
1155             VIS : {start : 29, len : 1, def : ' ' },
1156             MIX : {start : 23, len : 1, def : ' ' },
1157             MAP : {start : 29, len : 1, def : ' ' },
1158             SCO : {start : 23, len : 1, def : ' ' },
1159             REC : {start : 23, len : 1, def : ' ' }
1160         },
1161         _6 : {
1162             BKS : {start : 6, len : 1, def : ' ' },
1163             SER : {start : 6, len : 1, def : ' ' },
1164             VIS : {start : 12, len : 1, def : ' ' },
1165             MIX : {start : 6, len : 1, def : ' ' },
1166             MAP : {start : 12, len : 1, def : ' ' },
1167             SCO : {start : 6, len : 1, def : ' ' },
1168             REC : {start : 6, len : 1, def : ' ' }
1169         }
1170     },
1171     'S/L' : {
1172         _8 : {
1173             SER : {start : 34, len : 1, def : '0' }
1174         },
1175         _6 : {
1176             SER : {start : 17, len : 1, def : '0' }
1177         }
1178     },
1179     'Alph' : {
1180         _8 : {
1181             SER : {start : 33, len : 1, def : ' ' }
1182         },
1183         _6 : {
1184             SER : {start : 16, len : 1, def : ' ' }
1185         }
1186     },
1187     "GeoDiv" : {
1188         "_8" : {
1189             "AUT" : {"start" : 6, "len" : 1, "def" : ' ' }
1190         }
1191     },
1192     "Roman" : {
1193         "_8" : {
1194             "AUT" : {"start" : 7, "len" : 1, "def" : ' ' }
1195         }
1196     },
1197     "CatLang" : {
1198         "_8" : {
1199             "AUT" : {"start" : 8, "len" : 1, "def" : ' ' }
1200         }
1201     },
1202     "Kind" : {
1203         "_8" : {
1204             "AUT" : {"start" : 9, "len" : 1, "def" : ' ' }
1205         }
1206     },
1207     "Rules" : {
1208         "_8" : {
1209             "AUT" : {"start" : 10, "len" : 1, "def" : ' ' }
1210         }
1211     },
1212     "SHSys" : {
1213         "_8" : {
1214             "AUT" : {"start" : 11, "len" : 1, "def" : ' ' }
1215         }
1216     },
1217     "SerType" : {
1218         "_8" : {
1219             "AUT" : {"start" : 12, "len" : 1, "def" : ' ' }
1220         }
1221     },
1222     "SerNum" : {
1223         "_8" : {
1224             "AUT" : {"start" : 13, "len" : 1, "def" : ' ' }
1225         }
1226     },
1227     "HeadMain" : {
1228         "_8" : {
1229             "AUT" : {"start" : 14, "len" : 1, "def" : ' ' }
1230         }
1231     },
1232     "HeadSubj" : {
1233         "_8" : {
1234             "AUT" : {"start" : 15, "len" : 1, "def" : ' ' }
1235         }
1236     },
1237     "HeadSer" : {
1238         "_8" : {
1239             "AUT" : {"start" : 16, "len" : 1, "def" : ' ' }
1240         }
1241     },
1242     "TypeSubd" : {
1243         "_8" : {
1244             "AUT" : {"start" : 17, "len" : 1, "def" : ' ' }
1245         }
1246     },
1247     "TypeGov" : {
1248         "_8" : {
1249             "AUT" : {"start" : 28, "len" : 1, "def" : ' ' }
1250         }
1251     },
1252     "RefEval" : {
1253         "_8" : {
1254             "AUT" : {"start" : 29, "len" : 1, "def" : ' ' }
1255         }
1256     },
1257     "RecUpd" : {
1258         "_8" : {
1259             "AUT" : {"start" : 31, "len" : 1, "def" : ' ' }
1260         }
1261     },
1262     "NameDiff" : {
1263         "_8" : {
1264             "AUT" : {"start" : 32, "len" : 1, "def" : ' ' }
1265         }
1266     },
1267     "Level" : {
1268         "_8" : {
1269             "AUT" : {"start" : 33, "len" : 1, "def" : ' ' }
1270         }
1271     },
1272     "ModRec" : {
1273         "_8" : {
1274             "AUT" : {"start" : 38, "len" : 1, "def" : ' ' }
1275         }
1276     },
1277     "CatSrc" : {
1278         "_8" : {
1279             "AUT" : {"start" : 39, "len" : 1, "def" : ' ' }
1280         }
1281     }
1282 };
1283
1284 function recordType (rec) {
1285     try {
1286         var _l = rec.leader.toString();
1287
1288         var _t = _l.substr(ff_pos.Type.ldr.BKS.start, ff_pos.Type.ldr.BKS.len);
1289         var _b = _l.substr(ff_pos.BLvl.ldr.BKS.start, ff_pos.BLvl.ldr.BKS.len);
1290
1291         for (var t in rec_type) {
1292             if (_t.match(rec_type[t].Type) && _b.match(rec_type[t].BLvl)) {
1293                 document.getElementById('recordTypeLabel').value = t;
1294                 _record_type = t;
1295                 return t;
1296             }
1297         }
1298
1299         // in case we don't have a valid record type ...
1300         _record_type = 'BKS';
1301         return _record_type;
1302
1303     } catch(E) {
1304         alert('FIXME, MARC Editor, recordType: ' + E);
1305     }
1306 }
1307
1308 function toggleFFE () {
1309     var grid = document.getElementById('leaderGrid');
1310     if (grid.hidden) {
1311         grid.hidden = false;
1312     } else {
1313         grid.hidden = true;
1314     }
1315     return true;
1316 }
1317
1318 function changeFFEditor (type) {
1319     var grid = document.getElementById('leaderGrid');
1320     grid.setAttribute('type',type);
1321
1322     // Hide FFEditor rows that we don't need for our current type
1323     // If all of the labels for a given row do not include our
1324     // desired type in their set attribute, we can hide that row
1325     dojo.query('rows row', grid).forEach(function(node, index, arr) {
1326         if (dojo.query('label[set~=' + type + ']', node).length == 0) {
1327             node.hidden = true;
1328         }
1329     });
1330
1331 }
1332
1333 function fillFixedFields (rec) {
1334     try {
1335             var grid = document.getElementById('leaderGrid');
1336
1337             var rtype = _record_type;
1338
1339             var _l = rec.leader.toString();
1340             var _6 = rec.controlfield.(@tag=='006').toString();
1341             var _7 = rec.controlfield.(@tag=='007').toString();
1342             var _8 = rec.controlfield.(@tag=='008').toString();
1343
1344             var list = [];
1345             var pre_list = grid.getElementsByTagName('label');
1346             for (var i in pre_list) {
1347                 if ( pre_list[i].getAttribute && pre_list[i].getAttribute('set').indexOf(grid.getAttribute('type')) > -1 ) {
1348                     list.push( pre_list[i] );
1349                 }
1350             }
1351
1352             for (var i in list) {
1353                 var name = list[i].getAttribute('name');
1354
1355                 if (!ff_pos[name])
1356                     continue;
1357
1358                 var value = '';
1359                 if ( ff_pos[name].ldr && ff_pos[name].ldr[rtype] )
1360                     value = _l.substr(ff_pos[name].ldr[rtype].start, ff_pos[name].ldr[rtype].len);
1361
1362                 if ( ff_pos[name]._8 && ff_pos[name]._8[rtype] )
1363                     value = _8.substr(ff_pos[name]._8[rtype].start, ff_pos[name]._8[rtype].len);
1364
1365                 if ( !value && ff_pos[name]._6 && ff_pos[name]._6[rtype] )
1366                     value = _6.substr(ff_pos[name]._6[rtype].start, ff_pos[name]._6[rtype].len);
1367
1368                 if ( ff_pos[name]._7 && ff_pos[name]._7[rtype] )
1369                     value = _7.substr(ff_pos[name]._7[rtype].start, ff_pos[name]._7[rtype].len);
1370                 
1371                 if (!value) {
1372                     var d;
1373                     var p;
1374                     if (ff_pos[name].ldr && ff_pos[name].ldr[rtype]) {
1375                         d = ff_pos[name].ldr[rtype].def;
1376                         p = 'ldr';
1377                     }
1378
1379                     if (ff_pos[name]._8 && ff_pos[name]._8[rtype]) {
1380                         d = ff_pos[name]._8[rtype].def;
1381                         p = '_8';
1382                     }
1383
1384                     if (!value && ff_pos[name]._6 && ff_pos[name]._6[rtype]) {
1385                         d = ff_pos[name]._6[rtype].def;
1386                         p = '_6';
1387                     }
1388
1389                     if (ff_pos[name]._7 && ff_pos[name]._7[rtype]) {
1390                         d = ff_pos[name]._7[rtype].def;
1391                         p = '_7';
1392                     }
1393
1394                     if (p && !value) {
1395                         for (var j = 0; j < ff_pos[name][p][rtype].len; j++) {
1396                             value += d;
1397                         }
1398                     }
1399                 }
1400
1401                 list[i].nextSibling.value = value;
1402             }
1403
1404             return true;
1405     } catch(E) {
1406         alert('FIXME, MARC Editor, fillFixedFields: ' + E);
1407     }
1408 }
1409
1410 function updateFixedFields (element) {
1411     var grid = document.getElementById('leaderGrid');
1412     var recGrid = document.getElementById('recGrid');
1413
1414     var rtype = _record_type;
1415     var new_value = element.value;
1416
1417     var parts = {
1418         ldr : _record.leader,
1419         _6 : _record.controlfield.(@tag=='006'),
1420         _7 : _record.controlfield.(@tag=='007'),
1421         _8 : _record.controlfield.(@tag=='008')
1422     };
1423
1424     var name = element.getAttribute('name');
1425     for (var i in ff_pos[name]) {
1426
1427         if (!ff_pos[name][i][rtype]) continue;
1428         if (!parts[i]) {
1429             // we're missing the required field.  Add it now.
1430
1431             var newfield;
1432             if (i == '_6') newfield = '006';
1433             else if (i == '_7') newfield = '007';
1434             else if (i == '_8') newfield = '008';
1435             else continue;
1436
1437             createControlField(newfield,'                                        ');
1438             parts[i] = _record.controlfield.(@tag==newfield);
1439         }
1440
1441         var before = parts[i].substr(0, ff_pos[name][i][rtype].start);
1442         var after = parts[i].substr(ff_pos[name][i][rtype].start + ff_pos[name][i][rtype].len);
1443
1444         for (var j = 0; new_value.length < ff_pos[name][i][rtype].len; j++) {
1445             new_value += ff_pos[name][i][rtype].def;
1446         }
1447
1448         parts[i].setChildren( before + new_value + after );
1449         recGrid.getElementsByAttribute('tag',i)[0].lastChild.value = parts[i].toString();
1450     }
1451
1452     return true;
1453 }
1454
1455 function marcLeader (leader) {
1456     var row = createRow(
1457         { class : 'marcLeaderRow',
1458           tag : 'ldr' },
1459         createLabel(
1460             { value : 'LDR',
1461               class : 'marcTag',
1462               tooltiptext : $('catStrings').getString('staff.cat.marcedit.marcTag.LDR.label') } ),
1463         createLabel(
1464             { value : '',
1465               class : 'marcInd1' } ),
1466         createLabel(
1467             { value : '',
1468               class : 'marcInd2' } ),
1469         createLabel(
1470             { value : leader.text(),
1471               class : 'marcLeader' } )
1472     );
1473
1474     return row;
1475 }
1476
1477 function marcControlfield (field) {
1478     tagname = field.@tag.toString().substr(2);
1479     var row;
1480     if (tagname == '1' || tagname == '3' || tagname == '6' || tagname == '7' || tagname == '8') {
1481         row = createRow(
1482             { class : 'marcControlfieldRow',
1483               tag : '_' + tagname },
1484             createLabel(
1485                 { value : field.@tag,
1486                   class : 'marcTag',
1487                   context : 'tags_popup',
1488                   onmouseover : 'getTooltip(this, "tag");',
1489                   tooltipid : 'tag' + field.@tag } ),
1490             createLabel(
1491                 { value : field.@ind1,
1492                   class : 'marcInd1',
1493                   onmouseover : 'getTooltip(this, "ind1");',
1494                   tooltipid : 'tag' + field.@tag + 'ind1val' + field.@ind1 } ),
1495             createLabel(
1496                 { value : field.@ind2,
1497                   class : 'marcInd2',
1498                   onmouseover : 'getTooltip(this, "ind2");',
1499                   tooltipid : 'tag' + field.@tag + 'ind2val' + field.@ind2 } ),
1500             createMARCTextbox(
1501                 field,
1502                 { value : field.text(),
1503                   class : 'plain marcEditableControlfield',
1504                   name : 'CONTROL' + tagname,
1505                   context : 'clipboard',
1506                   size : 50,
1507                   maxlength : 50 } )
1508             );
1509     } else {
1510         row = createRow(
1511             { class : 'marcControlfieldRow',
1512               tag : '_' + tagname },
1513             createLabel(
1514                 { value : field.@tag,
1515                   class : 'marcTag',
1516                   onmouseover : 'getTooltip(this, "tag");',
1517                   tooltipid : 'tag' + field.@tag } ),
1518             createLabel(
1519                 { value : field.@ind1,
1520                   class : 'marcInd1',
1521                   onmouseover : 'getTooltip(this, "ind1");',
1522                   tooltipid : 'tag' + field.@tag + 'ind1val' + field.@ind1 } ),
1523             createLabel(
1524                 { value : field.@ind2,
1525                   class : 'marcInd2',
1526                   onmouseover : 'getTooltip(this, "ind2");',
1527                   tooltipid : 'tag' + field.@tag + 'ind2val' + field.@ind2 } ),
1528             createLabel(
1529                 { value : field.text(),
1530                   class : 'marcControlfield' } )
1531         );
1532     }
1533
1534     return row;
1535 }
1536
1537 function stackSubfields(checkbox) {
1538     var list = document.getElementsByAttribute('name','sf_box');
1539
1540     var o = 'vertical';
1541     if (!checkbox.checked) o = 'horizontal';
1542     
1543     for (var i = 0; i < list.length; i++) {
1544         if (list[i]) list[i].setAttribute('orient',o);
1545     }
1546 }
1547
1548 function fastItemAdd_toggle(checkbox) {
1549     var x = document.getElementById('fastItemAdd_textboxes');
1550     if (checkbox.checked) {
1551         x.hidden = false;
1552         document.getElementById('fastItemAdd_callnumber').focus();
1553         document.getElementById('fastItemAdd_callnumber').select();
1554     } else {
1555         x.hidden = true;
1556     }
1557 }
1558
1559 function fastItemAdd_attempt(doc_id) {
1560     try {
1561         if (typeof window.xulG.fast_add_item != 'function') { return; }
1562         if (!document.getElementById('fastItemAdd_checkbox').checked) { return; }
1563         if (!document.getElementById('fastItemAdd_callnumber').value) { return; }
1564         if (!document.getElementById('fastItemAdd_barcode').value) { return; }
1565         window.xulG.fast_add_item( doc_id, document.getElementById('fastItemAdd_callnumber').value, document.getElementById('fastItemAdd_barcode').value );
1566         document.getElementById('fastItemAdd_barcode').value = '';
1567     } catch(E) {
1568         alert('fastItemAdd_attempt: ' + E);
1569     }
1570 }
1571
1572 function save_attempt(xml_string) {
1573     try {
1574         var result = window.xulG.save.func( xml_string );   
1575         if (result) {
1576             if (result.id) fastItemAdd_attempt(result.id);
1577             if (typeof result.on_complete == 'function') result.on_complete();
1578         }
1579     } catch(E) {
1580         alert('save_attempt: ' + E);
1581     }
1582 }
1583
1584 function marcDatafield (field) {
1585     var row = createRow(
1586         { class : 'marcDatafieldRow' },
1587         createMARCTextbox(
1588             field.@tag,
1589             { value : field.@tag,
1590               class : 'plain marcTag',
1591               name : 'marcTag',
1592               context : 'tags_popup',
1593               oninput : 'if (this.value.length == 3) { this.nextSibling.focus(); }',
1594               size : 3,
1595               maxlength : 3,
1596               onmouseover : 'current_focus = this; getTooltip(this, "tag");' } ),
1597         createMARCTextbox(
1598             field.@ind1,
1599             { value : field.@ind1,
1600               class : 'plain marcInd1',
1601               name : 'marcInd1',
1602               oninput : 'if (this.value.length == 1) { this.nextSibling.focus(); }',
1603               size : 1,
1604               maxlength : 1,
1605               onmouseover : 'current_focus = this; getContextMenu(this, "ind1"); getTooltip(this, "ind1");',
1606               oncontextmenu : 'getContextMenu(this, "ind1");' } ),
1607         createMARCTextbox(
1608             field.@ind2,
1609             { value : field.@ind2,
1610               class : 'plain marcInd2',
1611               name : 'marcInd2',
1612               oninput : 'if (this.value.length == 1) { this.nextSibling.firstChild.firstChild.focus(); }',
1613               size : 1,
1614               maxlength : 1,
1615               onmouseover : 'current_focus = this; getContextMenu(this, "ind2"); getTooltip(this, "ind2");',
1616               oncontextmenu : 'getContextMenu(this, "ind2");' } ),
1617         createHbox({ name : 'sf_box' })
1618     );
1619
1620     if (!current_focus && field.@tag == '') current_focus = row.childNodes[0];
1621     if (!current_focus && field.@ind1 == '') current_focus = row.childNodes[1];
1622     if (!current_focus && field.@ind2 == '') current_focus = row.childNodes[2];
1623
1624     var sf_box = row.lastChild;
1625     if (document.getElementById('stackSubfields').checked)
1626         sf_box.setAttribute('orient','vertical');
1627
1628     sf_box.addEventListener(
1629         'click',
1630         function (e) {
1631             if (sf_box === e.target) {
1632                 sf_box.lastChild.lastChild.focus();
1633             } else if (e.target.parentNode === sf_box) {
1634                 e.target.lastChild.focus();
1635             }
1636         },
1637         false
1638     );
1639
1640
1641     for (var i in field.subfield) {
1642         var sf = field.subfield[i];
1643         sf_box.appendChild(
1644             marcSubfield(sf)
1645         );
1646
1647         dojo.query('.marcSubfield', sf_box).forEach(wrap_long_fields);
1648
1649         if (sf.@code == '' && (!current_focus || current_focus.className.match(/Ind/)))
1650             current_focus = sf_box.lastChild.childNodes[1];
1651     }
1652
1653     return row;
1654 }
1655
1656 function marcSubfield (sf) {            
1657     return createHbox(
1658         { class : 'marcSubfieldBox' },
1659         createLabel(
1660             { value : "\u2021",
1661               class : 'plain marcSubfieldDelimiter',
1662               onmouseover : 'getTooltip(this.nextSibling, "subfield");',
1663               oncontextmenu : 'getContextMenu(this.nextSibling, "subfield");',
1664                 //onclick : 'this.nextSibling.focus();',
1665                 onfocus : 'this.nextSibling.focus();',
1666               size : 2 } ),
1667         createMARCTextbox(
1668             sf.@code,
1669             { value : sf.@code,
1670               class : 'plain marcSubfieldCode',
1671               name : 'marcSubfieldCode',
1672               onmouseover : 'current_focus = this; getContextMenu(this, "subfield"); getTooltip(this, "subfield");',
1673               oncontextmenu : 'getContextMenu(this, "subfield");',
1674               oninput : 'if (this.value.length == 1) { this.nextSibling.focus(); }',
1675               size : 2,
1676               maxlength : 1 } ),
1677         createMARCTextbox(
1678             sf,
1679             { value : sf.text(),
1680               name : sf.parent().@tag + ':' + sf.@code,
1681               class : 'plain marcSubfield', 
1682               onmouseover : 'getTooltip(this, "subfield");',
1683               contextmenu : function (event) { getAuthorityContextMenu(event.target, sf) },
1684               size : new String(sf.text()).length + 2,
1685               oninput : "this.setAttribute('size', this.value.length + 2);"
1686             } )
1687     );
1688 }
1689
1690 function loadRecord(rec) {
1691     try {
1692             _record = rec;
1693             var grid_rows = document.getElementById('recGrid').lastChild;
1694
1695             while (grid_rows.firstChild) grid_rows.removeChild(grid_rows.firstChild);
1696
1697             grid_rows.appendChild( marcLeader( rec.leader ) );
1698
1699             for (var i in rec.controlfield) {
1700                 grid_rows.appendChild( marcControlfield( rec.controlfield[i] ) );
1701             }
1702
1703             for (var i in rec.datafield) {
1704                 grid_rows.appendChild( marcDatafield( rec.datafield[i] ) );
1705             }
1706
1707             grid_rows.getElementsByAttribute('class','marcDatafieldRow')[0].firstChild.focus();
1708             changeFFEditor(recordType(rec));
1709             fillFixedFields(rec);
1710     } catch(E) {
1711         alert('FIXME, MARC Editor, loadRecord: ' + E);
1712     }
1713 }
1714
1715
1716 function genToolTips () {
1717     for (var i in bib_data.field) {
1718         var f = bib_data.field[i];
1719     
1720         tag_menu.appendChild(
1721             createMenuitem(
1722                 { label : f.@tag,
1723                   oncommand : 
1724                       'current_focus.value = "' + f.@tag + '";' +
1725                     'var e = document.createEvent("MutationEvents");' +
1726                     'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1727                     'current_focus.inputField.dispatchEvent(e);',
1728                   disabled : f.@tag < '010' ? "true" : "false",
1729                   tooltiptext : f.description }
1730             )
1731         );
1732     
1733         var i1_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'i1' });
1734         context_menus.appendChild( i1_popup );
1735     
1736         var i2_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'i2' });
1737         context_menus.appendChild( i2_popup );
1738     
1739         var sf_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'sf' });
1740         context_menus.appendChild( sf_popup );
1741     
1742         tooltip_hash['tag' + f.@tag] = f.description;
1743         for (var j in f.indicator) {
1744             var ind = f.indicator[j];
1745             tooltip_hash['tag' + f.@tag + 'ind' + ind.@position + 'val' + ind.@value] = ind.description;
1746     
1747             if (ind.@position == 1) {
1748                 i1_popup.appendChild(
1749                     createMenuitem(
1750                         { label : ind.@value,
1751                           oncommand : 
1752                               'current_focus.value = "' + ind.@value + '";' +
1753                             'var e = document.createEvent("MutationEvents");' +
1754                             'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1755                             'current_focus.inputField.dispatchEvent(e);',
1756                           tooltiptext : ind.description }
1757                     )
1758                 );
1759             }
1760     
1761             if (ind.@position == 2) {
1762                 i2_popup.appendChild(
1763                     createMenuitem(
1764                         { label : ind.@value,
1765                           oncommand : 
1766                               'current_focus.value = "' + ind.@value + '";' +
1767                             'var e = document.createEvent("MutationEvents");' +
1768                             'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1769                             'current_focus.inputField.dispatchEvent(e);',
1770                           tooltiptext : ind.description }
1771                     )
1772                 );
1773             }
1774         }
1775     
1776         for (var j in f.subfield) {
1777             var sf = f.subfield[j];
1778             tooltip_hash['tag' + f.@tag + 'sf' + sf.@code] = sf.description;
1779     
1780             sf_popup.appendChild(
1781                 createMenuitem(
1782                     { label : sf.@code,
1783                       oncommand : 
1784                           'current_focus.value = "' + sf.@code + '";' +
1785                         'var e = document.createEvent("MutationEvents");' +
1786                         'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1787                         'current_focus.inputField.dispatchEvent(e);',
1788                       tooltiptext : sf.description
1789                     }
1790                 )
1791             );
1792         }
1793     }
1794 }
1795
1796 function getTooltip (target, type) {
1797
1798     var tt = '';
1799     if (type == 'subfield')
1800         tt = 'tag' + target.parentNode.parentNode.parentNode.firstChild.value + 'sf' + target.parentNode.childNodes[1].value;
1801
1802     if (type == 'ind1')
1803         tt = 'tag' + target.parentNode.firstChild.value + 'ind1val' + target.value;
1804
1805     if (type == 'ind2')
1806         tt = 'tag' + target.parentNode.firstChild.value + 'ind2val' + target.value;
1807
1808     if (type == 'tag')
1809         tt = 'tag' + target.parentNode.firstChild.value;
1810
1811     if (!document.getElementById( tt )) {
1812         p.appendChild(
1813             createTooltip(
1814                 { id : tt,
1815                   flex : "1",
1816                   orient : 'vertical',
1817                   onpopupshown : 'this.width = this.firstChild.boxObject.width + 10; this.height = this.firstChild.boxObject.height + 10;',
1818                   class : 'tooltip' },
1819                 createDescription({}, document.createTextNode( tooltip_hash[tt] ) )
1820             )
1821         );
1822     }
1823
1824     target.tooltip = tt;
1825     return true;
1826 }
1827
1828 function getContextMenu (target, type) {
1829
1830     var tt = '';
1831     if (type == 'subfield')
1832         tt = 't' + target.parentNode.parentNode.parentNode.firstChild.value + 'sf';
1833
1834     if (type == 'ind1')
1835         tt = 't' + target.parentNode.firstChild.value + 'i1';
1836
1837     if (type == 'ind2')
1838         tt = 't' + target.parentNode.firstChild.value + 'i2';
1839
1840     target.setAttribute('context', tt);
1841     return true;
1842 }
1843
1844 var authority_tag_map = {
1845     100 : ['[100,400,500,700]',100],
1846     400 : ['[100,400,500,700]',100],
1847     700 : ['[100,400,500,700]',100],
1848     800 : ['[100,400,500,700]',100],
1849     110 : ['[110,410,510,710]',110],
1850     410 : ['[110,410,510,710]',110],
1851     710 : ['[110,410,510,710]',110],
1852     810 : ['[110,410,510,710]',110],
1853     111 : ['[111,411,511,711]',111],
1854     411 : ['[111,411,511,711]',111],
1855     711 : ['[111,411,511,711]',111],
1856     811 : ['[111,411,511,711]',111],
1857     240 : ['[130,430,530,730]',130],
1858     440 : ['[130,430,530,730]',130],
1859     130 : ['[130,430,530,730]',130],
1860     730 : ['[130,430,530,730]',130],
1861     830 : ['[130,430,530,730]',130],
1862     600 : ['[100,400,480,481,482,485,500,580,581,582,585,700,780,781,782,785]',100],
1863     650 : ['[150,450,480,481,482,485,550,580,581,582,585,750,780,781,782,785]',150],
1864     651 : ['[151,451,480,481,482,485,551,580,581,582,585,751,780,781,782,785]',151],
1865     655 : ['[155,455,480,481,482,485,555,580,581,582,585,755,780,781,782,785]',155]
1866 };
1867
1868 function getAuthorityContextMenu (target, sf) {
1869     var menu_id = sf.parent().@tag + ':' + sf.@code + '-authority-context-' + sf;
1870
1871     var page = 0;
1872     var old = dojo.byId( menu_id );
1873     if (old) {
1874         page = auth_pages[menu_id];
1875         old.parentNode.removeChild(old);
1876     } else {
1877         auth_pages[menu_id] = 0;
1878     }
1879
1880     var sf_popup = createPopup({ id : menu_id, flex : 1 });
1881
1882     sf_popup.addEventListener("popuphiding", function(event) {
1883         if (show_auth_menu) {
1884             show_auth_menu = false;
1885             getAuthorityContextMenu(target, sf);
1886             dojo.byId(menu_id).openPopup();
1887         }  
1888     }, false);
1889
1890     context_menus.appendChild( sf_popup );
1891
1892     if (!authority_tag_map[sf.parent().@tag]) {
1893         sf_popup.appendChild(createLabel( { value : $('catStrings').getString('staff.cat.marcedit.not_authority_field.label') } ) );
1894         target.setAttribute('context', 'clipboard');
1895         return false;
1896     }
1897
1898     browseAuthority( sf_popup, menu_id, target, sf, 20, page);
1899
1900     return true;
1901 }
1902
1903 function applyAuthority ( target, ui_sf, e4x_sf ) {
1904
1905     var new_vals = target.getElementsByAttribute('checked','true');
1906     var field = e4x_sf.parent();
1907
1908     for (var i = 0; i < new_vals.length; i++) {
1909
1910         var sf_list = field.subfield;
1911         for (var j in sf_list) {
1912
1913             if (sf_list[j].@code == new_vals[i].getAttribute('subfield')) {
1914                 sf_list[j] = new_vals[i].getAttribute('value');
1915                 new_vals[i].setAttribute('subfield','');
1916                 break;
1917             }
1918         }
1919     }
1920
1921     for (var i = 0; i < new_vals.length; i++) {
1922         if (!new_vals[i].getAttribute('subfield')) continue;
1923
1924         var val = new_vals[i].getAttribute('value');
1925
1926         var sf = <subfield code="" xmlns="http://www.loc.gov/MARC21/slim">{val}</subfield>;
1927         sf.@code = new_vals[i].getAttribute('subfield');
1928
1929         field.insertChildAfter(field.subfield[field.subfield.length() - 1], sf);
1930     }
1931
1932     var row = marcDatafield( field );
1933
1934     var node = ui_sf;
1935     while (node.nodeName != 'row') {
1936         node = node.parentNode;
1937     }
1938
1939     node.parentNode.replaceChild( row, node );
1940     return true;
1941 }
1942
1943 var control_map = {
1944     100 : {
1945         'a' : { 100 : 'a' },
1946         'd' : { 100 : 'd' },
1947         'e' : { 100 : 'e' },
1948         'q' : { 100 : 'q' }
1949     },
1950     110 : {
1951         'a' : { 110 : 'a' },
1952         'd' : { 110 : 'd' }
1953     },
1954     111 : {
1955         'a' : { 111 : 'a' },
1956         'd' : { 111 : 'd' }
1957     },
1958     130 : {
1959         'a' : { 130 : 'a' },
1960         'd' : { 130 : 'd' }
1961     },
1962     240 : {
1963         'a' : { 130 : 'a' },
1964         'd' : { 130 : 'd' }
1965     },
1966     400 : {
1967         'a' : { 100 : 'a' },
1968         'd' : { 100 : 'd' }
1969     },
1970     410 : {
1971         'a' : { 110 : 'a' },
1972         'd' : { 110 : 'd' }
1973     },
1974     411 : {
1975         'a' : { 111 : 'a' },
1976         'd' : { 111 : 'd' }
1977     },
1978     440 : {
1979         'a' : { 130 : 'a' },
1980         'n' : { 130 : 'n' },
1981         'p' : { 130 : 'p' }
1982     },
1983     700 : {
1984         'a' : { 100 : 'a' },
1985         'd' : { 100 : 'd' },
1986         'q' : { 100 : 'q' },
1987         't' : { 100 : 't' }
1988     },
1989     710 : {
1990         'a' : { 110 : 'a' },
1991         'd' : { 110 : 'd' }
1992     },
1993     711 : {
1994         'a' : { 111 : 'a' },
1995         'c' : { 111 : 'c' },
1996         'd' : { 111 : 'd' }
1997     },
1998     730 : {
1999         'a' : { 130 : 'a' },
2000         'd' : { 130 : 'd' }
2001     },
2002     800 : {
2003         'a' : { 100 : 'a' },
2004         'd' : { 100 : 'd' }
2005     },
2006     810 : {
2007         'a' : { 110 : 'a' },
2008         'd' : { 110 : 'd' }
2009     },
2010     811 : {
2011         'a' : { 111 : 'a' },
2012         'd' : { 111 : 'd' }
2013     },
2014     830 : {
2015         'a' : { 130 : 'a' },
2016         'd' : { 130 : 'd' }
2017     },
2018     600 : {
2019         'a' : { 100 : 'a' },
2020         'd' : { 100 : 'd' },
2021         'q' : { 100 : 'q' },
2022         't' : { 100 : 't' },
2023         'v' : { 180 : 'v',
2024             100 : 'v',
2025             181 : 'v',
2026             182 : 'v',
2027             185 : 'v'
2028         },
2029         'x' : { 180 : 'x',
2030             100 : 'x',
2031             181 : 'x',
2032             182 : 'x',
2033             185 : 'x'
2034         },
2035         'y' : { 180 : 'y',
2036             100 : 'y',
2037             181 : 'y',
2038             182 : 'y',
2039             185 : 'y'
2040         },
2041         'z' : { 180 : 'z',
2042             100 : 'z',
2043             181 : 'z',
2044             182 : 'z',
2045             185 : 'z'
2046         }
2047     },
2048     610 : {
2049         'a' : { 110 : 'a' },
2050         'd' : { 110 : 'd' },
2051         't' : { 110 : 't' },
2052         'v' : { 180 : 'v',
2053             110 : 'v',
2054             181 : 'v',
2055             182 : 'v',
2056             185 : 'v'
2057         },
2058         'x' : { 180 : 'x',
2059             110 : 'x',
2060             181 : 'x',
2061             182 : 'x',
2062             185 : 'x'
2063         },
2064         'y' : { 180 : 'y',
2065             110 : 'y',
2066             181 : 'y',
2067             182 : 'y',
2068             185 : 'y'
2069         },
2070         'z' : { 180 : 'z',
2071             110 : 'z',
2072             181 : 'z',
2073             182 : 'z',
2074             185 : 'z'
2075         }
2076     },
2077     611 : {
2078         'a' : { 111 : 'a' },
2079         'd' : { 111 : 'd' },
2080         't' : { 111 : 't' },
2081         'v' : { 180 : 'v',
2082             111 : 'v',
2083             181 : 'v',
2084             182 : 'v',
2085             185 : 'v'
2086         },
2087         'x' : { 180 : 'x',
2088             111 : 'x',
2089             181 : 'x',
2090             182 : 'x',
2091             185 : 'x'
2092         },
2093         'y' : { 180 : 'y',
2094             111 : 'y',
2095             181 : 'y',
2096             182 : 'y',
2097             185 : 'y'
2098         },
2099         'z' : { 180 : 'z',
2100             111 : 'z',
2101             181 : 'z',
2102             182 : 'z',
2103             185 : 'z'
2104         }
2105     },
2106     630 : {
2107         'a' : { 130 : 'a' },
2108         'd' : { 130 : 'd' }
2109     },
2110     650 : {
2111         'a' : { 150 : 'a' },
2112         'b' : { 150 : 'b' },
2113         'v' : { 180 : 'v',
2114             150 : 'v',
2115             181 : 'v',
2116             182 : 'v',
2117             185 : 'v'
2118         },
2119         'x' : { 180 : 'x',
2120             150 : 'x',
2121             181 : 'x',
2122             182 : 'x',
2123             185 : 'x'
2124         },
2125         'y' : { 180 : 'y',
2126             150 : 'y',
2127             181 : 'y',
2128             182 : 'y',
2129             185 : 'y'
2130         },
2131         'z' : { 180 : 'z',
2132             150 : 'z',
2133             181 : 'z',
2134             182 : 'z',
2135             185 : 'z'
2136         }
2137     },
2138     651 : {
2139         'a' : { 151 : 'a' },
2140         'v' : { 180 : 'v',
2141             151 : 'v',
2142             181 : 'v',
2143             182 : 'v',
2144             185 : 'v'
2145         },
2146         'x' : { 180 : 'x',
2147             151 : 'x',
2148             181 : 'x',
2149             182 : 'x',
2150             185 : 'x'
2151         },
2152         'y' : { 180 : 'y',
2153             151 : 'y',
2154             181 : 'y',
2155             182 : 'y',
2156             185 : 'y'
2157         },
2158         'z' : { 180 : 'z',
2159             151 : 'z',
2160             181 : 'z',
2161             182 : 'z',
2162             185 : 'z'
2163         }
2164     },
2165     655 : {
2166         'a' : { 155 : 'a' },
2167         'v' : { 180 : 'v',
2168             155 : 'v',
2169             181 : 'v',
2170             182 : 'v',
2171             185 : 'v'
2172         },
2173         'x' : { 180 : 'x',
2174             155 : 'x',
2175             181 : 'x',
2176             182 : 'x',
2177             185 : 'x'
2178         },
2179         'y' : { 180 : 'y',
2180             155 : 'y',
2181             181 : 'y',
2182             182 : 'y',
2183             185 : 'y'
2184         },
2185         'z' : { 180 : 'z',
2186             155 : 'z',
2187             181 : 'z',
2188             182 : 'z',
2189             185 : 'z'
2190         }
2191     }
2192 };
2193
2194 function validateAuthority (button) {
2195     var grid = document.getElementById('recGrid');
2196     var label = button.getAttribute('label');
2197
2198     //loop over rows
2199     var rows = grid.lastChild.childNodes;
2200     for (var i = 0; i < rows.length; i++) {
2201         var row = rows[i];
2202         var tag = row.firstChild;
2203
2204         if (!control_map[tag.value]) continue
2205         button.setAttribute('label', label + ' - ' + tag.value);
2206
2207         var ind1 = tag.nextSibling;
2208         var ind2 = ind1.nextSibling;
2209         var subfields = ind2.nextSibling.childNodes;
2210
2211         var tags = {};
2212
2213         for (var j = 0; j < subfields.length; j++) {
2214             var sf = subfields[j];
2215             var sf_code = sf.childNodes[1].value;
2216             var sf_value = sf.childNodes[2].value;
2217
2218             if (!control_map[tag.value][sf_code]) continue;
2219
2220             var found = 0;
2221             for (var a_tag in control_map[tag.value][sf_code]) {
2222                 if (!tags[a_tag]) tags[a_tag] = [];
2223                 tags[a_tag].push({ term : sf_value, subfield : sf_code });
2224             }
2225
2226         }
2227
2228         for (var val_tag in tags) {
2229             var auth_data = validateBibField( [val_tag], tags[val_tag]);
2230             var res = new XML( auth_data.responseText );
2231             found = parseInt(res.gw::payload.gw::string.toString());
2232             if (found) break;
2233         }
2234
2235         // XXX If adt, etc should be validated separately from vxz, etc then move this up into the above for loop
2236         for (var j = 0; j < subfields.length; j++) {
2237             var sf = subfields[j];
2238             if (!found) {
2239                 dojo.removeClass(sf.childNodes[2], 'marcValidated');
2240                 dojo.addClass(sf.childNodes[2], 'marcUnvalidated');
2241             } else {
2242                 dojo.removeClass(sf.childNodes[2], 'marcUnvalidated');
2243                 dojo.addClass(sf.childNodes[2], 'marcValidated');
2244             }
2245         }
2246     }
2247
2248     button.setAttribute('label', label);
2249
2250     return true;
2251 }
2252
2253
2254 function validateBibField (tags, searches) {
2255     var url = "/gateway?input_format=json&format=xml&service=open-ils.search&method=open-ils.search.authority.validate.tag";
2256     url += '&param="tags"&param=' + js2JSON(tags);
2257     url += '&param="searches"&param=' + js2JSON(searches);
2258
2259
2260     var req = new XMLHttpRequest();
2261     req.open('GET',url,false);
2262     req.send(null);
2263
2264     return req;
2265
2266 }
2267 function searchAuthority (term, tag, sf, limit) {
2268     var url = "/gateway?input_format=json&format=xml&service=open-ils.search&method=open-ils.search.authority.fts";
2269     url += '&param="term"&param="' + term + '"';
2270     url += '&param="limit"&param=' + limit;
2271     url += '&param="tag"&param=' + tag;
2272     url += '&param="subfield"&param="' + sf + '"';
2273
2274
2275     var req = new XMLHttpRequest();
2276     req.open('GET',url,false);
2277     req.send(null);
2278
2279     return req;
2280
2281 }
2282
2283 function browseAuthority (sf_popup, menu_id, target, sf, limit, page) {
2284     dojo.require('dojox.xml.parser');
2285
2286     // map tag + subfield to the appropriate authority browse axis:
2287     // currently authority.author, authority.subject, authority.title, authority.topic
2288     // based on mappings in OpenILS::Application::SuperCat
2289
2290     var type;
2291
2292     // Map based on replacing the first char of the selected tag with '1'
2293     switch ('1' + (sf.parent().@tag.toString()).substring(1)) {
2294         case "130":
2295             type = 'authority.title';
2296             break;
2297
2298         case "100":
2299         case "110":
2300         case "111":
2301             type = 'authority.author';
2302             break;
2303
2304         case "150":
2305             type = 'authority.topic';
2306             break;
2307
2308         case "148":
2309         case "151":
2310         case "155":
2311             type = 'authority.subject';
2312             break;
2313
2314         // No matching tag means no authorities to search - shortcut
2315         default:
2316             return;
2317     }
2318
2319     if (!limit) {
2320         limit = 10;
2321     }
2322
2323     if (!page) {
2324         page = 0;
2325     }
2326
2327     var url = '/opac/extras/startwith/marcxml/'
2328         + type
2329         + '/1' // OU - currently unscoped
2330         + '/' + sf.toString()
2331         + '/' + page
2332         + '/' + limit
2333     ;
2334
2335     // would be good to carve this out into a separate function
2336     dojo.xhrGet({"url":url, "sync": true, "handleAs":"xml", "load": function(records) {
2337         var create_menu = createMenu({ label: $('catStrings').getString('staff.cat.marcedit.create_authority.label')});
2338
2339         var cm_popup = create_menu.appendChild(
2340             createMenuPopup()
2341         );
2342
2343         cm_popup.appendChild(
2344             createMenuitem({ label : $('catStrings').getString('staff.cat.marcedit.create_authority_now.label'),
2345                 command : function() { 
2346                     // Call middle-layer function to create and save the new authority
2347                     var source_f = summarizeField(sf);
2348                     var new_auth = fieldmapper.standardRequest(
2349                         ["open-ils.cat", "open-ils.cat.authority.record.create_from_bib"],
2350                         [source_f, ses()]
2351                     );
2352                     if (new_auth && new_auth.id()) {
2353                         var id_sf = <subfield code="0" xmlns="http://www.loc.gov/MARC21/slim">(CONS){new_auth.id()}</subfield>;
2354                         sf.parent().appendChild(id_sf);
2355                         var new_sf = marcSubfield(id_sf);
2356                         target.parentNode.appendChild(new_sf);
2357                         alert($('catStrings').getString('staff.cat.marcedit.create_authority_success.label'));
2358                     }
2359                 }
2360             })
2361         );
2362
2363         cm_popup.appendChild(
2364             createMenuitem({ label : $('catStrings').getString('staff.cat.marcedit.create_authority_edit.label'),
2365                 command : function() { 
2366                     // Generate the new authority by calling the new middle-layer
2367                     // function (a non-saving variant), then display in another
2368                     // MARC editor
2369                     var source_f = summarizeField(sf);
2370                     var authtoken = ses();
2371                     dojo.require('openils.PermaCrud');
2372                     var pcrud = new openils.PermaCrud({"authtoken": authtoken});
2373                     var rec = fieldmapper.standardRequest(
2374                         ["open-ils.cat", "open-ils.cat.authority.record.create_from_bib.readonly"],
2375                         { "params": [source_f] }
2376                     );
2377                     loadMarcEditor(pcrud, rec, target, sf);
2378                 }
2379             })
2380         );
2381
2382         sf_popup.appendChild(create_menu);
2383         sf_popup.appendChild( createComplexXULElement( 'menuseparator' ) );
2384
2385         // append "Previous page" results browser
2386         sf_popup.appendChild(
2387             createMenuitem({ label : $('catStrings').getString('staff.cat.marcedit.previous_page.label'),
2388                 command : function(event) { 
2389                     auth_pages[menu_id] -= 1;
2390                     show_auth_menu = true;
2391                 }
2392             })
2393         );
2394         sf_popup.appendChild( createComplexXULElement( 'menuseparator' ) );
2395
2396         dojo.query('record', records).forEach(function(record) {
2397             var main_text = '';
2398             var auth_id = dojox.xml.parser.textContent(dojo.query('datafield[tag="901"] subfield[code="c"]', record)[0]);
2399             var auth_org = dojox.xml.parser.textContent(dojo.query('controlfield[tag="003"]', record)[0]);
2400             // we have grabbed the fields with tags beginning with 1 or 5 and iterate through the subfields
2401             dojo.query('datafield[tag^="1"], datafield[tag^="5"]', record).forEach(function(field) {
2402                 dojo.query('subfield', field).forEach(function(subfield) {
2403                     if (main_text) {
2404                         main_text += ' / ';
2405                     }
2406                     main_text += dojox.xml.parser.textContent(subfield);
2407                 });
2408             });
2409
2410             /*
2411              * 
2412             if (! (main[0].parent().@tag == authority_tag_map[sf.parent().@tag][1]) ) return;
2413             */
2414
2415             var grid = dojo.query('[name="authority-marc-template"]')[0].cloneNode(true);
2416             grid.setAttribute('name','-none-');
2417             grid.setAttribute('style','overflow:scroll');
2418
2419             var submenu = createMenu( { label : main_text } );
2420
2421             var popup = createMenuPopup({ flex : "1" });
2422             submenu.appendChild(popup);
2423
2424             dojo.query('datafield[tag^="1"], datafield[tag^="5"]', record).forEach(function(field) {
2425                 var row = createRow(
2426                     {},
2427                     createLabel( { "value" : dojo.attr(field, 'ind1') } ),
2428                     createLabel( { "value" : dojo.attr(field, 'ind2') } )
2429                 );
2430
2431                 var sf_box = createHbox();
2432                 dojo.query('subfield', field).forEach(function(subfield) {
2433                     sf_box.appendChild(
2434                         createCheckbox(
2435                             { "label"    : '\u2021' + dojo.attr(subfield, 'code') + ' ' + dojox.xml.parser.textContent(subfield),
2436                               "subfield" : dojo.attr(subfield, 'code'),
2437                               "tag"      : dojo.attr(field, 'tag'),
2438                               "value"    : dojox.xml.parser.textContent(subfield)
2439                             }
2440                         )
2441                     );
2442                     row.appendChild(sf_box);
2443                 });
2444
2445                 // Append the authority linking subfield
2446                 sf_box.appendChild(
2447                     createCheckbox(
2448                         { "label"    : '\u2021' + '0' + ' (' + auth_org + ')' + auth_id,
2449                           "subfield" : '0',
2450                           "tag"      : dojo.attr(field, 'tag'),
2451                           "value"    : '(' + auth_org + ')' + auth_id
2452                         }
2453                     )
2454                 );
2455                 row.appendChild(sf_box);
2456
2457                 grid.lastChild.appendChild(row);
2458             });
2459
2460             grid.hidden = false;
2461             popup.appendChild( grid );
2462
2463             popup.appendChild(
2464                 createMenuitem(
2465                     { label : $('catStrings').getString('staff.cat.marcedit.apply_selected.label'),
2466                       command : function (event) {
2467                             applyAuthority(event.target.previousSibling, target, sf);
2468                             return true;
2469                       }
2470                     }
2471                 )
2472             );
2473
2474             sf_popup.appendChild( submenu );
2475         });
2476
2477         if (sf_popup.childNodes.length == 0) {
2478             sf_popup.appendChild(createLabel( { value : $('catStrings').getString('staff.cat.marcedit.no_authority_match.label') } ) );
2479         } else {
2480             // append "Next page" results browser
2481             sf_popup.appendChild( createComplexXULElement( 'menuseparator' ) );
2482             sf_popup.appendChild(
2483                 createMenuitem({ label : $('catStrings').getString('staff.cat.marcedit.next_page.label'),
2484                     command : function(event) { 
2485                         auth_pages[menu_id] += 1;
2486                         show_auth_menu = true;
2487                     }
2488                 })
2489             );
2490         }
2491
2492         target.setAttribute('context', menu_id);
2493         return true;
2494     }});
2495
2496 }
2497
2498 function summarizeField(sf) {
2499     var source_f= {
2500         "tag": '',
2501         "ind1": '',
2502         "ind2": '',
2503         "subfields": []
2504     };
2505     for (var i = 0; i < sf.parent().subfield.length(); i++) {
2506         source_f.subfields.push([sf.parent().subfield[i].@code.toString(), sf.parent().subfield[i].toString()]);
2507     }
2508     source_f.tag = sf.parent().@tag.toString();
2509     source_f.ind1 = sf.parent().@ind1.toString();
2510     source_f.ind1 = sf.parent().@ind2.toString();
2511     return source_f;
2512 }
2513
2514
2515 function buildBibSourceList (authtoken, recId) {
2516     /* TODO: Work out how to set the bib source of the bre that does not yet
2517      * exist - this is specifically in the case of Z39.50 imports. Right now
2518      * we just avoid populating and showing the config.bib_source list
2519      */
2520     if (!recId) {
2521         return false;
2522     }
2523
2524     var bib = xulG.record.bre;
2525
2526     dojo.require('openils.PermaCrud');
2527
2528     // cbsList = the XUL menulist that contains the available bib sources 
2529     var cbsList = dojo.byId('bib-source-list');
2530
2531     // bibSources = an array containing all of the bib source objects
2532     var bibSources = new openils.PermaCrud({"authtoken": authtoken}).retrieveAll('cbs');
2533
2534     // A tad ugly, but gives us the index of the bib source ID in cbsList
2535     var x = 0;
2536     var cbsListArr = [];
2537     dojo.forEach(bibSources, function (item) {
2538         cbsList.appendItem(item.source(), item.id());
2539         cbsListArr[item.id()] = x;
2540         x++;
2541     });
2542
2543     // Show the current value of the bib source for this record
2544     cbsList.selectedIndex = cbsListArr[bib.source()];
2545
2546     // Display the bib source selection widget
2547     dojo.byId('bib-source-list-caption').hidden = false;
2548     dojo.byId('bib-source-list').hidden = false;
2549     dojo.byId('bib-source-list-button').disabled = true;
2550     dojo.byId('bib-source-list-button').hidden = false;
2551 }
2552
2553 // Fired when the "Update Source" button is clicked
2554 // Updates the value of the bib source for the current record
2555 function updateBibSource() {
2556     var authtoken = ses();
2557     var cbs = dojo.byId('bib-source-list').selectedItem.value;
2558     var recId = xulG.record.id;
2559     var pcrud = new openils.PermaCrud({"authtoken": authtoken});
2560     var bib = pcrud.retrieve('bre', recId);
2561     if (bib.source() != cbs) {
2562         bib.source(cbs);
2563         bib.ischanged = true;
2564         pcrud.update(bib);
2565     }
2566 }
2567
2568 function onBibSourceSelect() {
2569     var cbs = dojo.byId('bib-source-list').selectedItem.value;
2570     var bib = xulG.record.bre;
2571     if (bib.source() != cbs) {
2572         dojo.byId('bib-source-list-button').disabled = false;   
2573     } else {
2574         dojo.byId('bib-source-list-button').disabled = true;   
2575     }
2576 }
2577
2578 function loadMarcEditor(pcrud, marcxml, target, sf) {
2579     /*
2580        To run in Firefox directly, must set signed.applets.codebase_principal_support
2581        to true in about:config
2582      */
2583     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
2584     win = window.open('/xul/server/cat/marcedit.xul'); // XXX version?
2585
2586     // Match marc2are.pl last_xact_id format, roughly
2587     var now = new Date;
2588     var xact_id = 'IMPORT-' + Date.parse(now);
2589     
2590     win.xulG = {
2591         "record": {"marc": marcxml, "rtype": "are"},
2592         "save": {
2593             "label": $('catStrings').getString('staff.cat.marcedit.save.label'),
2594             "func": function(xmlString) {
2595                 var rec = new are();
2596                 rec.marc(xmlString);
2597                 rec.last_xact_id(xact_id);
2598                 rec.isnew(true);
2599                 pcrud.create(rec, {
2600                     "oncomplete": function (r, objs) {
2601                         var new_rec = objs[0];
2602                         if (!new_rec) {
2603                             return '';
2604                         }
2605                         var id_sf = <subfield code="0" xmlns="http://www.loc.gov/MARC21/slim">(CONS){new_rec.id()}</subfield>;
2606                         sf.parent().appendChild(id_sf);
2607                         var new_sf = marcSubfield(id_sf);
2608                         target.parentNode.appendChild(new_sf);
2609                         alert($('catStrings').getString('staff.cat.marcedit.create_authority_success.label'));
2610                         win.close();
2611                     }
2612                 });
2613             }
2614         }
2615     };
2616 }
2617
2618