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