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