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