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