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