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