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