]> 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.creator.label'),
870         { 
871             render: 'fm.creator();',
872         }
873     ],
874     [
875         $('catStrings').getString('staff.cat.copy_editor.field.last_edit_date.label'),
876         { 
877             render: 'util.date.formatted_date( fm.edit_date(), "%F");',
878         }
879     ],
880     [
881         $('catStrings').getString('staff.cat.copy_editor.field.last_editor.label'),
882         {
883             render: 'fm.editor();',
884         }
885     ],
886
887 ],
888
889 'right_pane' :
890 [
891     [
892         $('catStrings').getString('staff.cat.copy_editor.field.location.label'),
893         { 
894             render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
895             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);',
896
897         }
898     ],
899     [
900         $('catStrings').getString('staff.cat.copy_editor.field.circulation_library.label'),
901         {     
902             render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
903             //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);',
904             input: 'c = function(v){ g.apply("circ_lib",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( 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);',
905         } 
906     ],
907     [
908         $('catStrings').getString('staff.cat.copy_editor.field.owning_library.label'),
909         {
910             render: 'fm.call_number();',
911             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,
912         }
913     ],
914     [
915         $('catStrings').getString('staff.cat.copy_editor.field.copy_number.label'),
916         { 
917             render: 'fm.copy_number() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : fm.copy_number()',
918             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);',
919         }
920     ],
921
922
923 ],
924
925 'right_pane2' :
926 [
927     [
928         $('catStrings').getString('staff.cat.copy_editor.field.circulate.label'),
929         {     
930             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") )',
931             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);',
932         }
933     ],
934     [
935         $('catStrings').getString('staff.cat.copy_editor.field.holdable.label'),
936         { 
937             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") )',
938             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);',
939         }
940     ],
941     [
942         $('catStrings').getString('staff.cat.copy_editor.field.age_based_hold_protection.label'),
943         {
944             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() )', 
945             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);',
946         }
947
948     ],
949     [
950         $('catStrings').getString('staff.cat.copy_editor.field.floating.label'),
951         { 
952             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") )',
953             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);',
954         }
955     ],
956     [
957         $('catStrings').getString('staff.cat.copy_editor.field.loan_duration.label'),
958         { 
959             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; }',
960             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);',
961
962         }
963     ],
964     [
965         $('catStrings').getString('staff.cat.copy_editor.field.fine_level.label'),
966         {
967             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; }',
968             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);',
969         }
970     ],
971
972      [
973         $('catStrings').getString('staff.cat.copy_editor.field.circulate_as_type.label'),
974         {     
975             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',
976             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);',
977         } 
978     ],
979     [
980         $('catStrings').getString('staff.cat.copy_editor.field.circulation_modifier.label'),
981         {    
982             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()])',
983             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);',
984         }
985     ],
986 ],
987
988 'right_pane3' :
989 [    [
990         $('catStrings').getString('staff.cat.copy_editor.field.alert_message.label'),
991         {
992             render: 'fm.alert_message() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : fm.alert_message()',
993             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);',
994         }
995     ],
996
997     [
998         $('catStrings').getString('staff.cat.copy_editor.field.deposit.label'),
999         { 
1000             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") )',
1001             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);',
1002         }
1003     ],
1004     [
1005         $('catStrings').getString('staff.cat.copy_editor.field.deposit_amount.label'),
1006         { 
1007             render: 'if (fm.deposit_amount() == null) { $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null"); } else { util.money.sanitize( fm.deposit_amount() ); }',
1008             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);',
1009         }
1010     ],
1011     [
1012         $('catStrings').getString('staff.cat.copy_editor.field.price.label'),
1013         { 
1014             render: 'if (fm.price() == null) { $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null"); } else { util.money.sanitize( fm.price() ); }', 
1015             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);',
1016         }
1017     ],
1018
1019     [
1020         $('catStrings').getString('staff.cat.copy_editor.field.opac_visible.label'),
1021         { 
1022             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") )', 
1023             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);',
1024         }
1025     ],
1026     [
1027         $('catStrings').getString('staff.cat.copy_editor.field.reference.label'),
1028         { 
1029             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") )', 
1030             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);',
1031         }
1032     ],
1033     [
1034         $('catStrings').getString('staff.cat.copy_editor.field.mint_condition.label'),
1035         { 
1036             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")', 
1037             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);',
1038         }
1039     ]
1040 ],
1041
1042 'right_pane4' : 
1043 [
1044 ]
1045
1046 };
1047 }
1048
1049 /******************************************************************************************************/
1050 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
1051
1052 g.summarize = function( copies ) {
1053     /******************************************************************************************************/
1054     /* Setup */
1055
1056     JSAN.use('util.date'); JSAN.use('util.money');
1057     g.summary = {};
1058     g.field_names = [];
1059     for (var i in g.panes_and_field_names) {
1060         g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
1061     }
1062     g.field_names = g.field_names.concat( g.editable_stat_cat_names );
1063     g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
1064
1065     /******************************************************************************************************/
1066     /* Loop through the field names */
1067
1068     for (var i = 0; i < g.field_names.length; i++) {
1069
1070         var field_name = g.field_names[i][0];
1071         var render = g.field_names[i][1].render;
1072         var attr = g.field_names[i][1].attr;
1073         g.summary[ field_name ] = {};
1074         g.populated[ field_name ] = 1; // delete later if we encounter a copy with the field unset
1075
1076         /******************************************************************************************************/
1077         /* Loop through the copies */
1078
1079         for (var j = 0; j < copies.length; j++) {
1080
1081             var fm = copies[j];
1082             var cmd = render || ('fm.' + field_name + '();');
1083             var value = $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");
1084
1085             /**********************************************************************************************/
1086             /* Try to retrieve the value for this field for this copy */
1087
1088             try { 
1089                 value = eval( cmd ); 
1090             } catch(E) { 
1091                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
1092             }
1093             if (typeof value == 'object' && value != null) {
1094                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
1095             }
1096             if (value == $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null")) {
1097                 delete g.populated[field_name];
1098             }
1099
1100             /**********************************************************************************************/
1101             /* Tally the count */
1102
1103             if (g.summary[ field_name ][ value ]) {
1104                 g.summary[ field_name ][ value ]++;
1105             } else {
1106                 g.summary[ field_name ][ value ] = 1;
1107             }
1108         }
1109     }
1110     g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
1111 }
1112
1113 /******************************************************************************************************/
1114 /* Display the summarized data and inputs for editing */
1115
1116 g.render = function() {
1117
1118     /******************************************************************************************************/
1119     /* Library setup and clear any existing interface */
1120
1121     JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
1122
1123     for (var i in g.panes_and_field_names) {
1124         var p = document.getElementById(i);
1125         if (p) util.widgets.remove_children(p);
1126     }
1127
1128     /******************************************************************************************************/
1129     /* Populate the library filter menu for stat cats */
1130
1131     var sc_libs = {};
1132     for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
1133         sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
1134     }
1135     var sc_libs2 = [];
1136     for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
1137     sc_libs2.sort();
1138     var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
1139     JSAN.use('util.widgets'); util.widgets.remove_children(x);
1140     for (var i = 0; i < sc_libs2.length; i++) {
1141         var menuitem = document.createElement('menuitem');
1142         menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
1143         menuitem.setAttribute('type','checkbox');
1144         menuitem.setAttribute('checked','true');
1145         menuitem.setAttribute('label',sc_libs2[i][0]);
1146         menuitem.setAttribute('value',sc_libs2[i][1]);
1147         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
1148         x.appendChild(menuitem);
1149     }
1150
1151     /******************************************************************************************************/
1152     /* Prepare the panes */
1153
1154     var groupbox; var caption; var vbox; var grid; var rows;
1155     
1156     /******************************************************************************************************/
1157     /* Loop through the field names */
1158
1159     for (h in g.panes_and_field_names) {
1160         if (!document.getElementById(h)) continue;
1161         for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1162             try {
1163                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1164                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1165                 if (attr) {
1166                     for (var a in attr) {
1167                         groupbox.setAttribute(a,attr[a]);
1168                     }
1169                 }
1170                 caption = document.createElement('caption'); groupbox.appendChild(caption);
1171                 caption.setAttribute('label',fn);
1172                 caption.setAttribute('id','caption_'+fn); // used for focus/keyboard navigation
1173                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox); // main display widget goes here
1174                 if (typeof g.changed[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_changed'); }
1175                 if (typeof g.required[fn] != 'undefined') { addCSSClass(vbox,'copy_editor_field_required'); }
1176                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1177                 grid.setAttribute('flex','1');
1178                 rows = grid.lastChild;
1179                 var row;
1180                 
1181                 /**************************************************************************************/
1182                 /* Loop through each value for the field */
1183
1184                 for (var j in g.summary[fn]) {
1185                     var value = j; var count = g.summary[fn][j];
1186                     row = document.createElement('row'); rows.appendChild(row);
1187                     var label1 = document.createElement('description'); row.appendChild(label1);
1188                     if (g.special_exception[ fn ]) {
1189                         g.special_exception[ fn ]( label1, value );
1190                     } else {
1191                         label1.appendChild( document.createTextNode(value) );
1192                     }
1193                     var label2 = document.createElement('description'); row.appendChild(label2);
1194                     var copy_count;
1195                     if (count == 1) {
1196                         copy_count = $('catStrings').getString('staff.cat.copy_editor.copy_count');
1197                     } else {
1198                         copy_count = $('catStrings').getFormattedString('staff.cat.copy_editor.copy_count.plural', [count]);
1199                     }
1200                     label2.appendChild( document.createTextNode(copy_count) );
1201                 }
1202                 groupbox.setAttribute('id','groupbox_'+fn); // this id is meant to be referenced by cat_custom.css for hiding fields
1203                 var hbox = document.createElement('hbox'); // main input controls go here
1204                 hbox.setAttribute('id',fn); // this id gets used to color areas green, etc.
1205                 groupbox.appendChild(hbox);
1206                 var hbox2 = document.createElement('hbox'); // cancel/apply buttons go here
1207                 groupbox.appendChild(hbox2);
1208
1209                 /**************************************************************************************/
1210                 /* Render the input widget */
1211
1212                 if (f[1].input && g.edit) {
1213                     g.render_input(hbox,f[1]);
1214                 }
1215
1216             } catch(E) {
1217                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1218             }
1219         }
1220     }
1221     
1222     
1223     /******************************************************************************************************/
1224     /* Synchronize stat cat visibility with library filter menu, and default template selection */
1225     JSAN.use('util.file'); 
1226     var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1227     g.copy_editor_prefs = util.widgets.load_attributes(file);
1228     for (var i in g.copy_editor_prefs) {
1229         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1230             try { 
1231                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1232             } catch(E) { alert(E); }
1233         }
1234     }
1235     if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1236
1237 }
1238
1239 /******************************************************************************************************/
1240 /* This actually draws the change button and input widget for a given field */
1241 g.render_input = function(node,blob) {
1242     try {
1243         // node = hbox ;    groupbox ->  hbox, hbox
1244
1245         var groupbox = node.parentNode;
1246         var caption = groupbox.firstChild;
1247         var vbox = node.previousSibling;
1248         var hbox = node;
1249         var hbox2 = node.nextSibling;
1250
1251         var input_cmd = blob.input;
1252         var render_cmd = blob.render;
1253         var attr = blob.attr;
1254
1255         var block = false; var first = true;
1256
1257         function on_mouseover(ev) {
1258             groupbox.setAttribute('style','background: white');
1259         }
1260
1261         function on_mouseout(ev) {
1262             groupbox.setAttribute('style','');
1263         }
1264
1265         vbox.addEventListener('mouseover',on_mouseover,false);
1266         vbox.addEventListener('mouseout',on_mouseout,false);
1267         groupbox.addEventListener('mouseover',on_mouseover,false);
1268         groupbox.addEventListener('mouseout',on_mouseout,false);
1269         groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1270         groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1271
1272         function on_click(ev){
1273             try {
1274                 if (block) return; block = true;
1275
1276                 oils_lock_page();
1277
1278                 function post_c(v) {
1279                     try {
1280                         /* FIXME - kludgy */
1281                         var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1282                         var f;
1283                         switch(t) {
1284                             case 'attribute' :
1285                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1286                             break;
1287                             case 'stat_cat' :
1288                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1289                             break;
1290                             case 'owning_lib' :
1291                                 f = null;
1292                             break;
1293                         }
1294                         g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1295                         block = false;
1296                         setTimeout(
1297                             function() {
1298                                 g.summarize( g.copies );
1299                                 g.render();
1300                                 g.check_for_unmet_required_fields();
1301                                 document.getElementById(caption.id).focus();
1302                             }, 0
1303                         );
1304                     } catch(E) {
1305                         g.error.standard_unexpected_error_alert('post_c',E);
1306                     }
1307                 }
1308                 var x; var c; eval( input_cmd );
1309                 if (x) {
1310                     util.widgets.remove_children(vbox);
1311                     util.widgets.remove_children(hbox);
1312                     util.widgets.remove_children(hbox2);
1313                     hbox.appendChild(x);
1314                     var apply = document.createElement('button');
1315                     apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
1316                     apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
1317                     hbox2.appendChild(apply);
1318                     apply.addEventListener('command',function() { c(x.value); },false);
1319                     var cancel = document.createElement('button');
1320                     cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
1321                     cancel.addEventListener('command',function() {
1322                             setTimeout( function() {
1323                                     g.summarize( g.copies );
1324                                     g.render();
1325                                     g.check_for_unmet_required_fields();
1326                                     document.getElementById(caption.id).focus(); 
1327                                 }, 0
1328                             );
1329                         }, false
1330                     );
1331                     hbox2.appendChild(cancel);
1332                     setTimeout( function() { x.focus(); }, 0 );
1333                 }
1334             } catch(E) {
1335                 g.error.standard_unexpected_error_alert('render_input',E);
1336             }
1337         }
1338         vbox.addEventListener('click',on_click, false);
1339         hbox.addEventListener('click',on_click, false);
1340         caption.addEventListener('click',on_click, false);
1341         caption.addEventListener('keypress',function(ev) {
1342             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1343         }, false);
1344         caption.setAttribute('style','-moz-user-focus: normal');
1345         caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1346         caption.setAttribute('onblur','this.setAttribute("class","")');
1347
1348     } catch(E) {
1349         g.error.sdump('D_ERROR',E + '\n');
1350     }
1351 }
1352
1353 /******************************************************************************************************/
1354 /* store the copies in the global xpcom stash */
1355
1356 g.stash_and_close = function() {
1357     try {
1358         oils_unlock_page();
1359
1360         if (g.handle_update) {
1361             try {
1362                 var r = g.network.request(
1363                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1364                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1365                     [ ses(), g.copies, true ]
1366                 );
1367                 if (typeof r.ilsevent != 'undefined') {
1368                     g.error.standard_unexpected_error_alert('copy update',r);
1369                 }
1370                 /* FIXME -- revisit the return value here */
1371             } catch(E) {
1372                 alert($('catStrings').getString('staff.cat.copy_editor.handle_update.error') + ' ' + js2JSON(E));
1373             }
1374         }
1375         //g.data.temp_copies = js2JSON( g.copies );
1376         //g.data.stash('temp_copies');
1377         xulG.copies = g.copies;
1378         update_modal_xulG(xulG);
1379         JSAN.use('util.widgets');
1380         util.widgets.dispatch('close',window);
1381     } catch(E) {
1382         alert('Error in copy_editor.js, g.stash_and_close(): '+E);
1383     }
1384 }
1385
1386 /******************************************************************************************************/
1387 /* spawn copy notes interface */
1388
1389 g.copy_notes = function() {
1390     JSAN.use('util.window'); var win = new util.window();
1391     win.open(
1392         urls.XUL_COPY_NOTES, 
1393         //+ '?copy_id=' + window.escape(g.copies[0].id()),
1394         $("catStrings").getString("staff.cat.copy_editor.copy_notes"),'chrome,resizable,modal',
1395         { 'copy_id' : g.copies[0].id() }
1396     );
1397 }
1398
1399 /******************************************************************************************************/
1400 /* hides or unhides stat cats based on library stat cat filter menu */
1401 g.toggle_stat_cat_display = function(el) {
1402     if (!el) return;
1403     var visible = el.getAttribute('checked');
1404     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1405     for (var n = 0; n < nl.length; n++) {
1406         if (visible) {
1407             nl[n].setAttribute('hidden','false');
1408         } else {
1409             nl[n].setAttribute('hidden','true');
1410         }
1411     }
1412     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1413     g.save_attributes();
1414 }
1415
1416 /******************************************************************************************************/
1417 /* This adds a stat cat definition to the stat cat pane for rendering */
1418 g.save_attributes = function() {
1419     JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1420     var what_to_save = {};
1421     for (var i in g.copy_editor_prefs) {
1422         what_to_save[i] = [];
1423         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1424     }
1425     util.widgets.save_attributes(file, what_to_save );
1426 }
1427
1428 /******************************************************************************************************/
1429 /* This adds a stat cat definition to the stat cat pane for rendering */
1430 g.add_stat_cat = function(sc) {
1431     try {
1432         if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1433
1434         var sc_id = sc;
1435
1436         if (typeof sc == 'object') {
1437
1438             sc_id = sc.id();
1439         }
1440
1441         if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1442
1443         g.stat_cat_seen[ sc_id ] = 1;
1444
1445         if (typeof sc != 'object') {
1446
1447             sc = g.network.simple_request(
1448                 'FM_ASC_BATCH_RETRIEVE',
1449                 [ ses(), [ sc_id ] ]
1450             )[0];
1451
1452         }
1453
1454         g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1455
1456         var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1457
1458         if (get_bool( sc.required() )) {
1459             g.required[ label_name ] = 1;
1460         }
1461
1462         var temp_array = [
1463             label_name,
1464             {
1465                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1466                     + sc.id() + '; } ); l ? l.value() : $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");',
1467                 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() 
1468                     + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1469                     + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1470                 attr: {
1471                     sc_lib: sc.owner(),
1472                 }
1473             }
1474         ];
1475
1476         g.panes_and_field_names.right_pane4.push( temp_array );
1477     } catch(E) {
1478         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.add_stat_cat.error'), E);
1479     }
1480 }
1481
1482 /******************************************************************************************************/
1483 /* Add stat cats to the panes_and_field_names.right_pane4 */
1484 g.populate_stat_cats = function() {
1485     try {
1486         g.data.stash_retrieve();
1487         g.stat_cat_seen = {}; // used for determining whether a stat cat is displayed (and is eligible to be manipulated via a template)
1488
1489         function get(lib_id,only_these) {
1490             g.data.stash_retrieve();
1491             var label = 'asc_list_for_lib_'+lib_id;
1492             if (typeof g.data[label] == 'undefined') {
1493                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1494                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1495                 var temp_list = [];
1496                 for (var j = 0; j < robj.length; j++) {
1497                     var my_asc = robj[j];
1498                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1499                     if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1500                         g.data.hash.asc[ my_asc.id() ] = my_asc;
1501                     }
1502                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1503                     if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1504                         temp_list.push( my_asc );
1505                     }
1506                 }
1507                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1508             }
1509             return g.data[label];
1510         }
1511
1512         /* The stat cats for the pertinent library -- this is based on workstation ou */
1513         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1514         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1515         for (var i = 0; i < g.data.list.my_asc.length; i++) {
1516             g.add_stat_cat( g.data.list.my_asc[i] );
1517         }
1518
1519         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1520             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1521
1522         function add_common_ancestors(sc_libs) {
1523             JSAN.use('util.fm_utils'); 
1524             var libs = []; for (var i in sc_libs) libs.push(i);
1525             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1526             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1527             if (ancestor) {
1528                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1529                 var asc_list = get(ancestor, ancestors);
1530                 for (var i = 0; i < asc_list.length; i++) {
1531                     g.add_stat_cat( asc_list[i] );
1532                 }
1533             }
1534         }
1535
1536         /* stat cats based on stat cat entries present on these copies */
1537         var sc_libs = {};
1538         for (var i = 0; i < g.copies.length; i++) {
1539             var entries = g.copies[i].stat_cat_entries();
1540             if (!entries) entries = [];
1541             for (var j = 0; j < entries.length; j++) {
1542                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1543                 sc_libs[ lib ] = true;
1544             }
1545         }
1546         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1547
1548         /* stat cats based on Circ Lib */
1549         sc_libs = {};
1550         for (var i = 0; i < g.copies.length; i++) {
1551             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1552             sc_libs[ circ_lib ] = true;
1553         }
1554         add_common_ancestors(sc_libs);
1555
1556         /* stat cats based on Owning Lib */
1557         sc_libs = {};
1558         for (var i = 0; i < g.copies.length; i++) {
1559             var cn_id = g.copies[i].call_number();
1560             if (cn_id > 0) {
1561                 if (! g.map_acn[ cn_id ]) {
1562                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1563                     if (typeof req.ilsevent == 'undefined') {
1564                         g.map_acn[ cn_id ] = req;
1565                     } else {
1566                         continue;
1567                     }
1568                 }
1569                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1570                 sc_libs[ owning_lib ] = true;
1571             }
1572         }
1573         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1574
1575         g.panes_and_field_names.right_pane4.sort();
1576
1577     } catch(E) {
1578         alert(E);
1579         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.populate_stat_cat.error'),E);
1580     }
1581 }
1582
1583 g.check_for_unmet_required_fields = function() {
1584     var abort = [];
1585     for (var fn in g.required) {
1586         if (typeof g.populated[fn] == 'undefined') {
1587             abort.push(fn);
1588         }
1589     }
1590     if (xulG.unified_interface) {
1591         if (abort.length > 0) {
1592             if (typeof xulG.lock_save_button == 'function') {
1593                 xulG.lock_save_button();
1594             }
1595         } else {
1596             if (typeof xulG.unlock_save_button == 'function') {
1597                 xulG.unlock_save_button();
1598             }
1599         }
1600     } else {
1601         if (abort.length > 0) {
1602             $('save').setAttribute('disabled','true');
1603         } else {
1604             $('save').setAttribute('disabled','false');
1605         }
1606     }
1607 }
1608
1609