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