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