]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
Wire up the Item Attribute Editor with oils_lock_page. Some tweaks needed to global_...
[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                 function post_c(v) {
1193                     try {
1194                         /* FIXME - kludgy */
1195                         var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1196                         var f;
1197                         switch(t) {
1198                             case 'attribute' :
1199                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1200                             break;
1201                             case 'stat_cat' :
1202                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1203                             break;
1204                             case 'owning_lib' :
1205                                 f = null;
1206                             break;
1207                         }
1208                         g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1209                         block = false;
1210                         setTimeout(
1211                             function() {
1212                                 g.summarize( g.copies );
1213                                 g.render();
1214                                 g.check_for_unmet_required_fields();
1215                                 document.getElementById(caption.id).focus();
1216                             }, 0
1217                         );
1218                     } catch(E) {
1219                         g.error.standard_unexpected_error_alert('post_c',E);
1220                     }
1221                 }
1222                 var x; var c; eval( input_cmd );
1223                 if (x) {
1224                     util.widgets.remove_children(vbox);
1225                     util.widgets.remove_children(hbox);
1226                     util.widgets.remove_children(hbox2);
1227                     hbox.appendChild(x);
1228                     var apply = document.createElement('button');
1229                     apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
1230                     apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
1231                     hbox2.appendChild(apply);
1232                     apply.addEventListener('command',function() { c(x.value); },false);
1233                     var cancel = document.createElement('button');
1234                     cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
1235                     cancel.addEventListener('command',function() {
1236                             setTimeout( function() {
1237                                     g.summarize( g.copies );
1238                                     g.render();
1239                                     g.check_for_unmet_required_fields();
1240                                     document.getElementById(caption.id).focus(); 
1241                                 }, 0
1242                             );
1243                         }, false
1244                     );
1245                     hbox2.appendChild(cancel);
1246                     setTimeout( function() { x.focus(); }, 0 );
1247                 }
1248             } catch(E) {
1249                 g.error.standard_unexpected_error_alert('render_input',E);
1250             }
1251         }
1252         vbox.addEventListener('click',on_click, false);
1253         hbox.addEventListener('click',on_click, false);
1254         caption.addEventListener('click',on_click, false);
1255         caption.addEventListener('keypress',function(ev) {
1256             if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1257         }, false);
1258         caption.setAttribute('style','-moz-user-focus: normal');
1259         caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1260         caption.setAttribute('onblur','this.setAttribute("class","")');
1261
1262     } catch(E) {
1263         g.error.sdump('D_ERROR',E + '\n');
1264     }
1265 }
1266
1267 /******************************************************************************************************/
1268 /* store the copies in the global xpcom stash */
1269
1270 g.stash_and_close = function() {
1271     try {
1272         oils_unlock_page();
1273
1274         if (g.handle_update) {
1275             try {
1276                 var r = g.network.request(
1277                     api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1278                     api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1279                     [ ses(), g.copies, true ]
1280                 );
1281                 if (typeof r.ilsevent != 'undefined') {
1282                     g.error.standard_unexpected_error_alert('copy update',r);
1283                 }
1284                 /* FIXME -- revisit the return value here */
1285             } catch(E) {
1286                 alert($('catStrings').getString('staff.cat.copy_editor.handle_update.error') + ' ' + js2JSON(E));
1287             }
1288         }
1289         //g.data.temp_copies = js2JSON( g.copies );
1290         //g.data.stash('temp_copies');
1291         xulG.copies = g.copies;
1292         update_modal_xulG(xulG);
1293         JSAN.use('util.widgets');
1294         util.widgets.dispatch('close',window);
1295     } catch(E) {
1296         alert('Error in copy_editor.js, g.stash_and_close(): '+E);
1297     }
1298 }
1299
1300 /******************************************************************************************************/
1301 /* spawn copy notes interface */
1302
1303 g.copy_notes = function() {
1304     JSAN.use('util.window'); var win = new util.window();
1305     win.open(
1306         urls.XUL_COPY_NOTES, 
1307         //+ '?copy_id=' + window.escape(g.copies[0].id()),
1308         $("catStrings").getString("staff.cat.copy_editor.copy_notes"),'chrome,resizable,modal',
1309         { 'copy_id' : g.copies[0].id() }
1310     );
1311 }
1312
1313 /******************************************************************************************************/
1314 /* hides or unhides stat cats based on library stat cat filter menu */
1315 g.toggle_stat_cat_display = function(el) {
1316     if (!el) return;
1317     var visible = el.getAttribute('checked');
1318     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1319     for (var n = 0; n < nl.length; n++) {
1320         if (visible) {
1321             nl[n].setAttribute('hidden','false');
1322         } else {
1323             nl[n].setAttribute('hidden','true');
1324         }
1325     }
1326     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1327     g.save_attributes();
1328 }
1329
1330 /******************************************************************************************************/
1331 /* This adds a stat cat definition to the stat cat pane for rendering */
1332 g.save_attributes = function() {
1333     JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1334     var what_to_save = {};
1335     for (var i in g.copy_editor_prefs) {
1336         what_to_save[i] = [];
1337         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1338     }
1339     util.widgets.save_attributes(file, what_to_save );
1340 }
1341
1342 /******************************************************************************************************/
1343 /* This adds a stat cat definition to the stat cat pane for rendering */
1344 g.add_stat_cat = function(sc) {
1345     try {
1346         if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1347
1348         var sc_id = sc;
1349
1350         if (typeof sc == 'object') {
1351
1352             sc_id = sc.id();
1353         }
1354
1355         if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1356
1357         g.stat_cat_seen[ sc_id ] = 1;
1358
1359         if (typeof sc != 'object') {
1360
1361             sc = g.network.simple_request(
1362                 'FM_ASC_BATCH_RETRIEVE',
1363                 [ ses(), [ sc_id ] ]
1364             )[0];
1365
1366         }
1367
1368         g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1369
1370         var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1371
1372         if (get_bool( sc.required() )) {
1373             g.required[ label_name ] = 1;
1374         }
1375
1376         var temp_array = [
1377             label_name,
1378             {
1379                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1380                     + sc.id() + '; } ); l ? l.value() : $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null");',
1381                 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() 
1382                     + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1383                     + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1384                 attr: {
1385                     sc_lib: sc.owner(),
1386                 }
1387             }
1388         ];
1389
1390         g.panes_and_field_names.right_pane4.push( temp_array );
1391     } catch(E) {
1392         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.add_stat_cat.error'), E);
1393     }
1394 }
1395
1396 /******************************************************************************************************/
1397 /* Add stat cats to the panes_and_field_names.right_pane4 */
1398 g.populate_stat_cats = function() {
1399     try {
1400         g.data.stash_retrieve();
1401         g.stat_cat_seen = {}; // used for determining whether a stat cat is displayed (and is eligible to be manipulated via a template)
1402
1403         function get(lib_id,only_these) {
1404             g.data.stash_retrieve();
1405             var label = 'asc_list_for_lib_'+lib_id;
1406             if (typeof g.data[label] == 'undefined') {
1407                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1408                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1409                 var temp_list = [];
1410                 for (var j = 0; j < robj.length; j++) {
1411                     var my_asc = robj[j];
1412                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1413                     if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1414                         g.data.hash.asc[ my_asc.id() ] = my_asc;
1415                     }
1416                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1417                     if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1418                         temp_list.push( my_asc );
1419                     }
1420                 }
1421                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1422             }
1423             return g.data[label];
1424         }
1425
1426         /* The stat cats for the pertinent library -- this is based on workstation ou */
1427         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1428         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1429         for (var i = 0; i < g.data.list.my_asc.length; i++) {
1430             g.add_stat_cat( g.data.list.my_asc[i] );
1431         }
1432
1433         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1434             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1435
1436         function add_common_ancestors(sc_libs) {
1437             JSAN.use('util.fm_utils'); 
1438             var libs = []; for (var i in sc_libs) libs.push(i);
1439             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1440             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1441             if (ancestor) {
1442                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1443                 var asc_list = get(ancestor, ancestors);
1444                 for (var i = 0; i < asc_list.length; i++) {
1445                     g.add_stat_cat( asc_list[i] );
1446                 }
1447             }
1448         }
1449
1450         /* stat cats based on stat cat entries present on these copies */
1451         var sc_libs = {};
1452         for (var i = 0; i < g.copies.length; i++) {
1453             var entries = g.copies[i].stat_cat_entries();
1454             if (!entries) entries = [];
1455             for (var j = 0; j < entries.length; j++) {
1456                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1457                 sc_libs[ lib ] = true;
1458             }
1459         }
1460         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1461
1462         /* stat cats based on Circ Lib */
1463         sc_libs = {};
1464         for (var i = 0; i < g.copies.length; i++) {
1465             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1466             sc_libs[ circ_lib ] = true;
1467         }
1468         add_common_ancestors(sc_libs);
1469
1470         /* stat cats based on Owning Lib */
1471         sc_libs = {};
1472         for (var i = 0; i < g.copies.length; i++) {
1473             var cn_id = g.copies[i].call_number();
1474             if (cn_id > 0) {
1475                 if (! g.map_acn[ cn_id ]) {
1476                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1477                     if (typeof req.ilsevent == 'undefined') {
1478                         g.map_acn[ cn_id ] = req;
1479                     } else {
1480                         continue;
1481                     }
1482                 }
1483                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1484                 sc_libs[ owning_lib ] = true;
1485             }
1486         }
1487         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1488
1489         g.panes_and_field_names.right_pane4.sort();
1490
1491     } catch(E) {
1492         alert(E);
1493         g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.populate_stat_cat.error'),E);
1494     }
1495 }
1496
1497 g.check_for_unmet_required_fields = function() {
1498     var abort = [];
1499     for (var fn in g.required) {
1500         if (typeof g.populated[fn] == 'undefined') {
1501             abort.push(fn);
1502         }
1503     }
1504     if (abort.length > 0) {
1505         $('save').setAttribute('disabled','true'); 
1506     } else {
1507         $('save').setAttribute('disabled','false'); 
1508     }
1509 }
1510
1511