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