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