]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
tweak to item attribute editor to support hiding of fields with cat_custom.css
[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                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox); // main display widget goes here
1062                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1063                 grid.setAttribute('flex','1');
1064                 rows = grid.lastChild;
1065                 var row;
1066                 
1067                 /**************************************************************************************/
1068                 /* Loop through each value for the field */
1069
1070                 for (var j in g.summary[fn]) {
1071                     var value = j; var count = g.summary[fn][j];
1072                     row = document.createElement('row'); rows.appendChild(row);
1073                     var label1 = document.createElement('description'); row.appendChild(label1);
1074                     if (g.special_exception[ fn ]) {
1075                         g.special_exception[ fn ]( label1, value );
1076                     } else {
1077                         label1.appendChild( document.createTextNode(value) );
1078                     }
1079                     var label2 = document.createElement('description'); row.appendChild(label2);
1080                     var copy_count;
1081                     if (count == 1) {
1082                         copy_count = $('catStrings').getString('staff.cat.copy_editor.copy_count');
1083                     } else {
1084                         copy_count = $('catStrings').getFormattedString('staff.cat.copy_editor.copy_count.plural', [count]);
1085                     }
1086                     label2.appendChild( document.createTextNode(copy_count) );
1087                 }
1088                 groupbox.setAttribute('id','groupbox_'+fn); // this id is meant to be referenced by cat_custom.css for hiding fields
1089                 var hbox = document.createElement('hbox'); // main input controls go here
1090                 hbox.setAttribute('id',fn); // this id gets used to color areas green, etc.
1091                 groupbox.appendChild(hbox);
1092                 var hbox2 = document.createElement('hbox'); // cancel/apply buttons go here
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