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