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