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