]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
do the stat cat filtering at the end of the render phase
[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('id','filter_'+sc_libs2[i][1]);
970         menuitem.setAttribute('type','checkbox');
971         menuitem.setAttribute('checked','true');
972         menuitem.setAttribute('label',sc_libs2[i][0]);
973         menuitem.setAttribute('value',sc_libs2[i][1]);
974         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
975         x.appendChild(menuitem);
976     }
977
978         /******************************************************************************************************/
979         /* Prepare the panes */
980
981         var groupbox; var caption; var vbox; var grid; var rows;
982         
983         /******************************************************************************************************/
984         /* Loop through the field names */
985
986         for (h in g.panes_and_field_names) {
987                 if (!document.getElementById(h)) continue;
988                 for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
989                         try {
990                                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
991                                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
992                 if (attr) {
993                     for (var a in attr) {
994                         groupbox.setAttribute(a,attr[a]);
995                     }
996                 }
997                                 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
998                                 caption = document.createElement('caption'); groupbox.appendChild(caption);
999                                 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
1000                                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
1001                                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1002                                 grid.setAttribute('flex','1');
1003                                 rows = grid.lastChild;
1004                                 var row;
1005                                 
1006                                 /**************************************************************************************/
1007                                 /* Loop through each value for the field */
1008
1009                                 for (var j in g.summary[fn]) {
1010                                         var value = j; var count = g.summary[fn][j];
1011                                         row = document.createElement('row'); rows.appendChild(row);
1012                                         var label1 = document.createElement('description'); row.appendChild(label1);
1013                                         if (g.special_exception[ fn ]) {
1014                                                 g.special_exception[ fn ]( label1, value );
1015                                         } else {
1016                                                 label1.appendChild( document.createTextNode(value) );
1017                                         }
1018                                         var label2 = document.createElement('description'); row.appendChild(label2);
1019                                         var unit = count == 1 ? 'copy' : 'copies';
1020                                         label2.appendChild( document.createTextNode(count + ' ' + unit) );
1021                                 }
1022                                 var hbox = document.createElement('hbox'); 
1023                                 hbox.setAttribute('id',fn);
1024                                 groupbox.appendChild(hbox);
1025                                 var hbox2 = document.createElement('hbox');
1026                                 groupbox.appendChild(hbox2);
1027
1028                                 /**************************************************************************************/
1029                                 /* Render the input widget */
1030
1031                                 if (f[1].input && g.edit) {
1032                                         g.render_input(hbox,f[1]);
1033                                 }
1034
1035                         } catch(E) {
1036                                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1037                         }
1038                 }
1039         }
1040     
1041     
1042         /******************************************************************************************************/
1043         /* Synchronize stat cat visiblity with library filter menu */
1044     JSAN.use('util.file'); 
1045         var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1046         g.copy_editor_prefs = util.widgets.load_attributes(file);
1047     for (var i in g.copy_editor_prefs) {
1048         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1049             try { 
1050                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1051             } catch(E) { alert(E); }
1052         }
1053     }
1054
1055
1056 }
1057
1058 /******************************************************************************************************/
1059 /* This actually draws the change button and input widget for a given field */
1060 g.render_input = function(node,blob) {
1061         try {
1062                 // node = hbox ;    groupbox ->  hbox, hbox
1063
1064                 var groupbox = node.parentNode;
1065                 var caption = groupbox.firstChild;
1066                 var vbox = node.previousSibling;
1067                 var hbox = node;
1068                 var hbox2 = node.nextSibling;
1069
1070                 var input_cmd = blob.input;
1071                 var render_cmd = blob.render;
1072         var attr = blob.attr;
1073
1074                 var block = false; var first = true;
1075
1076                 function on_mouseover(ev) {
1077                         groupbox.setAttribute('style','background: white');
1078                 }
1079
1080                 function on_mouseout(ev) {
1081                         groupbox.setAttribute('style','');
1082                 }
1083
1084                 vbox.addEventListener('mouseover',on_mouseover,false);
1085                 vbox.addEventListener('mouseout',on_mouseout,false);
1086                 groupbox.addEventListener('mouseover',on_mouseover,false);
1087                 groupbox.addEventListener('mouseout',on_mouseout,false);
1088                 groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1089                 groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1090
1091                 function on_click(ev){
1092                         try {
1093                                 if (block) return; block = true;
1094
1095                                 function post_c(v) {
1096                                         try {
1097                                                 /* FIXME - kludgy */
1098                                                 var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1099                                                 var f;
1100                                                 switch(t) {
1101                                                         case 'attribute' :
1102                                                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1103                                                         break;
1104                                                         case 'stat_cat' :
1105                                                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1106                                                         break;
1107                                                         case 'owning_lib' :
1108                                                                 f = null;
1109                                                         break;
1110                                                 }
1111                                                 g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1112                                                 block = false;
1113                                                 setTimeout(
1114                                                         function() {
1115                                                                 g.summarize( g.copies );
1116                                                                 g.render();
1117                                                                 document.getElementById(caption.id).focus();
1118                                                         }, 0
1119                                                 );
1120                                         } catch(E) {
1121                                                 g.error.standard_unexpected_error_alert('post_c',E);
1122                                         }
1123                                 }
1124                                 var x; var c; eval( input_cmd );
1125                                 if (x) {
1126                                         util.widgets.remove_children(vbox);
1127                                         util.widgets.remove_children(hbox);
1128                                         util.widgets.remove_children(hbox2);
1129                                         hbox.appendChild(x);
1130                                         var apply = document.createElement('button');
1131                                         apply.setAttribute('label','Apply');
1132                                         apply.setAttribute('accesskey','A');
1133                                         hbox2.appendChild(apply);
1134                                         apply.addEventListener('command',function() { c(x.value); },false);
1135                                         var cancel = document.createElement('button');
1136                                         cancel.setAttribute('label','Cancel');
1137                                         cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1138                                         hbox2.appendChild(cancel);
1139                                         setTimeout( function() { x.focus(); }, 0 );
1140                                 }
1141                         } catch(E) {
1142                                 g.error.standard_unexpected_error_alert('render_input',E);
1143                         }
1144                 }
1145                 vbox.addEventListener('click',on_click, false);
1146                 hbox.addEventListener('click',on_click, false);
1147                 caption.addEventListener('click',on_click, false);
1148                 caption.addEventListener('keypress',function(ev) {
1149                         if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1150                 }, false);
1151                 caption.setAttribute('style','-moz-user-focus: normal');
1152                 caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1153                 caption.setAttribute('onblur','this.setAttribute("class","")');
1154
1155         } catch(E) {
1156                 g.error.sdump('D_ERROR',E + '\n');
1157         }
1158 }
1159
1160 /******************************************************************************************************/
1161 /* store the copies in the global xpcom stash */
1162
1163 g.stash_and_close = function() {
1164         try {
1165                 if (g.handle_update) {
1166                         try {
1167                                 var r = g.network.request(
1168                                         api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1169                                         api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1170                                         [ ses(), g.copies, true ]
1171                                 );
1172                                 if (typeof r.ilsevent != 'undefined') {
1173                                         g.error.standard_unexpected_error_alert('copy update',r);
1174                                 } else {
1175                                         alert('Items added/modified.');
1176                                 }
1177                                 /* FIXME -- revisit the return value here */
1178                         } catch(E) {
1179                                 alert('copy update error: ' + js2JSON(E));
1180                         }
1181                 }
1182                 //g.data.temp_copies = js2JSON( g.copies );
1183                 //g.data.stash('temp_copies');
1184                 xulG.copies = g.copies;
1185                 update_modal_xulG(xulG);
1186                 window.close();
1187         } catch(E) {
1188                 g.error.standard_unexpected_error_alert('stash and close',E);
1189         }
1190 }
1191
1192 /******************************************************************************************************/
1193 /* spawn copy notes interface */
1194
1195 g.copy_notes = function() {
1196         JSAN.use('util.window'); var win = new util.window();
1197         win.open(
1198                 urls.XUL_COPY_NOTES, 
1199                 //+ '?copy_id=' + window.escape(g.copies[0].id()),
1200                 'Copy Notes','chrome,resizable,modal',
1201                 { 'copy_id' : g.copies[0].id() }
1202         );
1203 }
1204
1205 /******************************************************************************************************/
1206 /* hides or unhides stat cats based on library stat cat filter menu */
1207 g.toggle_stat_cat_display = function(el) {
1208     var visible = el.getAttribute('checked');
1209     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1210     for (var n = 0; n < nl.length; n++) {
1211         if (visible) {
1212             nl[n].setAttribute('hidden','false');
1213         } else {
1214             nl[n].setAttribute('hidden','true');
1215         }
1216     }
1217     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1218         JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1219     var what_to_save = {};
1220     for (var i in g.copy_editor_prefs) {
1221         what_to_save[i] = [];
1222         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1223     }
1224         util.widgets.save_attributes(file, what_to_save );
1225 }
1226
1227 /******************************************************************************************************/
1228 /* This adds a stat cat definition to the stat cat pane for rendering */
1229 g.add_stat_cat = function(sc) {
1230     try {
1231                 if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1232
1233                 var sc_id = sc;
1234
1235                 if (typeof sc == 'object') {
1236
1237                         sc_id = sc.id();
1238                 }
1239
1240                 if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1241
1242                 g.stat_cat_seen[ sc_id ] = 1;
1243
1244                 if (typeof sc != 'object') {
1245
1246                         sc = g.network.simple_request(
1247                                 'FM_ASC_BATCH_RETRIEVE',
1248                                 [ ses(), [ sc_id ] ]
1249                         )[0];
1250
1251                 }
1252
1253                 g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1254
1255                 var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1256
1257                 var temp_array = [
1258                         label_name,
1259                         {
1260                                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1261                                         + sc.id() + '; } ); l ? l.value() : "<Unset>";',
1262                                 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() 
1263                                         + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1264                                         + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1265                 attr: {
1266                     sc_lib: sc.owner(),
1267                 }
1268                         }
1269                 ];
1270
1271                 g.panes_and_field_names.right_pane4.push( temp_array );
1272         } catch(E) {
1273                 g.error.standard_unexpected_error_alert('Error adding stat cat to display definition',E);
1274     }
1275 }
1276
1277 /******************************************************************************************************/
1278 /* Add stat cats to the panes_and_field_names.right_pane4 */
1279 g.populate_stat_cats = function() {
1280     try {
1281         g.data.stash_retrieve();
1282                 g.stat_cat_seen = {};
1283
1284                 function get(lib_id,only_these) {
1285             g.data.stash_retrieve();
1286                         var label = 'asc_list_for_lib_'+lib_id;
1287                         if (typeof g.data[label] == 'undefined') {
1288                                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1289                                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1290                                 var temp_list = [];
1291                                 for (var j = 0; j < robj.length; j++) {
1292                                         var my_asc = robj[j];
1293                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1294                                         if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1295                                                 g.data.hash.asc[ my_asc.id() ] = my_asc;
1296                                         }
1297                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1298                                         if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1299                                                 temp_list.push( my_asc );
1300                                         }
1301                                 }
1302                                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1303                         }
1304                         return g.data[label];
1305                 }
1306
1307                 /* The stat cats for the pertinent library -- this is based on workstation ou */
1308         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1309         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1310                 for (var i = 0; i < g.data.list.my_asc.length; i++) {
1311                         g.add_stat_cat( g.data.list.my_asc[i] );
1312                 }
1313
1314         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1315             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1316
1317         function add_common_ancestors(sc_libs) {
1318             JSAN.use('util.fm_utils'); 
1319             var libs = []; for (var i in sc_libs) libs.push(i);
1320             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1321             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1322             if (ancestor) {
1323                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1324                 var asc_list = get(ancestor, ancestors);
1325                 for (var i = 0; i < asc_list.length; i++) {
1326                     g.add_stat_cat( asc_list[i] );
1327                 }
1328             }
1329         }
1330
1331                 /* stat cats based on stat cat entries present on these copies */
1332         var sc_libs = {};
1333                 for (var i = 0; i < g.copies.length; i++) {
1334                         var entries = g.copies[i].stat_cat_entries();
1335                         if (!entries) entries = [];
1336                         for (var j = 0; j < entries.length; j++) {
1337                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1338                                 sc_libs[ lib ] = true;
1339                         }
1340         }
1341         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1342
1343         /* stat cats based on Circ Lib */
1344         sc_libs = {};
1345                 for (var i = 0; i < g.copies.length; i++) {
1346             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1347             sc_libs[ circ_lib ] = true;
1348         }
1349         add_common_ancestors(sc_libs);
1350
1351         /* stat cats based on Owning Lib */
1352         sc_libs = {};
1353                 for (var i = 0; i < g.copies.length; i++) {
1354             var cn_id = g.copies[i].call_number();
1355                         if (cn_id > 0) {
1356                                 if (! g.map_acn[ cn_id ]) {
1357                                         g.map_acn[ cn_id ] = g.network.simple_request('FM_ACN_RETRIEVE',[ cn_id ]);
1358                                 }
1359                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1360                 sc_libs[ owning_lib ] = true;
1361                         }
1362                 }
1363         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1364
1365         g.panes_and_field_names.right_pane4.sort();
1366
1367     } catch(E) {
1368         g.error.standard_unexpected_error_alert('Error populating stat cats for display',E);
1369     }
1370 }
1371
1372