]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
the caption id was important after all, for keyboard navigation
[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);
1061                 caption.setAttribute('id','caption_'+fn); // used for focus/keyboard navigation
1062                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox); // main display widget goes here
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                 groupbox.setAttribute('id','groupbox_'+fn); // this id is meant to be referenced by cat_custom.css for hiding fields
1090                 var hbox = document.createElement('hbox'); // main input controls go here
1091                 hbox.setAttribute('id',fn); // this id gets used to color areas green, etc.
1092                 groupbox.appendChild(hbox);
1093                 var hbox2 = document.createElement('hbox'); // cancel/apply buttons go here
1094                 groupbox.appendChild(hbox2);
1095
1096                 /**************************************************************************************/
1097                 /* Render the input widget */
1098
1099                 if (f[1].input && g.edit) {
1100                     g.render_input(hbox,f[1]);
1101                 }
1102
1103             } catch(E) {
1104                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1105             }
1106         }
1107     }
1108     
1109     
1110     /******************************************************************************************************/
1111     /* Synchronize stat cat visibility with library filter menu, and default template selection */
1112     JSAN.use('util.file'); 
1113     var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1114     g.copy_editor_prefs = util.widgets.load_attributes(file);
1115     for (var i in g.copy_editor_prefs) {
1116         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1117             try { 
1118                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1119             } catch(E) { alert(E); }
1120         }
1121     }
1122     if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1123
1124 }
1125
1126 /******************************************************************************************************/
1127 /* This actually draws the change button and input widget for a given field */
1128 g.render_input = function(node,blob) {
1129     try {
1130         // node = hbox ;    groupbox ->  hbox, hbox
1131
1132         var groupbox = node.parentNode;
1133         var caption = groupbox.firstChild;
1134         var vbox = node.previousSibling;
1135         var hbox = node;
1136         var hbox2 = node.nextSibling;
1137
1138         var input_cmd = blob.input;
1139         var render_cmd = blob.render;
1140         var attr = blob.attr;
1141
1142         var block = false; var first = true;
1143
1144         function on_mouseover(ev) {
1145             groupbox.setAttribute('style','background: white');
1146         }
1147
1148         function on_mouseout(ev) {
1149             groupbox.setAttribute('style','');
1150         }
1151
1152         vbox.addEventListener('mouseover',on_mouseover,false);
1153         vbox.addEventListener('mouseout',on_mouseout,false);
1154         groupbox.addEventListener('mouseover',on_mouseover,false);
1155         groupbox.addEventListener('mouseout',on_mouseout,false);
1156         groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1157         groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1158
1159         function on_click(ev){
1160             try {
1161                 if (block) return; block = true;
1162
1163                 function post_c(v) {
1164                     try {
1165                         /* FIXME - kludgy */
1166                         var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1167                         var f;
1168                         switch(t) {
1169                             case 'attribute' :
1170                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1171                             break;
1172                             case 'stat_cat' :
1173                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1174                             break;
1175                             case 'owning_lib' :
1176                                 f = null;
1177                             break;
1178                         }
1179                         g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1180                         block = false;
1181                         setTimeout(
1182                             function() {
1183                                 g.summarize( g.copies );
1184                                 g.render();
1185                                 document.getElementById(caption.id).focus();
1186                             }, 0
1187                         );
1188                     } catch(E) {
1189                         g.error.standard_unexpected_error_alert('post_c',E);
1190                     }
1191                 }
1192                 var x; var c; eval( input_cmd );
1193                 if (x) {
1194                     util.widgets.remove_children(vbox);
1195                     util.widgets.remove_children(hbox);
1196                     util.widgets.remove_children(hbox2);
1197                     hbox.appendChild(x);
1198                     var apply = document.createElement('button');
1199                     apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
1200                     apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
1201                     hbox2.appendChild(apply);
1202                     apply.addEventListener('command',function() { c(x.value); },false);
1203                     var cancel = document.createElement('button');
1204                     cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
1205                     cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1206                     hbox2.appendChild(cancel);
1207                     setTimeout( function() { x.focus(); }, 0 );
1208                 }
1209             } catch(E) {
1210                 g.error.standard_unexpected_error_alert('render_input',E);
1211             }
1212         }
1213         vbox.addEventListener('click',on_click, false);
1214         hbox.addEventListener('click',on_click, false);
1215         caption.addEventListener('click',on_click, false);
1216         caption.addEventListener('keypress',function(ev) {
1217             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1218         }, false);
1219         caption.setAttribute('style','-moz-user-focus: normal');
1220         caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1221         caption.setAttribute('onblur','this.setAttribute("class","")');
1222
1223     } catch(E) {
1224         g.error.sdump('D_ERROR',E + '\n');
1225     }
1226 }
1227
1228 /******************************************************************************************************/
1229 /* store the copies in the global xpcom stash */
1230
1231 g.stash_and_close = function() {
1232     try {
1233         if (g.handle_update) {
1234             try {
1235                 var r = g.network.request(
1236                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1237                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1238                     [ ses(), g.copies, true ]
1239                 );
1240                 if (typeof r.ilsevent != 'undefined') {
1241                     g.error.standard_unexpected_error_alert('copy update',r);
1242                 }
1243                 /* FIXME -- revisit the return value here */
1244             } catch(E) {
1245                 alert($('catStrings').getString('staff.cat.copy_editor.handle_update.error') + ' ' + js2JSON(E));
1246             }
1247         }
1248         //g.data.temp_copies = js2JSON( g.copies );
1249         //g.data.stash('temp_copies');
1250         xulG.copies = g.copies;
1251         update_modal_xulG(xulG);
1252         window.close();
1253     } catch(E) {
1254         g.error.standard_unexpected_error_alert('stash and close',E);
1255     }
1256 }
1257
1258 /******************************************************************************************************/
1259 /* spawn copy notes interface */
1260
1261 g.copy_notes = function() {
1262     JSAN.use('util.window'); var win = new util.window();
1263     win.open(
1264         urls.XUL_COPY_NOTES, 
1265         //+ '?copy_id=' + window.escape(g.copies[0].id()),
1266         $("catStrings").getString("staff.cat.copy_editor.copy_notes"),'chrome,resizable,modal',
1267         { 'copy_id' : g.copies[0].id() }
1268     );
1269 }
1270
1271 /******************************************************************************************************/
1272 /* hides or unhides stat cats based on library stat cat filter menu */
1273 g.toggle_stat_cat_display = function(el) {
1274     if (!el) return;
1275     var visible = el.getAttribute('checked');
1276     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1277     for (var n = 0; n < nl.length; n++) {
1278         if (visible) {
1279             nl[n].setAttribute('hidden','false');
1280         } else {
1281             nl[n].setAttribute('hidden','true');
1282         }
1283     }
1284     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1285     g.save_attributes();
1286 }
1287
1288 /******************************************************************************************************/
1289 /* This adds a stat cat definition to the stat cat pane for rendering */
1290 g.save_attributes = function() {
1291     JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1292     var what_to_save = {};
1293     for (var i in g.copy_editor_prefs) {
1294         what_to_save[i] = [];
1295         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1296     }
1297     util.widgets.save_attributes(file, what_to_save );
1298 }
1299
1300 /******************************************************************************************************/
1301 /* This adds a stat cat definition to the stat cat pane for rendering */
1302 g.add_stat_cat = function(sc) {
1303     try {
1304         if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1305
1306         var sc_id = sc;
1307
1308         if (typeof sc == 'object') {
1309
1310             sc_id = sc.id();
1311         }
1312
1313         if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1314
1315         g.stat_cat_seen[ sc_id ] = 1;
1316
1317         if (typeof sc != 'object') {
1318
1319             sc = g.network.simple_request(
1320                 'FM_ASC_BATCH_RETRIEVE',
1321                 [ ses(), [ sc_id ] ]
1322             )[0];
1323
1324         }
1325
1326         g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1327
1328         var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1329
1330         var temp_array = [
1331             label_name,
1332             {
1333                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1334                     + sc.id() + '; } ); l ? l.value() : $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");',
1335                 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() 
1336                     + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1337                     + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1338                 attr: {
1339                     sc_lib: sc.owner(),
1340                 }
1341             }
1342         ];
1343
1344         g.panes_and_field_names.right_pane4.push( temp_array );
1345     } catch(E) {
1346         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.add_stat_cat.error'), E);
1347     }
1348 }
1349
1350 /******************************************************************************************************/
1351 /* Add stat cats to the panes_and_field_names.right_pane4 */
1352 g.populate_stat_cats = function() {
1353     try {
1354         g.data.stash_retrieve();
1355         g.stat_cat_seen = {};
1356
1357         function get(lib_id,only_these) {
1358             g.data.stash_retrieve();
1359             var label = 'asc_list_for_lib_'+lib_id;
1360             if (typeof g.data[label] == 'undefined') {
1361                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1362                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1363                 var temp_list = [];
1364                 for (var j = 0; j < robj.length; j++) {
1365                     var my_asc = robj[j];
1366                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1367                     if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1368                         g.data.hash.asc[ my_asc.id() ] = my_asc;
1369                     }
1370                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1371                     if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1372                         temp_list.push( my_asc );
1373                     }
1374                 }
1375                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1376             }
1377             return g.data[label];
1378         }
1379
1380         /* The stat cats for the pertinent library -- this is based on workstation ou */
1381         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1382         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1383         for (var i = 0; i < g.data.list.my_asc.length; i++) {
1384             g.add_stat_cat( g.data.list.my_asc[i] );
1385         }
1386
1387         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1388             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1389
1390         function add_common_ancestors(sc_libs) {
1391             JSAN.use('util.fm_utils'); 
1392             var libs = []; for (var i in sc_libs) libs.push(i);
1393             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1394             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1395             if (ancestor) {
1396                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1397                 var asc_list = get(ancestor, ancestors);
1398                 for (var i = 0; i < asc_list.length; i++) {
1399                     g.add_stat_cat( asc_list[i] );
1400                 }
1401             }
1402         }
1403
1404         /* stat cats based on stat cat entries present on these copies */
1405         var sc_libs = {};
1406         for (var i = 0; i < g.copies.length; i++) {
1407             var entries = g.copies[i].stat_cat_entries();
1408             if (!entries) entries = [];
1409             for (var j = 0; j < entries.length; j++) {
1410                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1411                 sc_libs[ lib ] = true;
1412             }
1413         }
1414         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1415
1416         /* stat cats based on Circ Lib */
1417         sc_libs = {};
1418         for (var i = 0; i < g.copies.length; i++) {
1419             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1420             sc_libs[ circ_lib ] = true;
1421         }
1422         add_common_ancestors(sc_libs);
1423
1424         /* stat cats based on Owning Lib */
1425         sc_libs = {};
1426         for (var i = 0; i < g.copies.length; i++) {
1427             var cn_id = g.copies[i].call_number();
1428             if (cn_id > 0) {
1429                 if (! g.map_acn[ cn_id ]) {
1430                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1431                     if (typeof req.ilsevent == 'undefined') {
1432                         g.map_acn[ cn_id ] = req;
1433                     } else {
1434                         continue;
1435                     }
1436                 }
1437                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1438                 sc_libs[ owning_lib ] = true;
1439             }
1440         }
1441         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1442
1443         g.panes_and_field_names.right_pane4.sort();
1444
1445     } catch(E) {
1446         alert(E);
1447         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.populate_stat_cat.error'),E);
1448     }
1449 }
1450
1451