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