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