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