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