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