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