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