]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
Change each tab to 4 spaces in the staff client javascript files.
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / cat / copy_editor.js
1 // vim:noet:sw=4:ts=4
2 var g = {};
3 g.map_acn = {};
4
5 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
921 'right_pane4' : 
922 [
923 ]
924
925 };
926 }
927
928 /******************************************************************************************************/
929 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
930
931 g.summarize = function( copies ) {
932     /******************************************************************************************************/
933     /* Setup */
934
935     JSAN.use('util.date'); JSAN.use('util.money');
936     g.summary = {};
937     g.field_names = [];
938     for (var i in g.panes_and_field_names) {
939         g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
940     }
941     g.field_names = g.field_names.concat( g.editable_stat_cat_names );
942     g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
943
944     /******************************************************************************************************/
945     /* Loop through the field names */
946
947     for (var i = 0; i < g.field_names.length; i++) {
948
949         var field_name = g.field_names[i][0];
950         var render = g.field_names[i][1].render;
951         var attr = g.field_names[i][1].attr;
952         g.summary[ field_name ] = {};
953
954         /******************************************************************************************************/
955         /* Loop through the copies */
956
957         for (var j = 0; j < copies.length; j++) {
958
959             var fm = copies[j];
960             var cmd = render || ('fm.' + field_name + '();');
961             var value = '???';
962
963             /**********************************************************************************************/
964             /* Try to retrieve the value for this field for this copy */
965
966             try { 
967                 value = eval( cmd ); 
968             } catch(E) { 
969                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
970             }
971             if (typeof value == 'object' && value != null) {
972                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
973             }
974
975             /**********************************************************************************************/
976             /* Tally the count */
977
978             if (g.summary[ field_name ][ value ]) {
979                 g.summary[ field_name ][ value ]++;
980             } else {
981                 g.summary[ field_name ][ value ] = 1;
982             }
983         }
984     }
985     g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
986 }
987
988 /******************************************************************************************************/
989 /* Display the summarized data and inputs for editing */
990
991 g.render = function() {
992
993     /******************************************************************************************************/
994     /* Library setup and clear any existing interface */
995
996     JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
997
998     for (var i in g.panes_and_field_names) {
999         var p = document.getElementById(i);
1000         if (p) util.widgets.remove_children(p);
1001     }
1002
1003     /******************************************************************************************************/
1004     /* Populate the library filter menu for stat cats */
1005
1006     var sc_libs = {};
1007     for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
1008         sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
1009     }
1010     var sc_libs2 = [];
1011     for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
1012     sc_libs2.sort();
1013     var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
1014     JSAN.use('util.widgets'); util.widgets.remove_children(x);
1015     for (var i = 0; i < sc_libs2.length; i++) {
1016         var menuitem = document.createElement('menuitem');
1017         menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
1018         menuitem.setAttribute('type','checkbox');
1019         menuitem.setAttribute('checked','true');
1020         menuitem.setAttribute('label',sc_libs2[i][0]);
1021         menuitem.setAttribute('value',sc_libs2[i][1]);
1022         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
1023         x.appendChild(menuitem);
1024     }
1025
1026     /******************************************************************************************************/
1027     /* Prepare the panes */
1028
1029     var groupbox; var caption; var vbox; var grid; var rows;
1030     
1031     /******************************************************************************************************/
1032     /* Loop through the field names */
1033
1034     for (h in g.panes_and_field_names) {
1035         if (!document.getElementById(h)) continue;
1036         for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1037             try {
1038                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1039                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1040                 if (attr) {
1041                     for (var a in attr) {
1042                         groupbox.setAttribute(a,attr[a]);
1043                     }
1044                 }
1045                 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
1046                 caption = document.createElement('caption'); groupbox.appendChild(caption);
1047                 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
1048                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
1049                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1050                 grid.setAttribute('flex','1');
1051                 rows = grid.lastChild;
1052                 var row;
1053                 
1054                 /**************************************************************************************/
1055                 /* Loop through each value for the field */
1056
1057                 for (var j in g.summary[fn]) {
1058                     var value = j; var count = g.summary[fn][j];
1059                     row = document.createElement('row'); rows.appendChild(row);
1060                     var label1 = document.createElement('description'); row.appendChild(label1);
1061                     if (g.special_exception[ fn ]) {
1062                         g.special_exception[ fn ]( label1, value );
1063                     } else {
1064                         label1.appendChild( document.createTextNode(value) );
1065                     }
1066                     var label2 = document.createElement('description'); row.appendChild(label2);
1067                     var copy_count;
1068                     if (count == 1) {
1069                         copy_count = $('catStrings').getString('staff.cat.copy_editor.copy_count');
1070                     } else {
1071                         copy_count = $('catStrings').getFormattedString('staff.cat.copy_editor.copy_count.plural', [count]);
1072                     }
1073                     label2.appendChild( document.createTextNode(copy_count) );
1074                 }
1075                 var hbox = document.createElement('hbox'); 
1076                 hbox.setAttribute('id',fn);
1077                 groupbox.appendChild(hbox);
1078                 var hbox2 = document.createElement('hbox');
1079                 groupbox.appendChild(hbox2);
1080
1081                 /**************************************************************************************/
1082                 /* Render the input widget */
1083
1084                 if (f[1].input && g.edit) {
1085                     g.render_input(hbox,f[1]);
1086                 }
1087
1088             } catch(E) {
1089                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1090             }
1091         }
1092     }
1093     
1094     
1095     /******************************************************************************************************/
1096     /* Synchronize stat cat visibility with library filter menu, and default template selection */
1097     JSAN.use('util.file'); 
1098     var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1099     g.copy_editor_prefs = util.widgets.load_attributes(file);
1100     for (var i in g.copy_editor_prefs) {
1101         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1102             try { 
1103                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1104             } catch(E) { alert(E); }
1105         }
1106     }
1107     if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1108
1109 }
1110
1111 /******************************************************************************************************/
1112 /* This actually draws the change button and input widget for a given field */
1113 g.render_input = function(node,blob) {
1114     try {
1115         // node = hbox ;    groupbox ->  hbox, hbox
1116
1117         var groupbox = node.parentNode;
1118         var caption = groupbox.firstChild;
1119         var vbox = node.previousSibling;
1120         var hbox = node;
1121         var hbox2 = node.nextSibling;
1122
1123         var input_cmd = blob.input;
1124         var render_cmd = blob.render;
1125         var attr = blob.attr;
1126
1127         var block = false; var first = true;
1128
1129         function on_mouseover(ev) {
1130             groupbox.setAttribute('style','background: white');
1131         }
1132
1133         function on_mouseout(ev) {
1134             groupbox.setAttribute('style','');
1135         }
1136
1137         vbox.addEventListener('mouseover',on_mouseover,false);
1138         vbox.addEventListener('mouseout',on_mouseout,false);
1139         groupbox.addEventListener('mouseover',on_mouseover,false);
1140         groupbox.addEventListener('mouseout',on_mouseout,false);
1141         groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1142         groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1143
1144         function on_click(ev){
1145             try {
1146                 if (block) return; block = true;
1147
1148                 function post_c(v) {
1149                     try {
1150                         /* FIXME - kludgy */
1151                         var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1152                         var f;
1153                         switch(t) {
1154                             case 'attribute' :
1155                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1156                             break;
1157                             case 'stat_cat' :
1158                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1159                             break;
1160                             case 'owning_lib' :
1161                                 f = null;
1162                             break;
1163                         }
1164                         g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1165                         block = false;
1166                         setTimeout(
1167                             function() {
1168                                 g.summarize( g.copies );
1169                                 g.render();
1170                                 document.getElementById(caption.id).focus();
1171                             }, 0
1172                         );
1173                     } catch(E) {
1174                         g.error.standard_unexpected_error_alert('post_c',E);
1175                     }
1176                 }
1177                 var x; var c; eval( input_cmd );
1178                 if (x) {
1179                     util.widgets.remove_children(vbox);
1180                     util.widgets.remove_children(hbox);
1181                     util.widgets.remove_children(hbox2);
1182                     hbox.appendChild(x);
1183                     var apply = document.createElement('button');
1184                     apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
1185                     apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
1186                     hbox2.appendChild(apply);
1187                     apply.addEventListener('command',function() { c(x.value); },false);
1188                     var cancel = document.createElement('button');
1189                     cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
1190                     cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1191                     hbox2.appendChild(cancel);
1192                     setTimeout( function() { x.focus(); }, 0 );
1193                 }
1194             } catch(E) {
1195                 g.error.standard_unexpected_error_alert('render_input',E);
1196             }
1197         }
1198         vbox.addEventListener('click',on_click, false);
1199         hbox.addEventListener('click',on_click, false);
1200         caption.addEventListener('click',on_click, false);
1201         caption.addEventListener('keypress',function(ev) {
1202             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1203         }, false);
1204         caption.setAttribute('style','-moz-user-focus: normal');
1205         caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1206         caption.setAttribute('onblur','this.setAttribute("class","")');
1207
1208     } catch(E) {
1209         g.error.sdump('D_ERROR',E + '\n');
1210     }
1211 }
1212
1213 /******************************************************************************************************/
1214 /* store the copies in the global xpcom stash */
1215
1216 g.stash_and_close = function() {
1217     try {
1218         if (g.handle_update) {
1219             try {
1220                 var r = g.network.request(
1221                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1222                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1223                     [ ses(), g.copies, true ]
1224                 );
1225                 if (typeof r.ilsevent != 'undefined') {
1226                     g.error.standard_unexpected_error_alert('copy update',r);
1227                 } else {
1228                     alert($('catStrings').getString('staff.cat.copy_editor.handle_update.success'));
1229                 }
1230                 /* FIXME -- revisit the return value here */
1231             } catch(E) {
1232                 alert($('catStrings').getString('staff.cat.copy_editor.handle_update.error') + ' ' + js2JSON(E));
1233             }
1234         }
1235         //g.data.temp_copies = js2JSON( g.copies );
1236         //g.data.stash('temp_copies');
1237         xulG.copies = g.copies;
1238         update_modal_xulG(xulG);
1239         window.close();
1240     } catch(E) {
1241         g.error.standard_unexpected_error_alert('stash and close',E);
1242     }
1243 }
1244
1245 /******************************************************************************************************/
1246 /* spawn copy notes interface */
1247
1248 g.copy_notes = function() {
1249     JSAN.use('util.window'); var win = new util.window();
1250     win.open(
1251         urls.XUL_COPY_NOTES, 
1252         //+ '?copy_id=' + window.escape(g.copies[0].id()),
1253         $("catStrings").getString("staff.cat.copy_editor.copy_notes"),'chrome,resizable,modal',
1254         { 'copy_id' : g.copies[0].id() }
1255     );
1256 }
1257
1258 /******************************************************************************************************/
1259 /* hides or unhides stat cats based on library stat cat filter menu */
1260 g.toggle_stat_cat_display = function(el) {
1261     if (!el) return;
1262     var visible = el.getAttribute('checked');
1263     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1264     for (var n = 0; n < nl.length; n++) {
1265         if (visible) {
1266             nl[n].setAttribute('hidden','false');
1267         } else {
1268             nl[n].setAttribute('hidden','true');
1269         }
1270     }
1271     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1272     g.save_attributes();
1273 }
1274
1275 /******************************************************************************************************/
1276 /* This adds a stat cat definition to the stat cat pane for rendering */
1277 g.save_attributes = function() {
1278     JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1279     var what_to_save = {};
1280     for (var i in g.copy_editor_prefs) {
1281         what_to_save[i] = [];
1282         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1283     }
1284     util.widgets.save_attributes(file, what_to_save );
1285 }
1286
1287 /******************************************************************************************************/
1288 /* This adds a stat cat definition to the stat cat pane for rendering */
1289 g.add_stat_cat = function(sc) {
1290     try {
1291         if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1292
1293         var sc_id = sc;
1294
1295         if (typeof sc == 'object') {
1296
1297             sc_id = sc.id();
1298         }
1299
1300         if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1301
1302         g.stat_cat_seen[ sc_id ] = 1;
1303
1304         if (typeof sc != 'object') {
1305
1306             sc = g.network.simple_request(
1307                 'FM_ASC_BATCH_RETRIEVE',
1308                 [ ses(), [ sc_id ] ]
1309             )[0];
1310
1311         }
1312
1313         g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1314
1315         var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1316
1317         var temp_array = [
1318             label_name,
1319             {
1320                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1321                     + sc.id() + '; } ); l ? l.value() : $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");',
1322                 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() 
1323                     + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1324                     + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1325                 attr: {
1326                     sc_lib: sc.owner(),
1327                 }
1328             }
1329         ];
1330
1331         g.panes_and_field_names.right_pane4.push( temp_array );
1332     } catch(E) {
1333         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.add_stat_cat.error'), E);
1334     }
1335 }
1336
1337 /******************************************************************************************************/
1338 /* Add stat cats to the panes_and_field_names.right_pane4 */
1339 g.populate_stat_cats = function() {
1340     try {
1341         g.data.stash_retrieve();
1342         g.stat_cat_seen = {};
1343
1344         function get(lib_id,only_these) {
1345             g.data.stash_retrieve();
1346             var label = 'asc_list_for_lib_'+lib_id;
1347             if (typeof g.data[label] == 'undefined') {
1348                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1349                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1350                 var temp_list = [];
1351                 for (var j = 0; j < robj.length; j++) {
1352                     var my_asc = robj[j];
1353                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1354                     if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1355                         g.data.hash.asc[ my_asc.id() ] = my_asc;
1356                     }
1357                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1358                     if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1359                         temp_list.push( my_asc );
1360                     }
1361                 }
1362                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1363             }
1364             return g.data[label];
1365         }
1366
1367         /* The stat cats for the pertinent library -- this is based on workstation ou */
1368         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1369         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1370         for (var i = 0; i < g.data.list.my_asc.length; i++) {
1371             g.add_stat_cat( g.data.list.my_asc[i] );
1372         }
1373
1374         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1375             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1376
1377         function add_common_ancestors(sc_libs) {
1378             JSAN.use('util.fm_utils'); 
1379             var libs = []; for (var i in sc_libs) libs.push(i);
1380             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1381             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1382             if (ancestor) {
1383                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1384                 var asc_list = get(ancestor, ancestors);
1385                 for (var i = 0; i < asc_list.length; i++) {
1386                     g.add_stat_cat( asc_list[i] );
1387                 }
1388             }
1389         }
1390
1391         /* stat cats based on stat cat entries present on these copies */
1392         var sc_libs = {};
1393         for (var i = 0; i < g.copies.length; i++) {
1394             var entries = g.copies[i].stat_cat_entries();
1395             if (!entries) entries = [];
1396             for (var j = 0; j < entries.length; j++) {
1397                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1398                 sc_libs[ lib ] = true;
1399             }
1400         }
1401         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1402
1403         /* stat cats based on Circ Lib */
1404         sc_libs = {};
1405         for (var i = 0; i < g.copies.length; i++) {
1406             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1407             sc_libs[ circ_lib ] = true;
1408         }
1409         add_common_ancestors(sc_libs);
1410
1411         /* stat cats based on Owning Lib */
1412         sc_libs = {};
1413         for (var i = 0; i < g.copies.length; i++) {
1414             var cn_id = g.copies[i].call_number();
1415             if (cn_id > 0) {
1416                 if (! g.map_acn[ cn_id ]) {
1417                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1418                     if (typeof req.ilsevent == 'undefined') {
1419                         g.map_acn[ cn_id ] = req;
1420                     } else {
1421                         continue;
1422                     }
1423                 }
1424                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1425                 sc_libs[ owning_lib ] = true;
1426             }
1427         }
1428         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1429
1430         g.panes_and_field_names.right_pane4.sort();
1431
1432     } catch(E) {
1433         alert(E);
1434         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.populate_stat_cat.error'),E);
1435     }
1436 }
1437
1438