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