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