]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/copy_editor.js
logic error fix
[working/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                                         if (only_these.indexOf( String( my_acpl.owning_lib() ) ) != -1) {
594                                                 temp_list.push( my_acpl );
595                                         }
596                                 }
597                                 g.data[label] = temp_list; g.data.stash(label,'hash','list');
598                         }
599                         return g.data[label];
600                 }
601
602                 var libs = []; var map_acn = {};
603                 for (var i = 0; i < g.copies.length; i++) {
604                         var cn_id = g.copies[i].call_number();
605                         if (cn_id > 0) {
606                                 if (! map_acn[ cn_id ]) {
607                                         map_acn[ cn_id ] = g.network.simple_request('FM_ACN_RETRIEVE',[ cn_id ]);
608                                         libs.push( map_acn[ cn_id ].owning_lib() );
609                                 }
610                         }
611                 }
612                 if (g.callnumbers) {
613                         for (var i in g.callnumbers) {
614                                 if ( ( libs.indexOf( g.callnumbers[i].owning_lib ) > -1 ) || ( libs.indexOf( String( g.callnumbers[i].owning_lib ) ) > -1 ) ) { /* already in list */ } else { libs.push( g.callnumbers[i].owning_lib ); }
615                         }
616                 }
617                 JSAN.use('util.fm_utils');
618                 var ancestor = util.fm_utils.find_common_aou_ancestor( libs );
619                 if (typeof ancestor == 'object' && ancestor != null) ancestor = ancestor.id();
620
621                 var ancestors = util.fm_utils.find_common_aou_ancestors( libs );
622
623                 if (ancestor) {
624                         return get(ancestor, ancestors);
625                 } else {
626                         return [];
627                 }
628
629         } catch(E) {
630                 g.error.standard_unexpected_error_alert('get_acpl_list',E);
631                 return [];
632         }
633 }
634
635
636 /******************************************************************************************************/
637 /* This keeps track of what fields have been edited for styling purposes */
638
639 g.changed = {};
640
641 /******************************************************************************************************/
642 /* These need data from the middle layer to render */
643
644 g.special_exception = {
645         'Owning Lib : Call Number' : function(label,value) {
646                 JSAN.use('util.widgets');
647                 if (value>0) { /* an existing call number */
648                         g.network.request(
649                                 api.FM_ACN_RETRIEVE.app,
650                                 api.FM_ACN_RETRIEVE.method,
651                                 [ value ],
652                                 function(req) {
653                                         var cn = '??? id = ' + value;
654                                         try {
655                                                 cn = req.getResultObject();
656                                         } catch(E) {
657                                                 g.error.sdump('D_ERROR','callnumber retrieve: ' + E);
658                                         }
659                                         util.widgets.set_text(label,g.data.hash.aou[ cn.owning_lib() ].shortname() + ' : ' + cn.label());
660                                 }
661                         );
662                 } else { /* a yet to be created call number */
663                         if (g.callnumbers) {
664                                 util.widgets.set_text(label,g.data.hash.aou[ g.callnumbers[value].owning_lib ].shortname() + ' : ' + g.callnumbers[value].label);
665                         }
666                 }
667         },
668         'Creator' : function(label,value) {
669                 if (value == null || value == '' || value == 'null') return;
670                 g.network.simple_request(
671                         'FM_AU_RETRIEVE_VIA_ID',
672                         [ ses(), value ],
673                         function(req) {
674                                 var p = '??? id = ' + value;
675                                 try {
676                                         p = req.getResultObject();
677                                         p = p.usrname();
678
679                                 } catch(E) {
680                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
681                                 }
682                                 JSAN.use('util.widgets');
683                                 util.widgets.set_text(label,p);
684                         }
685                 );
686         },
687         'Last Editor' : function(label,value) {
688                 if (value == null || value == '' || value == 'null') return;
689                 g.network.simple_request(
690                         'FM_AU_RETRIEVE_VIA_ID',
691                         [ ses(), value ],
692                         function(req) {
693                                 var p = '??? id = ' + value;
694                                 try {
695                                         p = req.getResultObject();
696                                         p = p.usrname();
697
698                                 } catch(E) {
699                                         g.error.sdump('D_ERROR','patron retrieve: ' + E);
700                                 }
701                                 util.widgets.set_text(label,p);
702                         }
703                 );
704         }
705
706 }
707
708 /******************************************************************************************************/
709 g.readonly_stat_cat_names = [];
710 g.editable_stat_cat_names = [];
711
712 /******************************************************************************************************/
713 /* These get show in the left panel */
714
715 function init_panes() {
716 g.panes_and_field_names = {
717
718         'left_pane' :
719 [
720         [
721                 "Barcode",               
722                 {
723                         render: 'fm.barcode();',
724                 }
725         ], 
726         [
727                 "Creation Date",
728                 { 
729                         render: 'util.date.formatted_date( fm.create_date(), "%F");',
730                 }
731         ],
732         [
733                 "Creator",
734                 { 
735                         render: 'fm.creator();',
736                 }
737         ],
738         [
739                 "Last Edit Date",
740                 { 
741                         render: 'util.date.formatted_date( fm.edit_date(), "%F");',
742                 }
743         ],
744         [
745                 "Last Editor",
746                 {
747                         render: 'fm.editor();',
748                 }
749         ],
750
751 ],
752
753 'right_pane' :
754 [
755         [
756                 "Shelving Location",
757                 { 
758                         render: 'typeof fm.location() == "object" ? fm.location().name() : g.data.lookup("acpl",fm.location()).name()', 
759                         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);',
760
761                 }
762         ],
763         [
764                 "Circulation Library",          
765                 {       
766                         render: 'typeof fm.circ_lib() == "object" ? fm.circ_lib().shortname() : g.data.hash.aou[ fm.circ_lib() ].shortname()',
767                         //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);',
768                         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);',
769                 } 
770         ],
771         [
772                 "Owning Lib : Call Number",     
773                 {
774                         render: 'fm.call_number();',
775                         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,
776                 }
777         ],
778         [
779                 "Copy Number",
780                 { 
781                         render: 'fm.copy_number() == null ? "<Unset>" : fm.copy_number()',
782                         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);',
783                 }
784         ],
785
786
787 ],
788
789 'right_pane2' :
790 [
791         [
792                 "Circulate?",
793                 {       
794                         render: 'fm.circulate() == null ? "<Unset>" : ( get_bool( fm.circulate() ) ? "Yes" : "No" )',
795                         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);',
796                 }
797         ],
798         [
799                 "Holdable?",
800                 { 
801                         render: 'fm.holdable() == null ? "<Unset>" : ( get_bool( fm.holdable() ) ? "Yes" : "No" )', 
802                         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);',
803                 }
804         ],
805         [
806                 "Age Protection",
807                 {
808                         render: 'fm.age_protect() == null ? "<Unset>" : ( typeof fm.age_protect() == "object" ? fm.age_protect().name() : g.data.hash.crahp[ fm.age_protect() ].name() )', 
809                         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);',
810                 }
811
812         ],
813         [
814                 "Loan Duration",
815                 { 
816                         render: 'switch(fm.loan_duration()){ case 1: case "1": "Short"; break; case 2: case "2": "Normal"; break; case 3:case "3": "Long"; break; }',
817                         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);',
818
819                 }
820         ],
821         [
822                 "Fine Level",
823                 {
824                         render: 'switch(fm.fine_level()){ case 1: case "1": "Low"; break; case 2: case "2": "Normal"; break; case 3: case "3": "High"; break; }',
825                         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);',
826                 }
827         ],
828
829          [
830                 "Circulate as Type",    
831                 {       
832                         render: 'fm.circ_as_type() == null ? "<Unset>" : g.data.hash.citm[ fm.circ_as_type() ].value()',
833                         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);',
834                 } 
835         ],
836         [
837                 "Circulation Modifier",
838                 {       
839                         render: 'fm.circ_modifier() == null ? "<Unset>" : fm.circ_modifier()',
840                         /*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);',*/
841                         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);',
842                 }
843         ],
844 ],
845
846 'right_pane3' :
847 [       [
848                 "Alert Message",
849                 {
850                         render: 'fm.alert_message() == null ? "<Unset>" : fm.alert_message()',
851                         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);',
852                 }
853         ],
854
855         [
856                 "Deposit?",
857                 { 
858                         render: 'fm.deposit() == null ? "<Unset>" : ( get_bool( fm.deposit() ) ? "Yes" : "No" )',
859                         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);',
860                 }
861         ],
862         [
863                 "Deposit Amount",
864                 { 
865                         render: 'if (fm.deposit_amount() == null) { "<Unset>"; } else { util.money.sanitize( fm.deposit_amount() ); }',
866                         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);',
867                 }
868         ],
869         [
870                 "Price",
871                 { 
872                         render: 'if (fm.price() == null) { "<Unset>"; } else { util.money.sanitize( fm.price() ); }', 
873                         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);',
874                 }
875         ],
876
877         [
878                 "OPAC Visible?",
879                 { 
880                         render: 'fm.opac_visible() == null ? "<Unset>" : ( get_bool( fm.opac_visible() ) ? "Yes" : "No" )', 
881                         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);',
882                 }
883         ],
884         [
885                 "Reference?",
886                 { 
887                         render: 'fm.ref() == null ? "<Unset>" : ( get_bool( fm.ref() ) ? "Yes" : "No" )', 
888                         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);',
889                 }
890         ],
891 ],
892
893 'right_pane4' : 
894 [
895 ]
896
897 };
898 }
899
900 /******************************************************************************************************/
901 /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
902
903 g.summarize = function( copies ) {
904         /******************************************************************************************************/
905         /* Setup */
906
907         JSAN.use('util.date'); JSAN.use('util.money');
908         g.summary = {};
909         g.field_names = [];
910         for (var i in g.panes_and_field_names) {
911                 g.field_names = g.field_names.concat( g.panes_and_field_names[i] );
912         }
913         g.field_names = g.field_names.concat( g.editable_stat_cat_names );
914         g.field_names = g.field_names.concat( g.readonly_stat_cat_names );
915
916         /******************************************************************************************************/
917         /* Loop through the field names */
918
919         for (var i = 0; i < g.field_names.length; i++) {
920
921                 var field_name = g.field_names[i][0];
922                 var render = g.field_names[i][1].render;
923                 g.summary[ field_name ] = {};
924
925                 /******************************************************************************************************/
926                 /* Loop through the copies */
927
928                 for (var j = 0; j < copies.length; j++) {
929
930                         var fm = copies[j];
931                         var cmd = render || ('fm.' + field_name + '();');
932                         var value = '???';
933
934                         /**********************************************************************************************/
935                         /* Try to retrieve the value for this field for this copy */
936
937                         try { 
938                                 value = eval( cmd ); 
939                         } catch(E) { 
940                                 g.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
941                         }
942                         if (typeof value == 'object' && value != null) {
943                                 alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
944                         }
945
946                         /**********************************************************************************************/
947                         /* Tally the count */
948
949                         if (g.summary[ field_name ][ value ]) {
950                                 g.summary[ field_name ][ value ]++;
951                         } else {
952                                 g.summary[ field_name ][ value ] = 1;
953                         }
954                 }
955         }
956         g.error.sdump('D_TRACE','summary = ' + js2JSON(g.summary) + '\n');
957 }
958
959 /******************************************************************************************************/
960 /* Display the summarized data and inputs for editing */
961
962 g.render = function() {
963
964         /******************************************************************************************************/
965         /* Library setup and clear any existing interface */
966
967         JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
968
969         for (var i in g.panes_and_field_names) {
970                 var p = document.getElementById(i);
971                 if (p) util.widgets.remove_children(p);
972         }
973
974         /******************************************************************************************************/
975         /* Prepare the panes */
976
977         var groupbox; var caption; var vbox; var grid; var rows;
978         
979         /******************************************************************************************************/
980         /* Loop through the field names */
981
982         for (h in g.panes_and_field_names) {
983                 if (!document.getElementById(h)) continue;
984                 for (var i = 0; i < g.panes_and_field_names[h].length; i++) {
985                         try {
986                                 var f = g.panes_and_field_names[h][i]; var fn = f[0];
987                                 groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
988                                 if (typeof g.changed[fn] != 'undefined') groupbox.setAttribute('class','copy_editor_field_changed');
989                                 caption = document.createElement('caption'); groupbox.appendChild(caption);
990                                 caption.setAttribute('label',fn); caption.setAttribute('id','caption_'+fn);
991                                 vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
992                                 grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
993                                 grid.setAttribute('flex','1');
994                                 rows = grid.lastChild;
995                                 var row;
996                                 
997                                 /**************************************************************************************/
998                                 /* Loop through each value for the field */
999
1000                                 for (var j in g.summary[fn]) {
1001                                         var value = j; var count = g.summary[fn][j];
1002                                         row = document.createElement('row'); rows.appendChild(row);
1003                                         var label1 = document.createElement('description'); row.appendChild(label1);
1004                                         if (g.special_exception[ fn ]) {
1005                                                 g.special_exception[ fn ]( label1, value );
1006                                         } else {
1007                                                 label1.appendChild( document.createTextNode(value) );
1008                                         }
1009                                         var label2 = document.createElement('description'); row.appendChild(label2);
1010                                         var unit = count == 1 ? 'copy' : 'copies';
1011                                         label2.appendChild( document.createTextNode(count + ' ' + unit) );
1012                                 }
1013                                 var hbox = document.createElement('hbox'); 
1014                                 hbox.setAttribute('id',fn);
1015                                 groupbox.appendChild(hbox);
1016                                 var hbox2 = document.createElement('hbox');
1017                                 groupbox.appendChild(hbox2);
1018
1019                                 /**************************************************************************************/
1020                                 /* Render the input widget */
1021
1022                                 if (f[1].input && g.edit) {
1023                                         g.render_input(hbox,f[1]);
1024                                 }
1025
1026                         } catch(E) {
1027                                 g.error.sdump('D_ERROR','copy editor: ' + E + '\n');
1028                         }
1029                 }
1030         }
1031 }
1032
1033 /******************************************************************************************************/
1034 /* This actually draws the change button and input widget for a given field */
1035 g.render_input = function(node,blob) {
1036         try {
1037                 // node = hbox ;    groupbox ->  hbox, hbox
1038
1039                 var groupbox = node.parentNode;
1040                 var caption = groupbox.firstChild;
1041                 var vbox = node.previousSibling;
1042                 var hbox = node;
1043                 var hbox2 = node.nextSibling;
1044
1045                 var input_cmd = blob.input;
1046                 var render_cmd = blob.render;
1047
1048                 var block = false; var first = true;
1049
1050                 function on_mouseover(ev) {
1051                         groupbox.setAttribute('style','background: white');
1052                 }
1053
1054                 function on_mouseout(ev) {
1055                         groupbox.setAttribute('style','');
1056                 }
1057
1058                 vbox.addEventListener('mouseover',on_mouseover,false);
1059                 vbox.addEventListener('mouseout',on_mouseout,false);
1060                 groupbox.addEventListener('mouseover',on_mouseover,false);
1061                 groupbox.addEventListener('mouseout',on_mouseout,false);
1062                 groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
1063                 groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
1064
1065                 function on_click(ev){
1066                         try {
1067                                 if (block) return; block = true;
1068
1069                                 function post_c(v) {
1070                                         try {
1071                                                 /* FIXME - kludgy */
1072                                                 var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
1073                                                 var f;
1074                                                 switch(t) {
1075                                                         case 'attribute' :
1076                                                                 f = input_cmd.match(/apply\("(.+?)",/)[1];
1077                                                         break;
1078                                                         case 'stat_cat' :
1079                                                                 f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
1080                                                         break;
1081                                                         case 'owning_lib' :
1082                                                                 f = null;
1083                                                         break;
1084                                                 }
1085                                                 g.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v };
1086                                                 block = false;
1087                                                 setTimeout(
1088                                                         function() {
1089                                                                 g.summarize( g.copies );
1090                                                                 g.render();
1091                                                                 document.getElementById(caption.id).focus();
1092                                                         }, 0
1093                                                 );
1094                                         } catch(E) {
1095                                                 g.error.standard_unexpected_error_alert('post_c',E);
1096                                         }
1097                                 }
1098                                 var x; var c; eval( input_cmd );
1099                                 if (x) {
1100                                         util.widgets.remove_children(vbox);
1101                                         util.widgets.remove_children(hbox);
1102                                         util.widgets.remove_children(hbox2);
1103                                         hbox.appendChild(x);
1104                                         var apply = document.createElement('button');
1105                                         apply.setAttribute('label','Apply');
1106                                         apply.setAttribute('accesskey','A');
1107                                         hbox2.appendChild(apply);
1108                                         apply.addEventListener('command',function() { c(x.value); },false);
1109                                         var cancel = document.createElement('button');
1110                                         cancel.setAttribute('label','Cancel');
1111                                         cancel.addEventListener('command',function() { setTimeout( function() { g.summarize( g.copies ); g.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
1112                                         hbox2.appendChild(cancel);
1113                                         setTimeout( function() { x.focus(); }, 0 );
1114                                 }
1115                         } catch(E) {
1116                                 g.error.standard_unexpected_error_alert('render_input',E);
1117                         }
1118                 }
1119                 vbox.addEventListener('click',on_click, false);
1120                 hbox.addEventListener('click',on_click, false);
1121                 caption.addEventListener('click',on_click, false);
1122                 caption.addEventListener('keypress',function(ev) {
1123                         if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
1124                 }, false);
1125                 caption.setAttribute('style','-moz-user-focus: normal');
1126                 caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
1127                 caption.setAttribute('onblur','this.setAttribute("class","")');
1128
1129         } catch(E) {
1130                 g.error.sdump('D_ERROR',E + '\n');
1131         }
1132 }
1133
1134 /******************************************************************************************************/
1135 /* store the copies in the global xpcom stash */
1136
1137 g.stash_and_close = function() {
1138         try {
1139                 if (g.handle_update) {
1140                         try {
1141                                 var r = g.network.request(
1142                                         api.FM_ACP_FLESHED_BATCH_UPDATE.app,
1143                                         api.FM_ACP_FLESHED_BATCH_UPDATE.method,
1144                                         [ ses(), g.copies, true ]
1145                                 );
1146                                 if (typeof r.ilsevent != 'undefined') {
1147                                         g.error.standard_unexpected_error_alert('copy update',r);
1148                                 } else {
1149                                         alert('Items added/modified.');
1150                                 }
1151                                 /* FIXME -- revisit the return value here */
1152                         } catch(E) {
1153                                 alert('copy update error: ' + js2JSON(E));
1154                         }
1155                 }
1156                 //g.data.temp_copies = js2JSON( g.copies );
1157                 //g.data.stash('temp_copies');
1158                 xulG.copies = g.copies;
1159                 update_modal_xulG(xulG);
1160                 window.close();
1161         } catch(E) {
1162                 g.error.standard_unexpected_error_alert('stash and close',E);
1163         }
1164 }
1165
1166 /******************************************************************************************************/
1167 /* spawn copy notes interface */
1168
1169 g.copy_notes = function() {
1170         JSAN.use('util.window'); var win = new util.window();
1171         win.open(
1172                 urls.XUL_COPY_NOTES, 
1173                 //+ '?copy_id=' + window.escape(g.copies[0].id()),
1174                 'Copy Notes','chrome,resizable,modal',
1175                 { 'copy_id' : g.copies[0].id() }
1176         );
1177 }
1178