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