]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
Switch to .authoritative version of some methods
[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.authoritative',
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.authoritative',[ 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.authoritative',[ 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.authoritative',[ 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.simple_request(
663                                 'FM_ACN_RETRIEVE.authoritative',
664                                 [ value ],
665                                 function(req) {
666                                         var cn = '??? id = ' + value;
667                                         try {
668                                                 cn = req.getResultObject();
669                                         } catch(E) {
670                                                 g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
671                                         }
672                                         util.widgets.set_text(label,g.data.hash.aou[ cn.owning_lib() ].shortname() + ' : ' + cn.label());
673                                 }
674                         );
675                 } else { /* a yet to be created call number */
676                         if (g.callnumbers) {
677                                 util.widgets.set_text(label,g.data.hash.aou[ g.callnumbers[value].owning_lib ].shortname() + ' : ' + g.callnumbers[value].label);
678                         }
679                 }
680         },
681         'Creator' : function(label,value) {
682                 if (value == null || value == '' || value == 'null') return;
683                 g.network.simple_request(
684                         'FM_AU_RETRIEVE_VIA_ID',
685                         [ ses(), value ],
686                         function(req) {
687                                 var p = '??? id = ' + value;
688                                 try {
689                                         p = req.getResultObject();
690                                         p = p.usrname();
691
692                                 } catch(E) {
693                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
694                                 }
695                                 JSAN.use('util.widgets');
696                                 util.widgets.set_text(label,p);
697                         }
698                 );
699         },
700         'Last Editor' : function(label,value) {
701                 if (value == null || value == '' || value == 'null') return;
702                 g.network.simple_request(
703                         'FM_AU_RETRIEVE_VIA_ID',
704                         [ ses(), value ],
705                         function(req) {
706                                 var p = '??? id = ' + value;
707                                 try {
708                                         p = req.getResultObject();
709                                         p = p.usrname();
710
711                                 } catch(E) {
712                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
713                                 }
714                                 util.widgets.set_text(label,p);
715                         }
716                 );
717         }
718
719 }
720
721 /******************************************************************************************************/
722 g.readonly_stat_cat_names = [];
723 g.editable_stat_cat_names = [];
724
725 /******************************************************************************************************/
726 /* These get show in the left panel */
727
728 function init_panes() {
729 g.panes_and_field_names = {
730
731         'left_pane' :
732 [
733         [
734                 "Barcode",               
735                 {
736                         render: 'fm.barcode();',
737                 }
738         ], 
739         [
740                 "Creation Date",
741                 { 
742                         render: 'util.date.formatted_date( fm.create_date(), "%F");',
743                 }
744         ],
745         [
746                 "Creator",
747                 { 
748                         render: 'fm.creator();',
749                 }
750         ],
751         [
752                 "Last Edit Date",
753                 { 
754                         render: 'util.date.formatted_date( fm.edit_date(), "%F");',
755                 }
756         ],
757         [
758                 "Last Editor",
759                 {
760                         render: 'fm.editor();',
761                 }
762         ],
763
764 ],
765
766 'right_pane' :
767 [
768         [
769                 "Shelving Location",
770                 { 
771                         render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
772                         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);',
773
774                 }
775         ],
776         [
777                 "Circulation Library",          
778                 {       
779                         render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
780                         //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);',
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( 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);',
782                 } 
783         ],
784         [
785                 "Owning Lib : Call Number",     
786                 {
787                         render: 'fm.call_number();',
788                         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,
789                 }
790         ],
791         [
792                 "Copy Number",
793                 { 
794                         render: 'fm.copy_number() == null ? "<Unset>" : fm.copy_number()',
795                         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);',
796                 }
797         ],
798
799
800 ],
801
802 'right_pane2' :
803 [
804         [
805                 "Circulate?",
806                 {       
807                         render: 'fm.circulate() == null ? "<Unset>" : ( get_bool( fm.circulate() ) ? "Yes" : "No" )',
808                         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);',
809                 }
810         ],
811         [
812                 "Holdable?",
813                 { 
814                         render: 'fm.holdable() == null ? "<Unset>" : ( get_bool( fm.holdable() ) ? "Yes" : "No" )', 
815                         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);',
816                 }
817         ],
818         [
819                 "Age Protection",
820                 {
821                         render: 'fm.age_protect() == null ? "<Unset>" : ( typeof fm.age_protect() == "object" ? fm.age_protect().name() : g.data.hash.crahp[ fm.age_protect() ].name() )', 
822                         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);',
823                 }
824
825         ],
826         [
827                 "Loan Duration",
828                 { 
829                         render: 'switch(Number(fm.loan_duration())){ case 1: "Short"; break; case 2: "Normal"; break; case 3: "Long"; break; }',
830                         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);',
831
832                 }
833         ],
834         [
835                 "Fine Level",
836                 {
837                         render: 'switch(Number(fm.fine_level())){ case 1: "Low"; break; case 2: "Normal"; break; case 3: "High"; break; }',
838                         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);',
839                 }
840         ],
841
842          [
843                 "Circulate as Type",    
844                 {       
845                         render: 'fm.circ_as_type() == null ? "<Unset>" : g.data.hash.citm[ fm.circ_as_type() ].value()',
846                         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);',
847                 } 
848         ],
849         [
850                 "Circulation Modifier",
851                 {       
852                         render: 'fm.circ_modifier() == null ? "<Unset>" : fm.circ_modifier()',
853                         /*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);',*/
854                         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);',
855                 }
856         ],
857 ],
858
859 'right_pane3' :
860 [       [
861                 "Alert Message",
862                 {
863                         render: 'fm.alert_message() == null ? "<Unset>" : fm.alert_message()',
864                         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);',
865                 }
866         ],
867
868         [
869                 "Deposit?",
870                 { 
871                         render: 'fm.deposit() == null ? "<Unset>" : ( get_bool( fm.deposit() ) ? "Yes" : "No" )',
872                         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);',
873                 }
874         ],
875         [
876                 "Deposit Amount",
877                 { 
878                         render: 'if (fm.deposit_amount() == null) { "<Unset>"; } else { util.money.sanitize( fm.deposit_amount() ); }',
879                         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);',
880                 }
881         ],
882         [
883                 "Price",
884                 { 
885                         render: 'if (fm.price() == null) { "<Unset>"; } else { util.money.sanitize( fm.price() ); }', 
886                         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);',
887                 }
888         ],
889
890         [
891                 "OPAC Visible?",
892                 { 
893                         render: 'fm.opac_visible() == null ? "<Unset>" : ( get_bool( fm.opac_visible() ) ? "Yes" : "No" )', 
894                         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);',
895                 }
896         ],
897         [
898                 "Reference?",
899                 { 
900                         render: 'fm.ref() == null ? "<Unset>" : ( get_bool( fm.ref() ) ? "Yes" : "No" )', 
901                         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);',
902                 }
903         ],
904 ],
905
906 'right_pane4' : 
907 [
908 ]
909
910 };
911 }
912
913 /******************************************************************************************************/
914 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
915
916 g.summarize = function( copies ) {
917         /******************************************************************************************************/
918         /* Setup */
919
920         JSAN.use('util.date'); JSAN.use('util.money');
921         g.summary = {};
922         g.field_names = [];
923         for (var i in g.panes_and_field_names) {
924                 g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
925         }
926         g.field_names = g.field_names.concat( g.editable_stat_cat_names );
927         g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
928
929         /******************************************************************************************************/
930         /* Loop through the field names */
931
932         for (var i = 0; i < g.field_names.length; i++) {
933
934                 var field_name = g.field_names[i][0];
935                 var render = g.field_names[i][1].render;
936         var attr = g.field_names[i][1].attr;
937                 g.summary[ field_name ] = {};
938
939                 /******************************************************************************************************/
940                 /* Loop through the copies */
941
942                 for (var j = 0; j < copies.length; j++) {
943
944                         var fm = copies[j];
945                         var cmd = render || ('fm.' + field_name + '();');
946                         var value = '???';
947
948                         /**********************************************************************************************/
949                         /* Try to retrieve the value for this field for this copy */
950
951                         try { 
952                                 value = eval( cmd ); 
953                         } catch(E) { 
954                                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
955                         }
956                         if (typeof value == 'object' && value != null) {
957                                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
958                         }
959
960                         /**********************************************************************************************/
961                         /* Tally the count */
962
963                         if (g.summary[ field_name ][ value ]) {
964                                 g.summary[ field_name ][ value ]++;
965                         } else {
966                                 g.summary[ field_name ][ value ] = 1;
967                         }
968                 }
969         }
970         g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
971 }
972
973 /******************************************************************************************************/
974 /* Display the summarized data and inputs for editing */
975
976 g.render = function() {
977
978         /******************************************************************************************************/
979         /* Library setup and clear any existing interface */
980
981         JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
982
983         for (var i in g.panes_and_field_names) {
984                 var p = document.getElementById(i);
985                 if (p) util.widgets.remove_children(p);
986         }
987
988         /******************************************************************************************************/
989         /* Populate the library filter menu for stat cats */
990
991     var sc_libs = {};
992     for (var i = 0; i < g.panes_and_field_names.right_pane4.length; i++) {
993         sc_libs[ g.panes_and_field_names.right_pane4[i][1].attr.sc_lib ] = true;
994     }
995     var sc_libs2 = [];
996     for (var i in sc_libs) { sc_libs2.push( [ g.data.hash.aou[ i ].shortname(), i ] ); }
997     sc_libs2.sort();
998     var x = document.getElementById("stat_cat_lib_filter_menu").firstChild;
999     JSAN.use('util.widgets'); util.widgets.remove_children(x);
1000     for (var i = 0; i < sc_libs2.length; i++) {
1001         var menuitem = document.createElement('menuitem');
1002         menuitem.setAttribute('id','filter_'+sc_libs2[i][1]);
1003         menuitem.setAttribute('type','checkbox');
1004         menuitem.setAttribute('checked','true');
1005         menuitem.setAttribute('label',sc_libs2[i][0]);
1006         menuitem.setAttribute('value',sc_libs2[i][1]);
1007         menuitem.setAttribute('oncommand','try{g.toggle_stat_cat_display(this);}catch(E){alert(E);}');
1008         x.appendChild(menuitem);
1009     }
1010
1011         /******************************************************************************************************/
1012         /* Prepare the panes */
1013
1014         var groupbox; var caption; var vbox; var grid; var rows;
1015         
1016         /******************************************************************************************************/
1017         /* Loop through the field names */
1018
1019         for (h in g.panes_and_field_names) {
1020                 if (!document.getElementById(h)) continue;
1021                 for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
1022                         try {
1023                                 var f = g.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
1024                                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
1025                 if (attr) {
1026                     for (var a in attr) {
1027                         groupbox.setAttribute(a,attr[a]);
1028                     }
1029                 }
1030                                 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
1031                                 caption = document.createElement('caption'); groupbox.appendChild(caption);
1032                                 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
1033                                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
1034                                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
1035                                 grid.setAttribute('flex','1');
1036                                 rows = grid.lastChild;
1037                                 var row;
1038                                 
1039                                 /**************************************************************************************/
1040                                 /* Loop through each value for the field */
1041
1042                                 for (var j in g.summary[fn]) {
1043                                         var value = j; var count = g.summary[fn][j];
1044                                         row = document.createElement('row'); rows.appendChild(row);
1045                                         var label1 = document.createElement('description'); row.appendChild(label1);
1046                                         if (g.special_exception[ fn ]) {
1047                                                 g.special_exception[ fn ]( label1, value );
1048                                         } else {
1049                                                 label1.appendChild( document.createTextNode(value) );
1050                                         }
1051                                         var label2 = document.createElement('description'); row.appendChild(label2);
1052                                         var unit = count == 1 ? 'copy' : 'copies';
1053                                         label2.appendChild( document.createTextNode(count + ' ' + unit) );
1054                                 }
1055                                 var hbox = document.createElement('hbox'); 
1056                                 hbox.setAttribute('id',fn);
1057                                 groupbox.appendChild(hbox);
1058                                 var hbox2 = document.createElement('hbox');
1059                                 groupbox.appendChild(hbox2);
1060
1061                                 /**************************************************************************************/
1062                                 /* Render the input widget */
1063
1064                                 if (f[1].input && g.edit) {
1065                                         g.render_input(hbox,f[1]);
1066                                 }
1067
1068                         } catch(E) {
1069                                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1070                         }
1071                 }
1072         }
1073     
1074     
1075         /******************************************************************************************************/
1076         /* Synchronize stat cat visibility with library filter menu, and default template selection */
1077     JSAN.use('util.file'); 
1078         var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1079         g.copy_editor_prefs = util.widgets.load_attributes(file);
1080     for (var i in g.copy_editor_prefs) {
1081         if (i.match(/filter_/) && g.copy_editor_prefs[i].checked == '') {
1082             try { 
1083                 g.toggle_stat_cat_display( document.getElementById(i) ); 
1084             } catch(E) { alert(E); }
1085         }
1086     }
1087     if (g.template_menu) g.template_menu.value = g.template_menu.getAttribute('value');
1088
1089 }
1090
1091 /******************************************************************************************************/
1092 /* This actually draws the change button and input widget for a given field */
1093 g.render_input = function(node,blob) {
1094         try {
1095                 // node = hbox ;    groupbox ->  hbox, hbox
1096
1097                 var groupbox = node.parentNode;
1098                 var caption = groupbox.firstChild;
1099                 var vbox = node.previousSibling;
1100                 var hbox = node;
1101                 var hbox2 = node.nextSibling;
1102
1103                 var input_cmd = blob.input;
1104                 var render_cmd = blob.render;
1105         var attr = blob.attr;
1106
1107                 var block = false; var first = true;
1108
1109                 function on_mouseover(ev) {
1110                         groupbox.setAttribute('style','background: white');
1111                 }
1112
1113                 function on_mouseout(ev) {
1114                         groupbox.setAttribute('style','');
1115                 }
1116
1117                 vbox.addEventListener('mouseover',on_mouseover,false);
1118                 vbox.addEventListener('mouseout',on_mouseout,false);
1119                 groupbox.addEventListener('mouseover',on_mouseover,false);
1120                 groupbox.addEventListener('mouseout',on_mouseout,false);
1121                 groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1122                 groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1123
1124                 function on_click(ev){
1125                         try {
1126                                 if (block) return; block = true;
1127
1128                                 function post_c(v) {
1129                                         try {
1130                                                 /* FIXME - kludgy */
1131                                                 var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1132                                                 var f;
1133                                                 switch(t) {
1134                                                         case 'attribute' :
1135                                                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1136                                                         break;
1137                                                         case 'stat_cat' :
1138                                                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1139                                                         break;
1140                                                         case 'owning_lib' :
1141                                                                 f = null;
1142                                                         break;
1143                                                 }
1144                                                 g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1145                                                 block = false;
1146                                                 setTimeout(
1147                                                         function() {
1148                                                                 g.summarize( g.copies );
1149                                                                 g.render();
1150                                                                 document.getElementById(caption.id).focus();
1151                                                         }, 0
1152                                                 );
1153                                         } catch(E) {
1154                                                 g.error.standard_unexpected_error_alert('post_c',E);
1155                                         }
1156                                 }
1157                                 var x; var c; eval( input_cmd );
1158                                 if (x) {
1159                                         util.widgets.remove_children(vbox);
1160                                         util.widgets.remove_children(hbox);
1161                                         util.widgets.remove_children(hbox2);
1162                                         hbox.appendChild(x);
1163                                         var apply = document.createElement('button');
1164                                         apply.setAttribute('label','Apply');
1165                                         apply.setAttribute('accesskey','A');
1166                                         hbox2.appendChild(apply);
1167                                         apply.addEventListener('command',function() { c(x.value); },false);
1168                                         var cancel = document.createElement('button');
1169                                         cancel.setAttribute('label','Cancel');
1170                                         cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1171                                         hbox2.appendChild(cancel);
1172                                         setTimeout( function() { x.focus(); }, 0 );
1173                                 }
1174                         } catch(E) {
1175                                 g.error.standard_unexpected_error_alert('render_input',E);
1176                         }
1177                 }
1178                 vbox.addEventListener('click',on_click, false);
1179                 hbox.addEventListener('click',on_click, false);
1180                 caption.addEventListener('click',on_click, false);
1181                 caption.addEventListener('keypress',function(ev) {
1182                         if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1183                 }, false);
1184                 caption.setAttribute('style','-moz-user-focus: normal');
1185                 caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1186                 caption.setAttribute('onblur','this.setAttribute("class","")');
1187
1188         } catch(E) {
1189                 g.error.sdump('D_ERROR',E + '\n');
1190         }
1191 }
1192
1193 /******************************************************************************************************/
1194 /* store the copies in the global xpcom stash */
1195
1196 g.stash_and_close = function() {
1197         try {
1198                 if (g.handle_update) {
1199                         try {
1200                                 var r = g.network.request(
1201                                         api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1202                                         api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1203                                         [ ses(), g.copies, true ]
1204                                 );
1205                                 if (typeof r.ilsevent != 'undefined') {
1206                                         g.error.standard_unexpected_error_alert('copy update',r);
1207                                 } else {
1208                                         alert('Items added/modified.');
1209                                 }
1210                                 /* FIXME -- revisit the return value here */
1211                         } catch(E) {
1212                                 alert('copy update error: ' + js2JSON(E));
1213                         }
1214                 }
1215                 //g.data.temp_copies = js2JSON( g.copies );
1216                 //g.data.stash('temp_copies');
1217                 xulG.copies = g.copies;
1218                 update_modal_xulG(xulG);
1219                 window.close();
1220         } catch(E) {
1221                 g.error.standard_unexpected_error_alert('stash and close',E);
1222         }
1223 }
1224
1225 /******************************************************************************************************/
1226 /* spawn copy notes interface */
1227
1228 g.copy_notes = function() {
1229         JSAN.use('util.window'); var win = new util.window();
1230         win.open(
1231                 urls.XUL_COPY_NOTES, 
1232                 //+ '?copy_id=' + window.escape(g.copies[0].id()),
1233                 'Copy Notes','chrome,resizable,modal',
1234                 { 'copy_id' : g.copies[0].id() }
1235         );
1236 }
1237
1238 /******************************************************************************************************/
1239 /* hides or unhides stat cats based on library stat cat filter menu */
1240 g.toggle_stat_cat_display = function(el) {
1241     if (!el) return;
1242     var visible = el.getAttribute('checked');
1243     var nl = document.getElementsByAttribute('sc_lib',el.getAttribute('value'));
1244     for (var n = 0; n < nl.length; n++) {
1245         if (visible) {
1246             nl[n].setAttribute('hidden','false');
1247         } else {
1248             nl[n].setAttribute('hidden','true');
1249         }
1250     }
1251     g.copy_editor_prefs[ el.getAttribute('id') ] = { 'checked' : visible };
1252     g.save_attributes();
1253 }
1254
1255 /******************************************************************************************************/
1256 /* This adds a stat cat definition to the stat cat pane for rendering */
1257 g.save_attributes = function() {
1258         JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file('copy_editor_prefs.'+g.data.server_unadorned);
1259     var what_to_save = {};
1260     for (var i in g.copy_editor_prefs) {
1261         what_to_save[i] = [];
1262         for (var j in g.copy_editor_prefs[i]) what_to_save[i].push(j);
1263     }
1264         util.widgets.save_attributes(file, what_to_save );
1265 }
1266
1267 /******************************************************************************************************/
1268 /* This adds a stat cat definition to the stat cat pane for rendering */
1269 g.add_stat_cat = function(sc) {
1270     try {
1271                 if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; g.data.stash('hash'); }
1272
1273                 var sc_id = sc;
1274
1275                 if (typeof sc == 'object') {
1276
1277                         sc_id = sc.id();
1278                 }
1279
1280                 if (typeof g.stat_cat_seen[sc_id] != 'undefined') { return; }
1281
1282                 g.stat_cat_seen[ sc_id ] = 1;
1283
1284                 if (typeof sc != 'object') {
1285
1286                         sc = g.network.simple_request(
1287                                 'FM_ASC_BATCH_RETRIEVE',
1288                                 [ ses(), [ sc_id ] ]
1289                         )[0];
1290
1291                 }
1292
1293                 g.data.hash.asc[ sc.id() ] = sc; g.data.stash('hash');
1294
1295                 var label_name = g.data.hash.aou[ sc.owner() ].shortname() + " : " + sc.name();
1296
1297                 var temp_array = [
1298                         label_name,
1299                         {
1300                                 render: 'var l = util.functional.find_list( fm.stat_cat_entries(), function(e){ return e.stat_cat() == ' 
1301                                         + sc.id() + '; } ); l ? l.value() : "<Unset>";',
1302                                 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() 
1303                                         + '].entries(), function(obj){ return [ obj.value(), obj.id() ]; } ) ).sort() ); '
1304                                         + 'x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c),false);',
1305                 attr: {
1306                     sc_lib: sc.owner(),
1307                 }
1308                         }
1309                 ];
1310
1311                 g.panes_and_field_names.right_pane4.push( temp_array );
1312         } catch(E) {
1313                 g.error.standard_unexpected_error_alert('Error adding stat cat to display definition',E);
1314     }
1315 }
1316
1317 /******************************************************************************************************/
1318 /* Add stat cats to the panes_and_field_names.right_pane4 */
1319 g.populate_stat_cats = function() {
1320     try {
1321         g.data.stash_retrieve();
1322                 g.stat_cat_seen = {};
1323
1324                 function get(lib_id,only_these) {
1325             g.data.stash_retrieve();
1326                         var label = 'asc_list_for_lib_'+lib_id;
1327                         if (typeof g.data[label] == 'undefined') {
1328                                 var robj = g.network.simple_request('FM_ASC_RETRIEVE_VIA_AOU', [ ses(), lib_id ]);
1329                                 if (typeof robj.ilsevent != 'undefined') throw(robj);
1330                                 var temp_list = [];
1331                                 for (var j = 0; j < robj.length; j++) {
1332                                         var my_asc = robj[j];
1333                     if (typeof g.data.hash.asc == 'undefined') { g.data.hash.asc = {}; }
1334                                         if (typeof g.data.hash.asc[ my_asc.id() ] == 'undefined') {
1335                                                 g.data.hash.asc[ my_asc.id() ] = my_asc;
1336                                         }
1337                     var only_this_lib = my_asc.owner(); if (typeof only_this_lib == 'object') only_this_lib = only_this_lib.id();
1338                                         if (only_these.indexOf( String( only_this_lib ) ) != -1) {
1339                                                 temp_list.push( my_asc );
1340                                         }
1341                                 }
1342                                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
1343                         }
1344                         return g.data[label];
1345                 }
1346
1347                 /* The stat cats for the pertinent library -- this is based on workstation ou */
1348         var label = 'asc_list_for_' + typeof g.data.ws_ou == 'object' ? g.data.ws_ou.id() : g.data.ws_ou;
1349         g.data[ label ] = g.data.list.my_asc; g.data.stash('label');
1350                 for (var i = 0; i < g.data.list.my_asc.length; i++) {
1351                         g.add_stat_cat( g.data.list.my_asc[i] );
1352                 }
1353
1354         /* For the others, we want to consider the owning libs, circ libs, and any libs that have stat cats already on the copies,
1355             however, if batch editing, we only want to show the ones they have in common.  So let's compile the libs  */
1356
1357         function add_common_ancestors(sc_libs) {
1358             JSAN.use('util.fm_utils'); 
1359             var libs = []; for (var i in sc_libs) libs.push(i);
1360             var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
1361             if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
1362             if (ancestor) {
1363                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
1364                 var asc_list = get(ancestor, ancestors);
1365                 for (var i = 0; i < asc_list.length; i++) {
1366                     g.add_stat_cat( asc_list[i] );
1367                 }
1368             }
1369         }
1370
1371                 /* stat cats based on stat cat entries present on these copies */
1372         var sc_libs = {};
1373                 for (var i = 0; i < g.copies.length; i++) {
1374                         var entries = g.copies[i].stat_cat_entries();
1375                         if (!entries) entries = [];
1376                         for (var j = 0; j < entries.length; j++) {
1377                 var lib = entries[j].owner(); if (typeof lib == 'object') lib = lib.id();
1378                                 sc_libs[ lib ] = true;
1379                         }
1380         }
1381         add_common_ancestors(sc_libs); // CAVEAT - if a copy has no stat_cat_entries, it basically gets no vote here
1382
1383         /* stat cats based on Circ Lib */
1384         sc_libs = {};
1385                 for (var i = 0; i < g.copies.length; i++) {
1386             var circ_lib = g.copies[i].circ_lib(); if (typeof circ_lib == 'object') circ_lib = circ_lib.id();
1387             sc_libs[ circ_lib ] = true;
1388         }
1389         add_common_ancestors(sc_libs);
1390
1391         /* stat cats based on Owning Lib */
1392         sc_libs = {};
1393                 for (var i = 0; i < g.copies.length; i++) {
1394             var cn_id = g.copies[i].call_number();
1395                         if (cn_id > 0) {
1396                                 if (! g.map_acn[ cn_id ]) {
1397                     var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
1398                     if (typeof req.ilsevent == 'undefined') {
1399                                         g.map_acn[ cn_id ] = req;
1400                     } else {
1401                         continue;
1402                     }
1403                                 }
1404                 var owning_lib = g.map_acn[ cn_id ].owning_lib(); if (typeof owning_lib == 'object') owning_lib = owning_lib.id();
1405                 sc_libs[ owning_lib ] = true;
1406                         }
1407                 }
1408         add_common_ancestors(sc_libs); // CAVEAT - if a copy is a pre-cat, it basically gets no vote here
1409
1410         g.panes_and_field_names.right_pane4.sort();
1411
1412     } catch(E) {
1413         g.error.standard_unexpected_error_alert('Error populating stat cats for display',E);
1414     }
1415 }
1416
1417