]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/marcedit.js
Support for Fast Item Add in MARC Editor for existing records. Removed a speedbump...
[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         document.getElementById('fastItemAdd_barcode').value = '';
1310     } catch(E) {
1311         alert('fastItemAdd_attempt: ' + E);
1312     }
1313 }
1314
1315 function save_attempt(xml_string) {
1316     try {
1317         var result = window.xulG.save.func( xml_string );   
1318         if (result) {
1319             if (result.id) fastItemAdd_attempt(result.id);
1320             if (typeof result.on_complete == 'function') result.on_complete();
1321         }
1322     } catch(E) {
1323         alert('save_attempt: ' + E);
1324     }
1325 }
1326
1327 function marcDatafield (field) {
1328     var row = createRow(
1329         { class : 'marcDatafieldRow' },
1330         createMARCTextbox(
1331             field.@tag,
1332             { value : field.@tag,
1333               class : 'plain marcTag',
1334               name : 'marcTag',
1335               context : 'tags_popup',
1336               oninput : 'if (this.value.length == 3) { this.nextSibling.focus(); }',
1337               size : 3,
1338               maxlength : 3,
1339               onmouseover : 'current_focus = this; getTooltip(this, "tag");' } ),
1340         createMARCTextbox(
1341             field.@ind1,
1342             { value : field.@ind1,
1343               class : 'plain marcInd1',
1344               name : 'marcInd1',
1345               oninput : 'if (this.value.length == 1) { this.nextSibling.focus(); }',
1346               size : 1,
1347               maxlength : 1,
1348               onmouseover : 'current_focus = this; getContextMenu(this, "ind1"); getTooltip(this, "ind1");',
1349               oncontextmenu : 'getContextMenu(this, "ind1");' } ),
1350         createMARCTextbox(
1351             field.@ind2,
1352             { value : field.@ind2,
1353               class : 'plain marcInd2',
1354               name : 'marcInd2',
1355               oninput : 'if (this.value.length == 1) { this.nextSibling.firstChild.firstChild.focus(); }',
1356               size : 1,
1357               maxlength : 1,
1358               onmouseover : 'current_focus = this; getContextMenu(this, "ind2"); getTooltip(this, "ind2");',
1359               oncontextmenu : 'getContextMenu(this, "ind2");' } ),
1360         createHbox({ name : 'sf_box' })
1361     );
1362
1363     if (!current_focus && field.@tag == '') current_focus = row.childNodes[0];
1364     if (!current_focus && field.@ind1 == '') current_focus = row.childNodes[1];
1365     if (!current_focus && field.@ind2 == '') current_focus = row.childNodes[2];
1366
1367     var sf_box = row.lastChild;
1368     if (document.getElementById('stackSubfields').checked)
1369         sf_box.setAttribute('orient','vertical');
1370
1371     sf_box.addEventListener(
1372         'click',
1373         function (e) {
1374             if (sf_box === e.target) {
1375                 sf_box.lastChild.lastChild.focus();
1376             } else if (e.target.parentNode === sf_box) {
1377                 e.target.lastChild.focus();
1378             }
1379         },
1380         false
1381     );
1382
1383
1384     for (var i in field.subfield) {
1385         var sf = field.subfield[i];
1386         sf_box.appendChild(
1387             marcSubfield(sf)
1388         );
1389
1390         if (sf.@code == '' && (!current_focus || current_focus.className.match(/Ind/)))
1391             current_focus = sf_box.lastChild.childNodes[1];
1392     }
1393
1394     return row;
1395 }
1396
1397 function marcSubfield (sf) {            
1398     return createHbox(
1399         { class : 'marcSubfieldBox' },
1400         createLabel(
1401             { value : "\u2021",
1402               class : 'plain marcSubfieldDelimiter',
1403               onmouseover : 'getTooltip(this.nextSibling, "subfield");',
1404               oncontextmenu : 'getContextMenu(this.nextSibling, "subfield");',
1405                 //onclick : 'this.nextSibling.focus();',
1406                 onfocus : 'this.nextSibling.focus();',
1407               size : 2 } ),
1408         createMARCTextbox(
1409             sf.@code,
1410             { value : sf.@code,
1411               class : 'plain marcSubfieldCode',
1412               name : 'marcSubfieldCode',
1413               onmouseover : 'current_focus = this; getContextMenu(this, "subfield"); getTooltip(this, "subfield");',
1414               oncontextmenu : 'getContextMenu(this, "subfield");',
1415               oninput : 'if (this.value.length == 1) { this.nextSibling.focus(); }',
1416               size : 2,
1417               maxlength : 1 } ),
1418         createMARCTextbox(
1419             sf,
1420             { value : sf.text(),
1421               name : sf.parent().@tag + ':' + sf.@code,
1422               class : 'plain marcSubfield', 
1423               onmouseover : 'getTooltip(this, "subfield");',
1424               contextmenu : function (event) { getAuthorityContextMenu(event.target, sf) },
1425               size : new String(sf.text()).length + 2,
1426               oninput : "this.setAttribute('size', this.value.length + 2);"
1427             } )
1428     );
1429 }
1430
1431 function loadRecord(rec) {
1432     try {
1433             _record = rec;
1434             var grid_rows = document.getElementById('recGrid').lastChild;
1435
1436             while (grid_rows.firstChild) grid_rows.removeChild(grid_rows.firstChild);
1437
1438             grid_rows.appendChild( marcLeader( rec.leader ) );
1439
1440             for (var i in rec.controlfield) {
1441                 grid_rows.appendChild( marcControlfield( rec.controlfield[i] ) );
1442             }
1443
1444             for (var i in rec.datafield) {
1445                 grid_rows.appendChild( marcDatafield( rec.datafield[i] ) );
1446             }
1447
1448             grid_rows.getElementsByAttribute('class','marcDatafieldRow')[0].firstChild.focus();
1449             changeFFEditor(recordType(rec));
1450             fillFixedFields(rec);
1451     } catch(E) {
1452         alert('FIXME, MARC Editor, loadRecord: ' + E);
1453     }
1454 }
1455
1456
1457 function genToolTips () {
1458     for (var i in bib_data.field) {
1459         var f = bib_data.field[i];
1460     
1461         tag_menu.appendChild(
1462             createMenuitem(
1463                 { label : f.@tag,
1464                   oncommand : 
1465                       'current_focus.value = "' + f.@tag + '";' +
1466                     'var e = document.createEvent("MutationEvents");' +
1467                     'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1468                     'current_focus.inputField.dispatchEvent(e);',
1469                   disabled : f.@tag < '010' ? "true" : "false",
1470                   tooltiptext : f.description }
1471             )
1472         );
1473     
1474         var i1_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'i1' });
1475         context_menus.appendChild( i1_popup );
1476     
1477         var i2_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'i2' });
1478         context_menus.appendChild( i2_popup );
1479     
1480         var sf_popup = createPopup({position : 'after_start', id : 't' + f.@tag + 'sf' });
1481         context_menus.appendChild( sf_popup );
1482     
1483         tooltip_hash['tag' + f.@tag] = f.description;
1484         for (var j in f.indicator) {
1485             var ind = f.indicator[j];
1486             tooltip_hash['tag' + f.@tag + 'ind' + ind.@position + 'val' + ind.@value] = ind.description;
1487     
1488             if (ind.@position == 1) {
1489                 i1_popup.appendChild(
1490                     createMenuitem(
1491                         { label : ind.@value,
1492                           oncommand : 
1493                               'current_focus.value = "' + ind.@value + '";' +
1494                             'var e = document.createEvent("MutationEvents");' +
1495                             'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1496                             'current_focus.inputField.dispatchEvent(e);',
1497                           tooltiptext : ind.description }
1498                     )
1499                 );
1500             }
1501     
1502             if (ind.@position == 2) {
1503                 i2_popup.appendChild(
1504                     createMenuitem(
1505                         { label : ind.@value,
1506                           oncommand : 
1507                               'current_focus.value = "' + ind.@value + '";' +
1508                             'var e = document.createEvent("MutationEvents");' +
1509                             'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1510                             'current_focus.inputField.dispatchEvent(e);',
1511                           tooltiptext : ind.description }
1512                     )
1513                 );
1514             }
1515         }
1516     
1517         for (var j in f.subfield) {
1518             var sf = f.subfield[j];
1519             tooltip_hash['tag' + f.@tag + 'sf' + sf.@code] = sf.description;
1520     
1521             sf_popup.appendChild(
1522                 createMenuitem(
1523                     { label : sf.@code,
1524                       oncommand : 
1525                           'current_focus.value = "' + sf.@code + '";' +
1526                         'var e = document.createEvent("MutationEvents");' +
1527                         'e.initMutationEvent("change",1,1,null,0,0,0,0);' +
1528                         'current_focus.inputField.dispatchEvent(e);',
1529                       tooltiptext : sf.description
1530                     }
1531                 )
1532             );
1533         }
1534     }
1535 }
1536
1537 function getTooltip (target, type) {
1538
1539     var tt = '';
1540     if (type == 'subfield')
1541         tt = 'tag' + target.parentNode.parentNode.parentNode.firstChild.value + 'sf' + target.parentNode.childNodes[1].value;
1542
1543     if (type == 'ind1')
1544         tt = 'tag' + target.parentNode.firstChild.value + 'ind1val' + target.value;
1545
1546     if (type == 'ind2')
1547         tt = 'tag' + target.parentNode.firstChild.value + 'ind2val' + target.value;
1548
1549     if (type == 'tag')
1550         tt = 'tag' + target.parentNode.firstChild.value;
1551
1552     if (!document.getElementById( tt )) {
1553         p.appendChild(
1554             createTooltip(
1555                 { id : tt,
1556                   flex : "1",
1557                   orient : 'vertical',
1558                   onpopupshown : 'this.width = this.firstChild.boxObject.width + 10; this.height = this.firstChild.boxObject.height + 10;',
1559                   class : 'tooltip' },
1560                 createDescription({}, document.createTextNode( tooltip_hash[tt] ) )
1561             )
1562         );
1563     }
1564
1565     target.tooltip = tt;
1566     return true;
1567 }
1568
1569 function getContextMenu (target, type) {
1570
1571     var tt = '';
1572     if (type == 'subfield')
1573         tt = 't' + target.parentNode.parentNode.parentNode.firstChild.value + 'sf';
1574
1575     if (type == 'ind1')
1576         tt = 't' + target.parentNode.firstChild.value + 'i1';
1577
1578     if (type == 'ind2')
1579         tt = 't' + target.parentNode.firstChild.value + 'i2';
1580
1581     target.setAttribute('context', tt);
1582     return true;
1583 }
1584
1585 var authority_tag_map = {
1586     100 : ['[100,400,500,700]',100],
1587     400 : ['[100,400,500,700]',100],
1588     700 : ['[100,400,500,700]',100],
1589     800 : ['[100,400,500,700]',100],
1590     110 : ['[110,410,510,710]',110],
1591     410 : ['[110,410,510,710]',110],
1592     710 : ['[110,410,510,710]',110],
1593     810 : ['[110,410,510,710]',110],
1594     111 : ['[111,411,511,711]',111],
1595     411 : ['[111,411,511,711]',111],
1596     711 : ['[111,411,511,711]',111],
1597     811 : ['[111,411,511,711]',111],
1598     240 : ['[130,430,530,730]',130],
1599     440 : ['[130,430,530,730]',130],
1600     130 : ['[130,430,530,730]',130],
1601     730 : ['[130,430,530,730]',130],
1602     830 : ['[130,430,530,730]',130],
1603     600 : ['[100,400,480,481,482,485,500,580,581,582,585,700,780,781,782,785]',100],
1604     650 : ['[150,450,480,481,482,485,550,580,581,582,585,750,780,781,782,785]',150],
1605     651 : ['[151,451,480,481,482,485,551,580,581,582,585,751,780,781,782,785]',151],
1606     655 : ['[155,455,480,481,482,485,555,580,581,582,585,755,780,781,782,785]',155]
1607 };
1608
1609 function getAuthorityContextMenu (target, sf) {
1610     var menu_id = sf.parent().@tag + ':' + sf.@code + '-authority-context-' + sf;
1611
1612     var old = document.getElementById( menu_id );
1613     if (old) old.parentNode.removeChild(old);
1614
1615     var sf_popup = createPopup({ id : menu_id, flex : 1 });
1616     context_menus.appendChild( sf_popup );
1617
1618     if (!authority_tag_map[sf.parent().@tag]) {
1619         sf_popup.appendChild(createLabel( { value : $('catStrings').getString('staff.cat.marcedit.not_authority_field.label') } ) );
1620         target.setAttribute('context', 'clipboard');
1621         return false;
1622     }
1623
1624     var auth_data = searchAuthority( sf, authority_tag_map[sf.parent().@tag][0], sf.@code, 50);
1625
1626     var res = new XML( auth_data.responseText );
1627
1628     var rec_list = [];
1629
1630     var recs = res.gw::payload.gw::array.gw::string;
1631     for (var i in recs) {
1632         var x = recs[i];
1633         var xml = new XML(x.toString());
1634         var main = xml.datafield.(@tag.toString().match(/^1/)).subfield;
1635
1636         if (! (main[0].parent().@tag == authority_tag_map[sf.parent().@tag][1]) ) continue;
1637
1638         var main_text = '';
1639         for (var i in main) {
1640             if (main_text) main_text += ' / ';
1641             main_text += main[i];
1642         }
1643
1644         rec_list.push( [ main_text, xml ] );
1645     }
1646     
1647     for (var i in rec_list.sort( function (a, b) { if(a[0] > b[0]) return 1; return -1; } )) {
1648
1649         var main_text = rec_list[i][0];
1650         var xml = rec_list[i][1];
1651         var main = xml.datafield.(@tag.toString().match(/^1/)).subfield;
1652
1653         if (! (main[0].parent().@tag == authority_tag_map[sf.parent().@tag][1]) ) continue;
1654
1655         var grid = document.getElementsByAttribute('name','authority-marc-template')[0].cloneNode(true);
1656         grid.setAttribute('name','-none-');
1657         grid.setAttribute('style','overflow:scroll');
1658
1659
1660         var submenu = createMenu( { label : main_text } );
1661
1662         var popup = createMenuPopup({ flex : "1" });
1663         submenu.appendChild(popup);
1664
1665         var fields = xml.datafield;
1666         for (var j in fields) {
1667
1668             var row = createRow(
1669                 {},
1670                 createLabel( { value : fields[j].@tag } ),
1671                 createLabel( { value : fields[j].@ind1 } ),
1672                 createLabel( { value : fields[j].@ind2 } )
1673             );
1674
1675             var sf_box = createHbox();
1676
1677             var subfields = fields[j].subfield;
1678             for (var k in subfields) {
1679                 sf_box.appendChild(
1680                     createCheckbox(
1681                         { label    : '\u2021' + subfields[k].@code + ' ' + subfields[k],
1682                           subfield : subfields[k].@code,
1683                           tag      : subfields[k].parent().@tag,
1684                           value    : subfields[k]
1685                         }
1686                     )
1687                 );
1688                 row.appendChild(sf_box);
1689             }
1690
1691             grid.lastChild.appendChild(row);
1692         }
1693
1694         grid.hidden = false;
1695         popup.appendChild( grid );
1696
1697         popup.appendChild(
1698             createMenuitem(
1699                 { label : $('catStrings').getString('staff.cat.marcedit.apply_selected.label'),
1700                   command : function (event) {
1701                         applyAuthority(event.target.previousSibling, target, sf);
1702                         return true;
1703                   }
1704                 }
1705             )
1706         );
1707
1708         sf_popup.appendChild( submenu );
1709     }
1710
1711     if (sf_popup.childNodes.length == 0)
1712         sf_popup.appendChild(createLabel( { value : $('catStrings').getString('staff.cat.marcedit.no_authority_match.label') } ) );
1713
1714     target.setAttribute('context', menu_id);
1715     return true;
1716 }
1717
1718 function applyAuthority ( target, ui_sf, e4x_sf ) {
1719
1720     var new_vals = target.getElementsByAttribute('checked','true');
1721     var field = e4x_sf.parent();
1722
1723     for (var i = 0; i < new_vals.length; i++) {
1724
1725         var sf_list = field.subfield;
1726         for (var j in sf_list) {
1727
1728             if (sf_list[j].@code == new_vals[i].getAttribute('subfield')) {
1729                 sf_list[j] = new_vals[i].getAttribute('value');
1730                 new_vals[i].setAttribute('subfield','');
1731                 break;
1732             }
1733         }
1734     }
1735
1736     for (var i = 0; i < new_vals.length; i++) {
1737         if (!new_vals[i].getAttribute('subfield')) continue;
1738
1739         var val = new_vals[i].getAttribute('value');
1740
1741         var sf = <subfield code="" xmlns="http://www.loc.gov/MARC21/slim">{val}</subfield>;
1742         sf.@code = new_vals[i].getAttribute('subfield');
1743
1744         field.insertChildAfter(field.subfield[field.subfield.length() - 1], sf);
1745     }
1746
1747     var row = marcDatafield( field );
1748
1749     var node = ui_sf;
1750     while (node.nodeName != 'row') {
1751         node = node.parentNode;
1752     }
1753
1754     node.parentNode.replaceChild( row, node );
1755     return true;
1756 }
1757
1758 var control_map = {
1759     100 : {
1760         'a' : { 100 : 'a' },
1761         'd' : { 100 : 'd' },
1762         'q' : { 100 : 'q' }
1763     },
1764     110 : {
1765         'a' : { 110 : 'a' },
1766         'd' : { 110 : 'd' }
1767     },
1768     111 : {
1769         'a' : { 111 : 'a' },
1770         'd' : { 111 : 'd' }
1771     },
1772     130 : {
1773         'a' : { 130 : 'a' },
1774         'd' : { 130 : 'd' }
1775     },
1776     240 : {
1777         'a' : { 130 : 'a' },
1778         'd' : { 130 : 'd' }
1779     },
1780     400 : {
1781         'a' : { 100 : 'a' },
1782         'd' : { 100 : 'd' }
1783     },
1784     410 : {
1785         'a' : { 110 : 'a' },
1786         'd' : { 110 : 'd' }
1787     },
1788     411 : {
1789         'a' : { 111 : 'a' },
1790         'd' : { 111 : 'd' }
1791     },
1792     440 : {
1793         'a' : { 130 : 'a' },
1794         'n' : { 130 : 'n' },
1795         'p' : { 130 : 'p' }
1796     },
1797     700 : {
1798         'a' : { 100 : 'a' },
1799         'd' : { 100 : 'd' },
1800         'q' : { 100 : 'q' },
1801         't' : { 100 : 't' }
1802     },
1803     710 : {
1804         'a' : { 110 : 'a' },
1805         'd' : { 110 : 'd' }
1806     },
1807     711 : {
1808         'a' : { 111 : 'a' },
1809         'd' : { 111 : 'd' }
1810     },
1811     730 : {
1812         'a' : { 130 : 'a' },
1813         'd' : { 130 : 'd' }
1814     },
1815     800 : {
1816         'a' : { 100 : 'a' },
1817         'd' : { 100 : 'd' }
1818     },
1819     810 : {
1820         'a' : { 110 : 'a' },
1821         'd' : { 110 : 'd' }
1822     },
1823     811 : {
1824         'a' : { 111 : 'a' },
1825         'd' : { 111 : 'd' }
1826     },
1827     830 : {
1828         'a' : { 130 : 'a' },
1829         'd' : { 130 : 'd' }
1830     },
1831     600 : {
1832         'a' : { 100 : 'a' },
1833         'd' : { 100 : 'd' },
1834         'q' : { 100 : 'q' },
1835         't' : { 100 : 't' },
1836         'v' : { 180 : 'v',
1837             100 : 'v',
1838             181 : 'v',
1839             182 : 'v',
1840             185 : 'v'
1841         },
1842         'x' : { 180 : 'x',
1843             100 : 'x',
1844             181 : 'x',
1845             182 : 'x',
1846             185 : 'x'
1847         },
1848         'y' : { 180 : 'y',
1849             100 : 'y',
1850             181 : 'y',
1851             182 : 'y',
1852             185 : 'y'
1853         },
1854         'z' : { 180 : 'z',
1855             100 : 'z',
1856             181 : 'z',
1857             182 : 'z',
1858             185 : 'z'
1859         }
1860     },
1861     610 : {
1862         'a' : { 110 : 'a' },
1863         'd' : { 110 : 'd' },
1864         't' : { 110 : 't' },
1865         'v' : { 180 : 'v',
1866             110 : 'v',
1867             181 : 'v',
1868             182 : 'v',
1869             185 : 'v'
1870         },
1871         'x' : { 180 : 'x',
1872             110 : 'x',
1873             181 : 'x',
1874             182 : 'x',
1875             185 : 'x'
1876         },
1877         'y' : { 180 : 'y',
1878             110 : 'y',
1879             181 : 'y',
1880             182 : 'y',
1881             185 : 'y'
1882         },
1883         'z' : { 180 : 'z',
1884             110 : 'z',
1885             181 : 'z',
1886             182 : 'z',
1887             185 : 'z'
1888         }
1889     },
1890     611 : {
1891         'a' : { 111 : 'a' },
1892         'd' : { 111 : 'd' },
1893         't' : { 111 : 't' },
1894         'v' : { 180 : 'v',
1895             111 : 'v',
1896             181 : 'v',
1897             182 : 'v',
1898             185 : 'v'
1899         },
1900         'x' : { 180 : 'x',
1901             111 : 'x',
1902             181 : 'x',
1903             182 : 'x',
1904             185 : 'x'
1905         },
1906         'y' : { 180 : 'y',
1907             111 : 'y',
1908             181 : 'y',
1909             182 : 'y',
1910             185 : 'y'
1911         },
1912         'z' : { 180 : 'z',
1913             111 : 'z',
1914             181 : 'z',
1915             182 : 'z',
1916             185 : 'z'
1917         }
1918     },
1919     630 : {
1920         'a' : { 130 : 'a' },
1921         'd' : { 130 : 'd' }
1922     },
1923     650 : {
1924         'a' : { 150 : 'a' },
1925         'b' : { 150 : 'b' },
1926         'v' : { 180 : 'v',
1927             150 : 'v',
1928             181 : 'v',
1929             182 : 'v',
1930             185 : 'v'
1931         },
1932         'x' : { 180 : 'x',
1933             150 : 'x',
1934             181 : 'x',
1935             182 : 'x',
1936             185 : 'x'
1937         },
1938         'y' : { 180 : 'y',
1939             150 : 'y',
1940             181 : 'y',
1941             182 : 'y',
1942             185 : 'y'
1943         },
1944         'z' : { 180 : 'z',
1945             150 : 'z',
1946             181 : 'z',
1947             182 : 'z',
1948             185 : 'z'
1949         }
1950     },
1951     651 : {
1952         'a' : { 151 : 'a' },
1953         'v' : { 180 : 'v',
1954             151 : 'v',
1955             181 : 'v',
1956             182 : 'v',
1957             185 : 'v'
1958         },
1959         'x' : { 180 : 'x',
1960             151 : 'x',
1961             181 : 'x',
1962             182 : 'x',
1963             185 : 'x'
1964         },
1965         'y' : { 180 : 'y',
1966             151 : 'y',
1967             181 : 'y',
1968             182 : 'y',
1969             185 : 'y'
1970         },
1971         'z' : { 180 : 'z',
1972             151 : 'z',
1973             181 : 'z',
1974             182 : 'z',
1975             185 : 'z'
1976         }
1977     },
1978     655 : {
1979         'a' : { 155 : 'a' },
1980         'v' : { 180 : 'v',
1981             155 : 'v',
1982             181 : 'v',
1983             182 : 'v',
1984             185 : 'v'
1985         },
1986         'x' : { 180 : 'x',
1987             155 : 'x',
1988             181 : 'x',
1989             182 : 'x',
1990             185 : 'x'
1991         },
1992         'y' : { 180 : 'y',
1993             155 : 'y',
1994             181 : 'y',
1995             182 : 'y',
1996             185 : 'y'
1997         },
1998         'z' : { 180 : 'z',
1999             155 : 'z',
2000             181 : 'z',
2001             182 : 'z',
2002             185 : 'z'
2003         }
2004     }
2005 };
2006
2007 function validateAuthority (button) {
2008     var grid = document.getElementById('recGrid');
2009     var label = button.getAttribute('label');
2010
2011     //loop over rows
2012     var rows = grid.lastChild.childNodes;
2013     for (var i = 0; i < rows.length; i++) {
2014         var row = rows[i];
2015         var tag = row.firstChild;
2016
2017         if (!control_map[tag.value]) continue
2018         button.setAttribute('label', label + ' - ' + tag.value);
2019
2020         var ind1 = tag.nextSibling;
2021         var ind2 = ind1.nextSibling;
2022         var subfields = ind2.nextSibling.childNodes;
2023
2024         var tags = {};
2025
2026         for (var j = 0; j < subfields.length; j++) {
2027             var sf = subfields[j];
2028             var sf_code = sf.childNodes[1].value;
2029             var sf_value = sf.childNodes[2].value;
2030
2031             if (!control_map[tag.value][sf_code]) continue;
2032
2033             var found = 0;
2034             for (var a_tag in control_map[tag.value][sf_code]) {
2035                 if (!tags[a_tag]) tags[a_tag] = [];
2036                 tags[a_tag].push({ term : sf_value, subfield : sf_code });
2037             }
2038
2039         }
2040
2041         for (var val_tag in tags) {
2042             var auth_data = validateBibField( [val_tag], tags[val_tag]);
2043             var res = new XML( auth_data.responseText );
2044             found = parseInt(res.gw::payload.gw::string.toString());
2045             if (found) break;
2046         }
2047
2048         // XXX If adt, etc should be validated separately from vxz, etc then move this up into the above for loop
2049         for (var j = 0; j < subfields.length; j++) {
2050             var sf = subfields[j];
2051                if (!found) {
2052                 sf.childNodes[2].inputField.style.color = 'red';
2053             } else {
2054                 sf.childNodes[2].inputField.style.color = 'black';
2055             }
2056         }
2057     }
2058
2059     button.setAttribute('label', label);
2060
2061     return true;
2062 }
2063
2064
2065 function validateBibField (tags, searches) {
2066     var url = "/gateway?input_format=json&format=xml&service=open-ils.search&method=open-ils.search.authority.validate.tag";
2067     url += '&param="tags"&param=' + js2JSON(tags);
2068     url += '&param="searches"&param=' + js2JSON(searches);
2069
2070
2071     var req = new XMLHttpRequest();
2072     req.open('GET',url,false);
2073     req.send(null);
2074
2075     return req;
2076
2077 }
2078 function searchAuthority (term, tag, sf, limit) {
2079     var url = "/gateway?input_format=json&format=xml&service=open-ils.search&method=open-ils.search.authority.fts";
2080     url += '&param="term"&param="' + term + '"';
2081     url += '&param="limit"&param=' + limit;
2082     url += '&param="tag"&param=' + tag;
2083     url += '&param="subfield"&param="' + sf + '"';
2084
2085
2086     var req = new XMLHttpRequest();
2087     req.open('GET',url,false);
2088     req.send(null);
2089
2090     return req;
2091
2092 }
2093