]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
Improve Firefox/XULRunner Support
[Evergreen.git] / Open-ILS / xul / staff_client / server / cat / copy_editor.js
1 // vim:et:sw=4:ts=4
2 var g = { 'disabled' : false };
3 g.map_acn = {};
4
5 function $(id) { return document.getElementById(id); }
6
7 function my_init() {
8     try {
9         /******************************************************************************************************/
10         /* setup JSAN and some initial libraries */
11
12         if (typeof JSAN == 'undefined') {
13             throw( $('commonStrings').getString('common.jsan.missing') );
14         }
15         JSAN.errorLevel = "die"; // none, warn, or die
16         JSAN.addRepository('/xul/server/');
17         JSAN.use('util.error'); g.error = new util.error();
18         g.error.sdump('D_TRACE','my_init() for cat/copy_editor.xul');
19
20         JSAN.use('util.functional');
21         JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'});
22         JSAN.use('util.network'); g.network = new util.network();
23
24         if (xulG.unified_interface) {
25             $('non_unified_buttons').hidden = true;
26         }
27
28         g.docid = xul_param('docid',{'modal_xulG':true});
29         g.handle_update = xul_param('handle_update',{'modal_xulG':true});
30
31         /******************************************************************************************************/
32         /* Get the copy ids from various sources and flesh them */
33
34         var copy_ids = xul_param('copy_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copy_ids','clear_xpcom':true,'modal_xulG':true});
35         if (!copy_ids) copy_ids = [];
36
37         if (copy_ids.length > 0) g.copies = g.network.simple_request(
38             'FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative',
39             [ copy_ids ]
40         );
41
42         /******************************************************************************************************/
43         /* And other fleshed copies if any */
44
45         if (!g.copies) g.copies = [];
46         var c = xul_param('copies',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_copies','clear_xpcom':true,'modal_xulG':true})
47         if (c) g.copies = g.copies.concat(c);
48
49         /******************************************************************************************************/
50         /* We try to retrieve callnumbers for existing copies, but for new copies, we rely on this */
51
52         g.callnumbers = xul_param('callnumbers',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_callnumbers','clear_xpcom':true,'modal_xulG':true});
53
54         /******************************************************************************************************/
55         /* Get preference (if it exists) for copy location label order */
56
57         g.cl_first = false; // Default to legacy OU first mode
58         var prefs = Components.classes['@mozilla.org/preferences-service;1']
59             .getService(Components.interfaces['nsIPrefBranch']);
60         try {
61             g.cl_first = prefs.getBoolPref('oils.copy_editor.copy_location_name_first');
62         } catch(E) { }
63
64         /******************************************************************************************************/
65         /* Quick fix, this was defined inline in the global scope but now needs g.error and g.copies from my_init */
66         /* Quick fix, messagecatalog only usable during/after onload */
67
68         init_panes0();
69         init_panes();
70
71         /******************************************************************************************************/
72         /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
73
74         if (xul_param('edit',{'modal_xulG':true}) == '1') { 
75
76             g.edit = false;
77
78             if (g.copies.length > 0) { // When loaded in the unified interface, there may be no copies yet (from the volum/item creator) 
79
80                 // Editor desired, but let's check permissions
81
82                 try {
83                     var check = g.network.simple_request(
84                         'PERM_MULTI_ORG_CHECK',
85                         [ 
86                             ses(), 
87                             g.data.list.au[0].id(), 
88                             util.functional.map_list(
89                                 g.copies,
90                                 function (o) {
91                                     var lib;
92                                     var cn_id = o.call_number();
93                                     if (cn_id == -1) {
94                                         lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
95                                     } else {
96                                         if (! g.map_acn[ cn_id ]) {
97                                             var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
98                                             if (typeof req.ilsevent == 'undefined') {
99                                                 g.map_acn[ cn_id ] = req;
100                                                 lib = g.map_acn[ cn_id ].owning_lib();
101                                             } else {
102                                                 lib = o.circ_lib();
103                                             }
104                                         } else {
105                                             lib = g.map_acn[ cn_id ].owning_lib();
106                                         }
107                                     }
108                                     return typeof lib == 'object' ? lib.id() : lib;
109                                 }
110                             ),
111                             g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
112                         ]
113                     );
114                     g.edit = check.length == 0;
115                 } catch(E) {
116                     g.error.standard_unexpected_error_alert('batch permission check',E);
117                 }
118
119             }
120
121             if (g.edit) {
122                 $('caption').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.caption')); 
123                 $('save').setAttribute('hidden','false'); 
124             } else {
125                 $('top_nav').setAttribute('hidden','true');
126             }
127
128             g.retrieve_templates();
129
130         } else {
131             $('top_nav').setAttribute('hidden','true');
132         }
133
134         g.panes_and_field_names.left_pane = 
135             [
136                 [
137                     $('catStrings').getString('staff.cat.copy_editor.status'),
138                     { 
139                         render: 'typeof fm.status() == "object" ? fm.status().name() : g.data.hash.ccs[ fm.status() ].name()', 
140                         input: g.safe_to_edit_copy_status() ? 'c = function(v){ g.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.ccs, function(obj) { return [ obj.name(), obj.id(), typeof my_constants.magical_statuses[obj.id()] != "undefined" ? true : false ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);' : undefined,
141                         //input: 'c = function(v){ g.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list( g.data.list.ccs, function(obj) { return typeof my_constants.magical_statuses[obj.id()] == "undefined"; } ), function(obj) { return [ obj.name(), obj.id() ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
142                     }
143                 ]
144             ].concat(g.panes_and_field_names.left_pane);
145
146
147         /******************************************************************************************************/
148         /* Show the Record Details? */
149
150         var bdb = document.getElementById('brief_display_box'); while(bdb.firstChild) bdb.removeChild(bdb.lastChild);
151         if (g.docid) {
152             var brief_display = document.createElement('iframe'); bdb.appendChild(brief_display); 
153             brief_display.setAttribute( 'src', urls.XUL_BIB_BRIEF + '?docid=' + g.docid); // this is a modal window, so can't push in xulG
154             brief_display.setAttribute( 'flex','1' );
155         }
156
157         /******************************************************************************************************/
158         /* Add stat cats to the panes_and_field_names.right_pane4 */
159
160         g.populate_stat_cats();
161
162         /******************************************************************************************************/
163         /* Backup copies :) */
164
165         g.original_copies = js2JSON( g.copies );
166
167         /******************************************************************************************************/
168         /* Do it */
169
170         g.summarize( g.copies );
171         g.render();
172         g.check_for_unmet_required_fields();
173
174         if (xulG.unified_interface) {
175             xulG.disable_copy_editor = function(c) {
176                 addCSSClass(document.documentElement,'disabled_copy_editor');
177                 g.disabled = true;
178             }
179             xulG.enable_copy_editor = function(c) {
180                 removeCSSClass(document.documentElement,'disabled_copy_editor');
181                 g.disabled = false;
182                 xulG.refresh_copy_editor();
183             }
184             xulG.refresh_copy_editor = function() {
185                 dump('refresh_copy_editor\n');
186                 addCSSClass(document.documentElement,'enabling_copy_editor');
187                 try {
188                     xulG.clear_update_copy_editor_timeout();
189                     g.copies = xulG.copies;
190                     g.edit = g.copies.length > 0;
191                     if (g.edit) {
192                         $('caption').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.caption'));
193                     }
194                     g.original_copies = js2JSON( g.copies );
195                     g.hide_copy_notes_button();
196                     for (var i = 0; i < g.applied_templates.length; i++) {
197                         g._apply_template( g.applied_templates[i], false);
198                     }
199                     if (g.copies.length > 0) {
200                         // Stop tracking these templates once they're applied
201                         // to actual copies
202                         g.applied_templates = [];
203                     }
204                     g.summarize( g.copies );
205                     g.render();
206                     g.check_for_unmet_required_fields();
207                     setTimeout(
208                         function() {
209                             removeCSSClass(document.documentElement,'enabling_copy_editor');
210                         }, 1000
211                     );
212                 } catch(E) {
213                     alert('Error in copy_editor.js, xulG.refresh_copy_editor(): ' + E);
214                 }
215             };
216             xulG.unlock_copy_editor = function() {
217                 oils_unlock_page();
218             };
219             xulG.notify_of_templatable_field_change = function(id,v) {
220                 g.changed[ 'volume_copy_creator.'+id ] = { 'type' : 'volume_copy_creator', 'field' : id, 'value' : v };
221             }
222         } else {
223             g.hide_copy_notes_button();
224         }
225
226         JSAN.use('util.hide');
227         util.hide.generate_css('ui.hide_copy_editor_fields');
228
229     } catch(E) {
230         var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/copy_editor.js', E]);
231         try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
232         alert(err_msg);
233     }
234 }
235
236 /******************************************************************************************************/
237 /* Show copy notes button */
238 g.hide_copy_notes_button = function() {
239     if (g.copies.length > 0 && g.copies[0].id() < 0) {
240         document.getElementById('copy_notes').setAttribute('hidden','true');
241         $('save').setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.create_copies'));
242         $('save').setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.create_copies.accesskey'));
243     }
244     if (g.copies.length != 1) {
245         document.getElementById('copy_notes').setAttribute('hidden','true');
246     }
247 }
248
249 /******************************************************************************************************/
250 /* Retrieve Templates */
251
252 g.retrieve_templates = function() {
253     try {
254         JSAN.use('util.widgets'); JSAN.use('util.functional');
255         g.templates = {};
256         var robj = g.network.simple_request('FM_AUS_RETRIEVE',[ses(),g.data.list.au[0].id()]);
257         if (typeof robj['staff_client.copy_editor.templates'] != 'undefined') {
258             g.templates = robj['staff_client.copy_editor.templates'];
259         }
260         util.widgets.remove_children('template_placeholder');
261         var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } ).sort();
262
263         g.template_menu = util.widgets.make_menulist( list );
264         g.template_menu.setAttribute('id','template_menu');
265         $('template_placeholder').appendChild(g.template_menu);
266         g.template_menu.addEventListener(
267             'command',
268             function() { g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; g.save_attributes(); },
269             false
270         );
271
272         if (xulG.unified_interface) {
273             if (typeof xulG.update_unified_template_list == 'function') {
274                 xulG.update_unified_template_list(list);
275                 // functions the unified wrapper should use to let the item attribute editor do the heavy lifting for templates
276                 xulG.update_item_editor_template_selection = function(new_value) {
277                     g.template_menu.setAttribute('value', new_value);
278                     g.template_menu.value = new_value;
279                     g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value };
280                     g.save_attributes();
281                 }
282                 xulG.item_editor_apply_template = function() { g.apply_template(true); };
283                 xulG.item_editor_delete_template = function() { g.delete_template(); };
284                 xulG.item_editor_save_template = function() { g.save_template(); };
285                 xulG.item_editor_import_templates = function() { g.import_templates(); };
286                 xulG.item_editor_export_templates = function() { g.export_templates(); };
287                 xulG.item_editor_reset = function() { g.reset(); };
288             }
289         }
290
291     } catch(E) {
292         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.retrieve_templates.error'), E);
293     }
294 }
295
296 /******************************************************************************************************/
297 /* Apply Template */
298
299 g.applied_templates = [];
300
301 g.apply_template = function(apply_volume_editor_template_changes) {
302     try {
303         var name = g.template_menu.value;
304         if (g.templates[ name ] != 'undefined') {
305             if (g.copies == 0) {
306                 // We're only tracking these applied templates temporarily,
307                 // specifically when they're used prior to copies being
308                 // created in the unified interface.
309                 g.applied_templates.push( name );
310             }
311             g._apply_template(name,apply_volume_editor_template_changes);
312             g.summarize( g.copies );
313             g.render();
314             g.check_for_unmet_required_fields();
315         }
316     } catch(E) {
317         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E);
318     }
319 }
320
321 g._apply_template = function(name,apply_volume_editor_template_changes) {
322     try {
323         if (g.templates[ name ] != 'undefined') {
324             var template = g.templates[ name ];
325             for (var i in template) {
326                 if (g.is_field_hidden(i)) {
327                     alert($('catStrings').getFormattedString(
328                         'staff.cat.copy_editor.apply_unsafe_field',
329                         [i]
330                     ));
331                     continue;
332                 }
333                 if (template[i].field == 'status') {
334                     if (!g.safe_to_edit_copy_status()) {
335                         alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_unsafe_field',[i]));
336                         continue;
337                     }
338                 }
339                 g.changed[ i ] = template[ i ];
340                 switch( template[i].type ) {
341                     case 'attribute' :
342                         g.apply(template[i].field,template[i].value);
343                     break;
344                     case 'stat_cat' :
345                         if (g.stat_cat_seen[ template[i].field ]) g.apply_stat_cat(template[i].field,template[i].value);
346                     break;
347                     case 'owning_lib' :
348                         g.apply_owning_lib(template[i].value);
349                     break;
350                     case 'volume_copy_creator' :
351                         if (xulG.unified_interface && apply_volume_editor_template_changes) {
352                             xulG.apply_template_to_batch(template[i].field,template[i].value);
353                         }
354                     break;
355                 }
356             }
357         }
358     } catch(E) {
359         alert('Error in copy_editor.js, g._apply_template('+name+'): ' + E);
360     }
361 }
362
363 /******************************************************************************************************/
364 /* Save as Template */
365
366 g.save_template = function() {
367     try {
368         var name = window.prompt(
369             $('catStrings').getString('staff.cat.copy_editor.save_as_template.prompt'),
370             '',
371             $('catStrings').getString('staff.cat.copy_editor.save_as_template.title')
372         );
373         if (!name) return;
374         g.templates[name] = g.changed;
375         var robj = g.network.simple_request(
376             'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
377         );
378         if (typeof robj.ilsevent != 'undefined') {
379             throw(robj);
380         } else {
381             alert($('catStrings').getFormattedString('staff.cat.copy_editor.save_as_template.success', [name]));
382             setTimeout(
383                 function() {
384                     try {
385                         g.retrieve_templates();
386                     } catch(E) {
387                         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.save_as_template.error'), E);
388                     }
389                 },0
390             );
391         }
392     } catch(E) {
393         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.save_as_template.error'), E);
394     }
395 }
396
397 /******************************************************************************************************/
398 /* Delete Template */
399
400 g.delete_template = function() {
401     try {
402         var name = g.template_menu.value;
403         if (!name) return;
404         if (! window.confirm($('catStrings').getFormattedString('staff.cat.copy_editor.delete_template.confirm', [name]))) return;
405         delete(g.templates[name]);
406         var robj = g.network.simple_request(
407             'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
408         );
409         if (typeof robj.ilsevent != 'undefined') {
410             throw(robj);
411         } else {
412             alert($('catStrings').getFormattedString('staff.cat.copy_editor.delete_template.confirm', [name]));
413             setTimeout(
414                 function() {
415                     try {
416                         g.retrieve_templates();
417                     } catch(E) {
418                         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.delete_template.error'), E);
419                     }
420                 },0
421             );
422         }
423     } catch(E) {
424         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.delete_template.error'), E);
425     }
426 }
427
428 /******************************************************************************************************/
429 /* Export Templates */
430
431 g.export_templates = function() {
432     try {
433         JSAN.use('util.file'); var f = new util.file('');
434         f.export_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.export_templates.title'), 'data' : g.templates } );
435     } catch(E) {
436         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.export_templates.error'), E);
437     }
438 }
439
440 /******************************************************************************************************/
441 /* Import Templates */
442
443 g.import_templates = function() {
444     try {
445         JSAN.use('util.file'); var f = new util.file('');
446         var temp = f.import_file( { 'title' : $('catStrings').getString('staff.cat.copy_editor.import_templates.title') } );
447         if (temp) {
448             for (var i in temp) {
449
450                 if (g.templates[i]) {
451
452                     var r = g.error.yns_alert(
453                         $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.prompt') + '\n' + g.error.pretty_print( js2JSON( temp[i] ) ),
454                         $('catStrings').getFormattedString('staff.cat.copy_editor.import_templates.replace.title', [i]),
455                         $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.yes'),
456                         $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.no'),
457                         null,
458                         $('catStrings').getString('staff.cat.copy_editor.import_templates.replace.click_here')
459                     );
460
461                     if (r == 0 /* Yes */) g.templates[i] = temp[i];
462
463                 } else {
464
465                     g.templates[i] = temp[i];
466
467                 }
468
469             }
470
471             var r = g.error.yns_alert(
472                 $('catStrings').getString('staff.cat.copy_editor.import_templates.save.prompt'),
473                 $('catStrings').getFormattedString('staff.cat.copy_editor.import_templates.save.title'),
474                 $('catStrings').getString('staff.cat.copy_editor.import_templates.save.yes'),
475                 $('catStrings').getString('staff.cat.copy_editor.import_templates.save.no'),
476                 null,
477                 $('catStrings').getString('staff.cat.copy_editor.import_templates.save.click_here')
478             );
479
480             if (r == 0 /* Yes */) {
481                 var robj = g.network.simple_request(
482                     'FM_AUS_UPDATE',[ses(),g.data.list.au[0].id(), { 'staff_client.copy_editor.templates' : g.templates }]
483                 );
484                 if (typeof robj.ilsevent != 'undefined') {
485                     throw(robj);
486                 } else {
487                     alert($('catStrings').getString('staff.cat.copy_editor.import_templates.save.success'));
488                     setTimeout(
489                         function() {
490                             try {
491                                 g.retrieve_templates();
492                             } catch(E) {
493                                 g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.import_templates.save.error'), E);
494                             }
495                         },0
496                     );
497                 }
498             } else {
499                 util.widgets.remove_children('template_placeholder');
500                 var list = util.functional.map_object_to_list( g.templates, function(obj,i) { return [i, i]; } );
501                 g.template_menu = util.widgets.make_menulist( list );
502                 $('template_placeholder').appendChild(g.template_menu);
503                 alert($('catStrings').getString('staff.cat.copy_editor.import_templates.note'));
504             }
505
506         }
507     } catch(E) {
508         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.import_templates.error'), E);
509     }
510 }
511
512
513 /******************************************************************************************************/
514 /* Restore backup copies */
515
516 g.reset = function() {
517     g.applied_templates = [];
518     g.changed = {};
519     g.copies = JSON2js( g.original_copies );
520     g.summarize( g.copies );
521     g.render();
522     g.check_for_unmet_required_fields();
523     oils_unlock_page();
524     if (xulG.unified_interface) {
525         xulG.reset_batch_menus();
526     }
527 }
528
529 /******************************************************************************************************/
530 /* Apply a value to a specific field on all the copies being edited */
531
532 g.apply = function(field,value) {
533     g.error.sdump('D_TRACE','applying field = <' + field + '>  value = <' + value + '>\n');
534     if (value == '<HACK:KLUDGE:NULL>') {
535         value = null;
536     }
537     if (field == 'alert_message') { value = value.replace(/^\W+$/g,''); }
538     if (field == 'price' || field == 'deposit_amount') {
539         if (value == '') {
540             value = null;
541         } else {
542             JSAN.use('util.money'); value = util.money.sanitize( value );
543         }
544     }
545     for (var i = 0; i < g.copies.length; i++) {
546         var copy = g.copies[i];
547         try {
548             copy[field]( value ); copy.ischanged('1');
549         } catch(E) {
550             alert(E);
551         }
552     }
553
554     oils_lock_page();
555 }
556
557 /******************************************************************************************************/
558 /* Apply a stat cat entry to all the copies being edited.  An entry_id of < 0 signifies the stat cat is being removed. */
559
560 g.apply_stat_cat = function(sc_id,entry_id) {
561     g.error.sdump('D_TRACE','sc_id = ' + sc_id + '  entry_id = ' + entry_id + '\n');
562     for (var i = 0; i < g.copies.length; i++) {
563         var copy = g.copies[i];
564         try {
565             copy.ischanged('1');
566             var temp = copy.stat_cat_entries();
567             if (!temp) temp = [];
568             temp = util.functional.filter_list(
569                 temp,
570                 function (obj) {
571                     return (obj.stat_cat() != sc_id);
572                 }
573             );
574             if (entry_id > -1) {
575                 temp.push( 
576                     util.functional.find_id_object_in_list( 
577                         g.data.hash.asc[sc_id].entries(), 
578                         entry_id
579                     )
580                 );
581             }
582             copy.stat_cat_entries( temp );
583
584         } catch(E) {
585             g.error.standard_unexpected_error_alert('apply_stat_cat',E);
586         }
587     }
588
589     oils_lock_page();
590 }
591
592 /******************************************************************************************************/
593 /* Apply an "owning lib" to all the copies being edited.  That is, change and auto-vivicating volumes */
594
595 g.apply_owning_lib = function(ou_id) {
596     g.error.sdump('D_TRACE','ou_id = ' + ou_id + '\n');
597     // but don't allow this when bundled with the volume/copy creator UI, or if we're editing pre-cats
598     if (! g.safe_to_change_owning_lib() ) { return; }
599     for (var i = 0; i < g.copies.length; i++) {
600         var copy = g.copies[i];
601         try {
602             if (!g.map_acn[copy.call_number()]) {
603                 var volume = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ copy.call_number() ]);
604                 if (typeof volume.ilsevent != 'undefined') {
605                     g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.undefined_volume.error', [copy.barcode()]), volume);
606                     continue;
607                 }
608                 g.map_acn[copy.call_number()] = volume;
609             }
610             var old_volume = g.map_acn[copy.call_number()];
611             var acn_blob = g.network.simple_request(
612                 'FM_ACN_FIND_OR_CREATE',
613                 [ses(),old_volume.label(),old_volume.record(),ou_id,old_volume.prefix().id(),old_volume.suffix().id(),old_volume.label_class().id()]
614             );
615             if (typeof acn_blob.ilsevent != 'undefined') {
616                 g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.call_number.error', [copy.barcode()]), acn_blob);
617                 continue;
618             }
619             copy.call_number(acn_blob.acn_id);
620             copy.ischanged('1');
621         } catch(E) {
622             g.error.standard_unexpected_error_alert('apply_stat_cat',E);
623         }
624     }
625
626     oils_lock_page();
627 }
628
629 /******************************************************************************************************/
630 /* This returns false if any of the copies being edited are pre-cats, or if we're embedded in the unified volume/copy UI */
631
632 g.safe_to_change_owning_lib = function() {
633     try {
634         if (xulG.unified_interface) { return false; }
635         var safe = true;
636         for (var i = 0; i < g.copies.length; i++) {
637             var cn = g.copies[i].call_number();
638             if (typeof cn == 'object') { cn = cn.id(); }
639             if (cn == -1) { safe = false; }
640         }
641         return safe;
642     } catch(E) {
643         g.error.standard_unexpected_error_alert('safe_to_change_owning_lib?',E);
644         return false;
645     }
646 }
647
648 /******************************************************************************************************/
649 /* This returns true if none of the copies being edited have a magical status found in my_constants.magical_statuses */
650
651 g.safe_to_edit_copy_status = function() {
652     try {
653         var safe = true;
654         for (var i = 0; i < g.copies.length; i++) {
655             var status = g.copies[i].status(); if (typeof status == 'object') status = status.id();
656             if (typeof my_constants.magical_statuses[ status ] != 'undefined') safe = false;
657         }
658         return safe;
659     } catch(E) {
660         g.error.standard_unexpected_error_alert('safe_to_edit_copy_status?',E);
661         return false;
662     }
663 }
664
665 /******************************************************************************************************/
666 /* This returns true if the field has been hidden via util.hide */
667
668 g.is_field_hidden = function(field) {
669     try {
670         g.data.stash_retrieve();
671         if (g.data.hash.aous['ui.hide_copy_editor_fields']
672             && g.data.hash.aous['ui.hide_copy_editor_fields'].indexOf(field) > -1) {
673             return true;
674         }
675     } catch(E) {
676         g.error.standard_unexpected_error_alert('is_field_hidden?',E);
677         return false;
678     }
679 }
680
681
682 /******************************************************************************************************/
683 /* This concats and uniques all the alert messages for use as the default value for a new alert message */
684
685 g.populate_alert_message_input = function(tb) {
686     try {
687         var seen = {}; var s = '';
688         for (var i = 0; i < g.copies.length; i++) {
689             var msg = g.copies[i].alert_message(); 
690             if (msg) {
691                 if (typeof seen[msg] == 'undefined') {
692                     s += msg + '\n';
693                     seen[msg] = true;
694                 }
695             }
696         }
697         tb.setAttribute('value',s);
698     } catch(E) {
699         g.error.standard_unexpected_error_alert('populate_alert_message_input',E);
700     }
701 }
702
703 /***************************************************************************************************************/
704 /* This returns a list of acpl's appropriate for the copies being edited (and caches them in the global stash) */
705
706 g.get_acpl_list_for_lib = function(lib_id,but_only_these) {
707     g.data.stash_retrieve();
708     var label = 'acpl_list_for_lib_'+lib_id;
709     if (typeof g.data[label] == 'undefined') {
710         var robj = g.network.simple_request('FM_ACPL_RETRIEVE', [ lib_id ]); // This returns acpl's for all ancestors and descendants as well as the lib
711         if (typeof robj.ilsevent != 'undefined') throw(robj);
712         var temp_list = [];
713         for (var j = 0; j < robj.length; j++) {
714             var my_acpl = robj[j];
715             if (typeof g.data.hash.acpl[ my_acpl.id() ] == 'undefined') {
716                 g.data.hash.acpl[ my_acpl.id() ] = my_acpl;
717                 g.data.list.acpl.push( my_acpl );
718             }
719             var only_this_lib = my_acpl.owning_lib(); if (!only_this_lib) continue;
720             if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
721             if (but_only_these.indexOf( String( only_this_lib ) ) != -1) { // This filters out some of the libraries (usually the descendants)
722                 temp_list.push( my_acpl );
723             }
724         }
725         g.data[label] = temp_list; g.data.stash(label,'hash','list');
726     }
727     return g.data[label];
728 }
729
730 /******************************************************************************************************/
731 /* This returns a list of acpl's appropriate for the copies being edited */
732
733 g.get_acpl_list = function() {
734     try {
735
736         JSAN.use('util.functional');
737
738         var my_acpls = {};
739
740         /**************************************/
741         /* get owning libs from call numbers */
742
743         var owning_libs = {}; 
744         for (var i = 0; i < g.copies.length; i++) {
745             var callnumber = g.copies[i].call_number();
746             if (!callnumber) continue;
747             var cn_id = typeof callnumber == 'object' ? callnumber.id() : callnumber;
748             if (cn_id > 0) {
749                 if (! g.map_acn[ cn_id ]) {
750                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
751                     if (typeof req.ilsevent == 'undefined') {
752                         g.map_acn[ cn_id ] = req;
753                     } else {
754                         continue;
755                     }
756                 }
757                 var consider_lib = g.map_acn[ cn_id ].owning_lib();
758                 if (!consider_lib) continue;
759                 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
760             }
761         }
762         if (g.callnumbers) {
763             for (var i in g.callnumbers) {
764                 var consider_lib = g.callnumbers[i].owning_lib;
765                 if (!consider_lib) continue;
766                 owning_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
767             }
768         }
769
770         /***************************************************************************************************/
771         /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
772
773         JSAN.use('util.fm_utils');
774         var libs = []; for (var i in owning_libs) libs.push(i);
775         if (libs.length > 0) {
776             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
777             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
778
779             if (ancestor) {
780                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
781                 var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
782                 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
783                     if (acpl_list[i] != null) {
784                         my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
785                     }
786                 }
787             }
788         }
789         
790         /*****************/
791         /* get circ libs */
792
793         var circ_libs = {};
794
795         for (var i = 0; i < g.copies.length; i++) {
796             var consider_lib = g.copies[i].circ_lib();
797             if (!consider_lib) continue;
798             circ_libs[ typeof consider_lib == 'object' ? consider_lib.id() : consider_lib ] = true;
799         }
800
801         /***************************************************************************************************/
802         /* now find the first ancestor they all have in common, get the acpl's for it and higher ancestors */
803
804         libs = []; for (var i in circ_libs) libs.push(i);
805         if (libs.length > 0) {
806             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
807             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
808
809             if (ancestor) {
810                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
811                 var acpl_list = g.get_acpl_list_for_lib(ancestor, ancestors);
812                 if (acpl_list) for (var i = 0; i < acpl_list.length; i++) {
813                     if (acpl_list[i] != null) {
814                         my_acpls[ typeof acpl_list[i] == 'object' ? acpl_list[i].id() : acpl_list[i] ] = true;
815                     }
816                 }
817             }
818         }
819
820         var acpl_list = []; for (var i in my_acpls) acpl_list.push( g.data.hash.acpl[ i ] );
821         return acpl_list.sort(
822             function(a,b) {
823                 var label_a = g.data.hash.aou[ a.owning_lib() ].shortname() + ' : ' + a.name();
824                 var label_b = g.data.hash.aou[ b.owning_lib() ].shortname() + ' : ' + b.name();
825                 if (label_a < label_b) return -1;
826                 if (label_a > label_b) return 1;
827                 return 0;
828             }
829         );
830     
831     } catch(E) {
832         g.error.standard_unexpected_error_alert('get_acpl_list',E);
833         return [];
834     }
835 }
836
837
838 /******************************************************************************************************/
839 /* This keeps track of which fields have been edited for styling purposes */
840
841 g.changed = {};
842
843 /******************************************************************************************************/
844 /* This keeps track of which fields are required, and which fields have been populated */
845
846 g.required = {};
847 g.populated = {};
848
849 /******************************************************************************************************/
850 /* These need data from the middle layer to render */
851
852 function init_panes0() {
853 g.special_exception = {};
854 g.special_exception[$('catStrings').getString('staff.cat.copy_editor.field.owning_library.label')] = function(label,value) {
855         JSAN.use('util.widgets');
856         if (value>0) { /* an existing call number */
857             g.network.simple_request(
858                 'FM_ACN_RETRIEVE.authoritative',
859                 [ value ],
860                 function(req) {
861                     var cn = '??? id = ' + value;
862                     try {
863                         cn = req.getResultObject();
864                     } catch(E) {
865                         g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
866                     }
867                     util.widgets.set_text(label,g.data.hash.aou[ cn.owning_lib() ].shortname() + ' : ' + cn.label());
868                 }
869             );
870         } else { /* a yet to be created call number */
871             if (g.callnumbers) {
872                 util.widgets.set_text(label,g.data.hash.aou[ g.callnumbers[value].owning_lib ].shortname() + ' : ' + g.callnumbers[value].label);
873             }
874         }
875     };
876 g.special_exception[$('catStrings').getString('staff.cat.copy_editor.field.creator.label')] = function(label,value) {
877         if (!Number(value)) return;
878         g.network.simple_request(
879             'FM_AU_RETRIEVE_VIA_ID',
880             [ ses(), value ],
881             function(req) {
882                 var p = '??? id = ' + value;
883                 try {
884                     p = req.getResultObject();
885                     p = p.usrname();
886
887                 } catch(E) {
888                     g.error.sdump('D_ERROR','patron retrieve: ' + E);
889                 }
890                 JSAN.use('util.widgets');
891                 util.widgets.set_text(label,p);
892             }
893         );
894     };
895 g.special_exception[$('catStrings').getString('staff.cat.copy_editor.field.last_editor.label')] = function(label,value) {
896         if (!Number(value)) return;
897         g.network.simple_request(
898             'FM_AU_RETRIEVE_VIA_ID',
899             [ ses(), value ],
900             function(req) {
901                 var p = '??? id = ' + value;
902                 try {
903                     p = req.getResultObject();
904                     p = p.usrname();
905
906                 } catch(E) {
907                     g.error.sdump('D_ERROR','patron retrieve: ' + E);
908                 }
909                 util.widgets.set_text(label,p);
910             }
911         );
912     };
913 }
914
915 /******************************************************************************************************/
916 g.readonly_stat_cat_names = [];
917 g.editable_stat_cat_names = [];
918
919 /******************************************************************************************************/
920 /* These get show in the left panel */
921
922 function init_panes() {
923 g.panes_and_field_names = {
924
925     'left_pane' :
926 [
927     [
928         $('catStrings').getString('staff.cat.copy_editor.field.barcode.label'),
929         {
930             render: 'fm.barcode();',
931         }
932     ], 
933     [
934         $('catStrings').getString('staff.cat.copy_editor.field.creation_date.label'),
935         { 
936             render: 'util.date.formatted_date( fm.create_date(), "%F");',
937         }
938     ],
939     [
940         $('catStrings').getString('staff.cat.copy_editor.field.active_date.label'),
941         { 
942             render: 'util.date.formatted_date( fm.active_date(), "%F");',
943         }
944     ],
945     [
946         $('catStrings').getString('staff.cat.copy_editor.field.creator.label'),
947         { 
948             render: 'fm.creator();',
949         }
950     ],
951     [
952         $('catStrings').getString('staff.cat.copy_editor.field.last_edit_date.label'),
953         { 
954             render: 'util.date.formatted_date( fm.edit_date(), "%F");',
955         }
956     ],
957     [
958         $('catStrings').getString('staff.cat.copy_editor.field.last_editor.label'),
959         {
960             render: 'fm.editor();',
961         }
962     ],
963
964 ],
965
966 'right_pane' :
967 [
968     [
969         $('catStrings').getString('staff.cat.copy_editor.field.location.label'),
970         { 
971             render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
972             input: 'c = function(v){ g.apply("location",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.get_acpl_list(), function(obj) { return [ ' + (g.cl_first ? 'obj.name() + " : " + g.data.hash.aou[ obj.owning_lib() ].shortname()' : 'g.data.hash.aou[ obj.owning_lib() ].shortname() + " : " + obj.name()') + ', obj.id() ]; }).sort()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
973
974         }
975     ],
976     [
977         $('catStrings').getString('staff.cat.copy_editor.field.circulation_library.label'),
978         {     
979             render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
980             //input: 'c = function(v){ g.apply("circ_lib",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list(g.data.list.my_aou, function(obj) { return g.data.hash.aout[ obj.ou_type() ].can_have_vols(); }), function(obj) { return [ obj.shortname(), obj.id() ]; }).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
981             input: 'c = function(v){ g.apply("circ_lib",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.aou, function(obj) { var sname = obj.shortname(); for (i = sname.length; i < 20; i++) sname += " "; return [ obj.name() ? sname + " " + obj.name() : obj.shortname(), obj.id(), ( ! get_bool( g.data.hash.aout[ obj.ou_type() ].can_have_vols() ) ), ( g.data.hash.aout[ obj.ou_type() ].depth() * 2), ]; }), g.data.list.au[0].ws_ou()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
982         } 
983     ],
984     [
985         $('catStrings').getString('staff.cat.copy_editor.field.owning_library.label'),
986         {
987             render: 'fm.call_number();',
988             input: g.safe_to_change_owning_lib() ? 'c = function(v){ g.apply_owning_lib(v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( g.data.list.aou, function(obj) { var sname = obj.shortname(); for (i = sname.length; i < 20; i++) sname += " "; return [ obj.name() ? sname + " " + obj.name() : obj.shortname(), obj.id(), ( ! get_bool( g.data.hash.aout[ obj.ou_type() ].can_have_vols() ) ), ( g.data.hash.aout[ obj.ou_type() ].depth() * 2), ]; }), g.data.list.au[0].ws_ou()); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);' : undefined,
989         }
990     ],
991     [
992         $('catStrings').getString('staff.cat.copy_editor.field.copy_number.label'),
993         { 
994             render: 'fm.copy_number() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : fm.copy_number()',
995             input: 'c = function(v){ g.apply("copy_number",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
996         }
997     ],
998
999
1000 ],
1001
1002 'right_pane2' :
1003 [
1004     [
1005         $('catStrings').getString('staff.cat.copy_editor.field.circulate.label'),
1006         {     
1007             render: 'fm.circulate() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.circulate() ) ? $("catStrings").getString("staff.cat.copy_editor.field.circulate.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.circulate.no_or_false") )',
1008             input: 'c = function(v){ g.apply("circulate",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.circulate.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.circulate.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1009         }
1010     ],
1011     [
1012         $('catStrings').getString('staff.cat.copy_editor.field.holdable.label'),
1013         { 
1014             render: 'fm.holdable() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.holdable() ) ? $("catStrings").getString("staff.cat.copy_editor.field.holdable.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.holdable.no_or_false") )',
1015             input: 'c = function(v){ g.apply("holdable",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.holdable.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.holdable.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1016         }
1017     ],
1018     [
1019         $('catStrings').getString('staff.cat.copy_editor.field.age_based_hold_protection.label'),
1020         {
1021             render: 'fm.age_protect() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( typeof fm.age_protect() == "object" ? fm.age_protect().name() : g.data.hash.crahp[ fm.age_protect() ].name() )', 
1022             input: 'c = function(v){ g.apply("age_protect",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.remove_age_based_hold_protection"), "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.crahp, function(obj) { return [ obj.name(), obj.id() ]; }).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1023         }
1024
1025     ],
1026     [
1027         $('catStrings').getString('staff.cat.copy_editor.field.floating.label'),
1028         { 
1029             render: 'fm.floating() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.floating() ) ? $("catStrings").getString("staff.cat.copy_editor.field.floating.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.floating.no_or_false") )',
1030             input: 'c = function(v){ g.apply("floating",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.floating.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.floating.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1031         }
1032     ],
1033     [
1034         $('catStrings').getString('staff.cat.copy_editor.field.loan_duration.label'),
1035         { 
1036             render: 'switch(Number(fm.loan_duration())){ case 1: $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.short"); break; case 2: $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.normal"); break; case 3: $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.extended"); break; }',
1037             input: 'c = function(v){ g.apply("loan_duration",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.short"), "1" ], [ $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.normal"), "2" ], [ $("catStrings").getString("staff.cat.copy_editor.field.loan_duration.extended"), "3" ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1038
1039         }
1040     ],
1041     [
1042         $('catStrings').getString('staff.cat.copy_editor.field.fine_level.label'),
1043         {
1044             render: 'switch(Number(fm.fine_level())){ case 1: $("catStrings").getString("staff.cat.copy_editor.field.fine_level.low"); break; case 2: $("catStrings").getString("staff.cat.copy_editor.field.fine_level.normal"); break; case 3: $("catStrings").getString("staff.cat.copy_editor.field.fine_level.high"); break; }',
1045             input: 'c = function(v){ g.apply("fine_level",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.fine_level.low"), "1" ], [ $("catStrings").getString("staff.cat.copy_editor.field.fine_level.normal"), "2" ], [ $("catStrings").getString("staff.cat.copy_editor.field.fine_level.high"), "3" ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1046         }
1047     ],
1048
1049      [
1050         $('catStrings').getString('staff.cat.copy_editor.field.circulate_as_type.label'),
1051         {     
1052             render: 'fm.circ_as_type() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : g.data.hash.citm[ fm.circ_as_type() ].value',
1053             input: 'c = function(v){ g.apply("circ_as_type",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.remove_circulate_as_type"), "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.citm, function(n){return [ n.code + " - " + n.value, n.code];} ).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1054         } 
1055     ],
1056     [
1057         $('catStrings').getString('staff.cat.copy_editor.field.circulation_modifier.label'),
1058         {    
1059             render: 'fm.circ_modifier() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : $("commonStrings").getFormattedString("staff.circ_modifier.display",[fm.circ_modifier(),g.data.hash.ccm[fm.circ_modifier()].name(),g.data.hash.ccm[fm.circ_modifier()].description()])',
1060             input: 'c = function(v){ g.apply("circ_modifier",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null"), "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.ccm, function(obj) { return [ $("commonStrings").getFormattedString("staff.circ_modifier.display",[obj.code(),obj.name(),obj.description()]), obj.code() ]; } ).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1061         }
1062     ],
1063 ],
1064
1065 'right_pane3' :
1066 [    [
1067         $('catStrings').getString('staff.cat.copy_editor.field.alert_message.label'),
1068         {
1069             render: 'fm.alert_message() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : fm.alert_message()',
1070             input: 'c = function(v){ g.apply("alert_message",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.setAttribute("multiline",true); g.populate_alert_message_input(x); x.addEventListener("apply",function(f){ return function(ev) { f( ev.target.value ); } }(c), false);',
1071         }
1072     ],
1073
1074     [
1075         $('catStrings').getString('staff.cat.copy_editor.field.deposit.label'),
1076         { 
1077             render: 'fm.deposit() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.deposit() ) ? $("catStrings").getString("staff.cat.copy_editor.field.deposit.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.deposit.no_or_false") )',
1078             input: 'c = function(v){ g.apply("deposit",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.deposit.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.deposit.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1079         }
1080     ],
1081     [
1082         $('catStrings').getString('staff.cat.copy_editor.field.deposit_amount.label'),
1083         { 
1084             render: 'if (fm.deposit_amount() == null) { $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null"); } else { util.money.sanitize( fm.deposit_amount() ); }',
1085             input: 'c = function(v){ g.apply("deposit_amount",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1086         }
1087     ],
1088     [
1089         $('catStrings').getString('staff.cat.copy_editor.field.price.label'),
1090         { 
1091             render: 'if (fm.price() == null) { $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null"); } else { util.money.sanitize( fm.price() ); }', 
1092             input: 'c = function(v){ g.apply("price",v); if (typeof post_c == "function") post_c(v); }; x = document.createElement("textbox"); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1093         }
1094     ],
1095
1096     [
1097         $('catStrings').getString('staff.cat.copy_editor.field.opac_visible.label'),
1098         { 
1099             render: 'fm.opac_visible() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.opac_visible() ) ? $("catStrings").getString("staff.cat.copy_editor.field.opac_visible.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.opac_visible.no_or_false") )', 
1100             input: 'c = function(v){ g.apply("opac_visible",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.opac_visible.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.opac_visible.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1101         }
1102     ],
1103     [
1104         $('catStrings').getString('staff.cat.copy_editor.field.reference.label'),
1105         { 
1106             render: 'fm.ref() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : ( get_bool( fm.ref() ) ? $("catStrings").getString("staff.cat.copy_editor.field.reference.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.reference.no_or_false") )', 
1107             input: 'c = function(v){ g.apply("ref",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.reference.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.reference.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1108         }
1109     ],
1110     [
1111         $('catStrings').getString('staff.cat.copy_editor.field.mint_condition.label'),
1112         { 
1113             render: 'get_bool( fm.mint_condition() ) ? $("catStrings").getString("staff.cat.copy_editor.field.mint_condition.yes_or_true") : $("catStrings").getString("staff.cat.copy_editor.field.mint_condition.no_or_false")', 
1114             input: 'c = function(v){ g.apply("mint_condition",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.field.mint_condition.yes_or_true"), get_db_true() ], [ $("catStrings").getString("staff.cat.copy_editor.field.mint_condition.no_or_false"), get_db_false() ] ] ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
1115         }
1116     ]
1117 ],
1118
1119 'right_pane4' : 
1120 [
1121 ]
1122
1123 };
1124 }
1125
1126 /******************************************************************************************************/
1127 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
1128
1129 g.summarize = function( copies ) {
1130     /******************************************************************************************************/
1131     /* Setup */
1132
1133     JSAN.use('util.date'); JSAN.use('util.money');
1134     g.summary = {};
1135     g.field_names = [];
1136     for (var i in g.panes_and_field_names) {
1137         g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
1138     }
1139     g.field_names = g.field_names.concat( g.editable_stat_cat_names );
1140     g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
1141
1142     /******************************************************************************************************/
1143     /* Loop through the field names */
1144
1145     for (var i = 0; i < g.field_names.length; i++) {
1146
1147         var field_name = g.field_names[i][0];
1148         var render = g.field_names[i][1].render;
1149         var attr = g.field_names[i][1].attr;
1150         g.summary[ field_name ] = {};
1151         g.populated[ field_name ] = 1; // delete later if we encounter a copy with the field unset
1152
1153         /******************************************************************************************************/
1154         /* Loop through the copies */
1155
1156         for (var j = 0; j < copies.length; j++) {
1157
1158             var fm = copies[j];
1159             var cmd = render || ('fm.' + field_name + '();');
1160             var value = $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");
1161
1162             /**********************************************************************************************/
1163             /* Try to retrieve the value for this field for this copy */
1164
1165             try { 
1166                 value = eval( cmd ); 
1167             } catch(E) { 
1168                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
1169             }
1170             if (typeof value == 'object' && value != null) {
1171                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
1172             }
1173             if (value == $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null")) {
1174                 delete g.populated[field_name];
1175             }
1176
1177             /**********************************************************************************************/
1178             /* Tally the count */
1179
1180             if (g.summary[ field_name ][ value ]) {
1181                 g.summary[ field_name ][ value ]++;
1182             } else {
1183                 g.summary[ field_name ][ value ] = 1;
1184             }
1185         }
1186     }
1187     g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
1188 }
1189
1190 /******************************************************************************************************/
1191 /* Display the summarized data and inputs for editing */
1192
1193 g.render = function() {
1194
1195     /******************************************************************************************************/
1196     /* Library setup and clear any existing interface */
1197
1198     JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
1199
1200     for (var i in g.panes_and_field_names) {
1201         var p = document.getElementById(i);
1202         if (p) util.widgets.remove_children(p);
1203     }
1204
1205     /******************************************************************************************************/
1206     /* Populate the library filter menu for stat cats */
1207
1208     var sc_libs = {};
1209     for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
1210         sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
1211     }
1212     var sc_libs2 = [];
1213     for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
1214     sc_libs2.sort();
1215     var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
1216     JSAN.use('util.widgets'); util.widgets.remove_children(x);
1217     for (var i = 0; i < sc_libs2.length; i++) {
1218         var menuitem = document.createElement('menuitem');
1219         menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
1220         menuitem.setAttribute('type','checkbox');
1221         menuitem.setAttribute('checked','true');
1222         menuitem.setAttribute('label',sc_libs2[i][0]);
1223         menuitem.setAttribute('value',sc_libs2[i][1]);
1224         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
1225         x.appendChild(menuitem);
1226     }
1227
1228     /******************************************************************************************************/
1229     /* Prepare the panes */
1230
1231     var groupbox; var caption; var vbox; var grid; var rows;
1232     
1233     /******************************************************************************************************/
1234     /* Loop through the field names */
1235
1236     for (h in g.panes_and_field_names) {
1237         if (!document.getElementById(h)) continue;
1238         for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1239             try {
1240                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1241                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1242                 groupbox.setAttribute('hideable',fn);
1243                 if (attr) {
1244                     for (var a in attr) {
1245                         groupbox.setAttribute(a,attr[a]);
1246                     }
1247                 }
1248                 caption = document.createElement('caption'); groupbox.appendChild(caption);
1249                 caption.setAttribute('label',fn);
1250                 caption.setAttribute('id','caption_'+fn); // used for focus/keyboard navigation
1251                 caption.setAttribute('hideable',fn);
1252                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox); // main display widget goes here
1253                 if (typeof g.changed[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_changed'); }
1254                 if (typeof g.required[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_required'); }
1255                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1256                 grid.setAttribute('flex','1');
1257                 rows = grid.lastChild;
1258                 var row;
1259                 
1260                 /**************************************************************************************/
1261                 /* Loop through each value for the field */
1262
1263                 for (var j in g.summary[fn]) {
1264                     var value = j; var count = g.summary[fn][j];
1265                     row = document.createElement('row'); rows.appendChild(row);
1266                     var label1 = document.createElement('description'); row.appendChild(label1);
1267                     if (g.special_exception[ fn ]) {
1268                         g.special_exception[ fn ]( label1, value );
1269                     } else {
1270                         label1.appendChild( document.createTextNode(value) );
1271                     }
1272                     var label2 = document.createElement('description'); row.appendChild(label2);
1273                     var copy_count;
1274                     if (count == 1) {
1275                         copy_count = $('catStrings').getString('staff.cat.copy_editor.copy_count');
1276                     } else {
1277                         copy_count = $('catStrings').getFormattedString('staff.cat.copy_editor.copy_count.plural', [count]);
1278                     }
1279                     label2.appendChild( document.createTextNode(copy_count) );
1280                 }
1281                 groupbox.setAttribute('id','groupbox_'+fn); // this id is meant to be referenced by cat_custom.css for hiding fields
1282                 var hbox = document.createElement('hbox'); // main input controls go here
1283                 hbox.setAttribute('id',fn); // this id gets used to color areas green, etc.
1284                 groupbox.appendChild(hbox);
1285                 var hbox2 = document.createElement('hbox'); // cancel/apply buttons go here
1286                 groupbox.appendChild(hbox2);
1287
1288                 /**************************************************************************************/
1289                 /* Render the input widget */
1290
1291                 if (f[1].input && g.edit) {
1292                     g.render_input(hbox,f[1]);
1293                 }
1294
1295             } catch(E) {
1296                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1297             }
1298         }
1299     }
1300     
1301     
1302     /******************************************************************************************************/
1303     /* Synchronize stat cat visibility with library filter menu, and default template selection */
1304     JSAN.use('util.file'); 
1305     var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1306     g.copy_editor_prefs = util.widgets.load_attributes(file);
1307     for (var i in g.copy_editor_prefs) {
1308         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1309             try { 
1310                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1311             } catch(E) { alert(E); }
1312         }
1313     }
1314     if (g.template_menu) {
1315         g.template_menu.value = g.template_menu.getAttribute('value');
1316         if (xulG.unified_interface) {
1317             if (typeof xulG.update_unified_template_selection == 'function') {
1318                 xulG.update_unified_template_selection(g.template_menu.value);
1319             }
1320         }
1321     }
1322
1323     util.hide.generate_css('ui.hide_copy_editor_fields');
1324 }
1325
1326 /******************************************************************************************************/
1327 /* This actually draws the change button and input widget for a given field */
1328 g.render_input = function(node,blob) {
1329     try {
1330         // node = hbox ;    groupbox ->  hbox, hbox
1331
1332         var groupbox = node.parentNode;
1333         var caption = groupbox.firstChild;
1334         var vbox = node.previousSibling;
1335         var hbox = node;
1336         var hbox2 = node.nextSibling;
1337
1338         var input_cmd = blob.input;
1339         var render_cmd = blob.render;
1340         var attr = blob.attr;
1341
1342         var block = false; var first = true;
1343
1344         function on_mouseover(ev) {
1345             groupbox.setAttribute('style','background: white');
1346         }
1347
1348         function on_mouseout(ev) {
1349             groupbox.setAttribute('style','');
1350         }
1351
1352         vbox.addEventListener('mouseover',on_mouseover,false);
1353         vbox.addEventListener('mouseout',on_mouseout,false);
1354         groupbox.addEventListener('mouseover',on_mouseover,false);
1355         groupbox.addEventListener('mouseout',on_mouseout,false);
1356         groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1357         groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1358
1359         function on_click(ev){
1360             try {
1361                 if (block || g.disabled || !g.edit) {
1362                     return;
1363                 }
1364                 block = true;
1365
1366                 oils_lock_page();
1367
1368                 function post_c(v) {
1369                     try {
1370                         /* FIXME - kludgy */
1371                         var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1372                         var f;
1373                         switch(t) {
1374                             case 'attribute' :
1375                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1376                             break;
1377                             case 'stat_cat' :
1378                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1379                             break;
1380                             case 'owning_lib' :
1381                                 f = null;
1382                             break;
1383                         }
1384                         g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1385                         block = false;
1386                         setTimeout(
1387                             function() {
1388                                 g.summarize( g.copies );
1389                                 g.render();
1390                                 g.check_for_unmet_required_fields();
1391                                 document.getElementById(caption.id).focus();
1392                             }, 0
1393                         );
1394                     } catch(E) {
1395                         g.error.standard_unexpected_error_alert('post_c',E);
1396                     }
1397                 }
1398                 var x; var c; eval( input_cmd );
1399                 if (x) {
1400                     util.widgets.remove_children(vbox);
1401                     util.widgets.remove_children(hbox);
1402                     util.widgets.remove_children(hbox2);
1403                     hbox.appendChild(x);
1404                     var apply = document.createElement('button');
1405                     apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
1406                     apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
1407                     hbox2.appendChild(apply);
1408                     apply.addEventListener('command',function() { c(x.value); },false);
1409                     var cancel = document.createElement('button');
1410                     cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
1411                     cancel.addEventListener('command',function() {
1412                             setTimeout( function() {
1413                                     g.summarize( g.copies );
1414                                     g.render();
1415                                     g.check_for_unmet_required_fields();
1416                                     document.getElementById(caption.id).focus(); 
1417                                 }, 0
1418                             );
1419                         }, false
1420                     );
1421                     hbox2.appendChild(cancel);
1422                     setTimeout( function() { x.focus(); }, 0 );
1423                 }
1424             } catch(E) {
1425                 g.error.standard_unexpected_error_alert('render_input',E);
1426             }
1427         }
1428         vbox.addEventListener('click',on_click, false);
1429         hbox.addEventListener('click',on_click, false);
1430         caption.addEventListener('click',on_click, false);
1431         caption.addEventListener('keypress',function(ev) {
1432             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1433         }, false);
1434         caption.setAttribute('style','-moz-user-focus: normal');
1435         caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1436         caption.setAttribute('onblur','this.setAttribute("class","")');
1437
1438     } catch(E) {
1439         g.error.sdump('D_ERROR',E + '\n');
1440     }
1441 }
1442
1443 /******************************************************************************************************/
1444 /* store the copies in the global xpcom stash */
1445
1446 g.stash_and_close = function() {
1447     try {
1448         oils_unlock_page();
1449
1450         if (g.handle_update) {
1451             try {
1452                 var r = g.network.request(
1453                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1454                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1455                     [ ses(), g.copies, true ]
1456                 );
1457                 if (typeof r.ilsevent != 'undefined') {
1458                     g.error.standard_unexpected_error_alert('copy update',r);
1459                 }
1460                 /* FIXME -- revisit the return value here */
1461             } catch(E) {
1462                 alert($('catStrings').getString('staff.cat.copy_editor.handle_update.error') + ' ' + js2JSON(E));
1463             }
1464         }
1465         //g.data.temp_copies = js2JSON( g.copies );
1466         //g.data.stash('temp_copies');
1467         xulG.copies = g.copies;
1468         update_modal_xulG(xulG);
1469         JSAN.use('util.widgets');
1470         util.widgets.dispatch('close',window);
1471     } catch(E) {
1472         alert('Error in copy_editor.js, g.stash_and_close(): '+E);
1473     }
1474 }
1475
1476 /******************************************************************************************************/
1477 /* spawn copy notes interface */
1478
1479 g.copy_notes = function() {
1480     JSAN.use('util.window'); var win = new util.window();
1481     win.open(
1482         urls.XUL_COPY_NOTES, 
1483         //+ '?copy_id=' + window.escape(g.copies[0].id()),
1484         $("catStrings").getString("staff.cat.copy_editor.copy_notes"),'chrome,resizable,modal',
1485         { 'copy_id' : g.copies[0].id() }
1486     );
1487 }
1488
1489 /******************************************************************************************************/
1490 /* hides or unhides stat cats based on library stat cat filter menu */
1491 g.toggle_stat_cat_display = function(el) {
1492     if (!el) return;
1493     var visible = el.getAttribute('checked');
1494     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1495     for (var n = 0; n < nl.length; n++) {
1496         if (visible) {
1497             nl[n].setAttribute('hidden','false');
1498         } else {
1499             nl[n].setAttribute('hidden','true');
1500         }
1501     }
1502     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1503     g.save_attributes();
1504 }
1505
1506 /******************************************************************************************************/
1507 /* This adds a stat cat definition to the stat cat pane for rendering */
1508 g.save_attributes = function() {
1509     JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1510     var what_to_save = {};
1511     for (var i in g.copy_editor_prefs) {
1512         what_to_save[i] = [];
1513         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1514     }
1515     util.widgets.save_attributes(file, what_to_save );
1516 }
1517
1518 /******************************************************************************************************/
1519 /* This adds a stat cat definition to the stat cat pane for rendering */
1520 g.add_stat_cat = function(sc) {
1521     try {
1522         if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1523
1524         var sc_id = sc;
1525
1526         if (typeof sc == 'object') {
1527
1528             sc_id = sc.id();
1529         }
1530
1531         if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1532
1533         g.stat_cat_seen[ sc_id ] = 1;
1534
1535         if (typeof sc != 'object') {
1536
1537             sc = g.network.simple_request(
1538                 'FM_ASC_BATCH_RETRIEVE',
1539                 [ ses(), [ sc_id ] ]
1540             )[0];
1541
1542         }
1543
1544         g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1545
1546         var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1547
1548         if (get_bool( sc.required() )) {
1549             g.required[ label_name ] = 1;
1550         }
1551
1552         var temp_array = [
1553             label_name,
1554             {
1555                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1556                     + sc.id() + '; } ); l ? l.value() : $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");',
1557                 input: 'c = function(v){ g.apply_stat_cat(' + sc.id() + ',v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.remove_stat_cat_entry"), -1 ] ].concat( util.functional.map_list( g.data.hash.asc[' + sc.id() 
1558                     + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1559                     + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1560                 attr: {
1561                     sc_lib: sc.owner(),
1562                 }
1563             }
1564         ];
1565
1566         g.panes_and_field_names.right_pane4.push( temp_array );
1567     } catch(E) {
1568         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.add_stat_cat.error'), E);
1569     }
1570 }
1571
1572 /******************************************************************************************************/
1573 /* Add stat cats to the panes_and_field_names.right_pane4 */
1574 g.populate_stat_cats = function() {
1575     try {
1576         g.data.stash_retrieve();
1577         g.stat_cat_seen = {}; // used for determining whether a stat cat is displayed (and is eligible to be manipulated via a template)
1578
1579         function get(lib_id,only_these) {
1580             g.data.stash_retrieve();
1581             var label = 'asc_list_for_lib_'+lib_id;
1582             if (typeof g.data[label] == 'undefined') {
1583                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1584                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1585                 var temp_list = [];
1586                 for (var j = 0; j < robj.length; j++) {
1587                     var my_asc = robj[j];
1588                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1589                     if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1590                         g.data.hash.asc[ my_asc.id() ] = my_asc;
1591                     }
1592                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1593                     if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1594                         temp_list.push( my_asc );
1595                     }
1596                 }
1597                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1598             }
1599             return g.data[label];
1600         }
1601
1602         /* The stat cats for the pertinent library -- this is based on workstation ou */
1603         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1604         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1605         for (var i = 0; i < g.data.list.my_asc.length; i++) {
1606             g.add_stat_cat( g.data.list.my_asc[i] );
1607         }
1608
1609         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1610             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1611
1612         function add_common_ancestors(sc_libs) {
1613             JSAN.use('util.fm_utils'); 
1614             var libs = []; for (var i in sc_libs) libs.push(i);
1615             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1616             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1617             if (ancestor) {
1618                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1619                 var asc_list = get(ancestor, ancestors);
1620                 for (var i = 0; i < asc_list.length; i++) {
1621                     g.add_stat_cat( asc_list[i] );
1622                 }
1623             }
1624         }
1625
1626         /* stat cats based on stat cat entries present on these copies */
1627         var sc_libs = {};
1628         for (var i = 0; i < g.copies.length; i++) {
1629             var entries = g.copies[i].stat_cat_entries();
1630             if (!entries) entries = [];
1631             for (var j = 0; j < entries.length; j++) {
1632                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1633                 sc_libs[ lib ] = true;
1634             }
1635         }
1636         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1637
1638         /* stat cats based on Circ Lib */
1639         sc_libs = {};
1640         for (var i = 0; i < g.copies.length; i++) {
1641             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1642             sc_libs[ circ_lib ] = true;
1643         }
1644         add_common_ancestors(sc_libs);
1645
1646         /* stat cats based on Owning Lib */
1647         sc_libs = {};
1648         for (var i = 0; i < g.copies.length; i++) {
1649             var cn_id = g.copies[i].call_number();
1650             if (cn_id > 0) {
1651                 if (! g.map_acn[ cn_id ]) {
1652                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1653                     if (typeof req.ilsevent == 'undefined') {
1654                         g.map_acn[ cn_id ] = req;
1655                     } else {
1656                         continue;
1657                     }
1658                 }
1659                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1660                 sc_libs[ owning_lib ] = true;
1661             }
1662         }
1663         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1664
1665         g.panes_and_field_names.right_pane4.sort();
1666
1667     } catch(E) {
1668         alert(E);
1669         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.populate_stat_cat.error'),E);
1670     }
1671 }
1672
1673 g.check_for_unmet_required_fields = function() {
1674     var abort = [];
1675     for (var fn in g.required) {
1676         if (typeof g.populated[fn] == 'undefined') {
1677             abort.push(fn);
1678         }
1679     }
1680     if (xulG.unified_interface) {
1681         if (abort.length > 0) {
1682             if (typeof xulG.lock_save_button == 'function') {
1683                 xulG.lock_save_button();
1684             }
1685         } else {
1686             if (typeof xulG.unlock_save_button == 'function') {
1687                 xulG.unlock_save_button();
1688             }
1689         }
1690     } else {
1691         if (abort.length > 0) {
1692             $('save').setAttribute('disabled','true');
1693         } else {
1694             $('save').setAttribute('disabled','false');
1695         }
1696     }
1697 }
1698
1699