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