]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/server/cat/util.js
LP#1497335 Item details shows aged circs (XUL edition)
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / cat / util.js
1 dump('entering cat/util.js\n');
2
3 function $(id) { return document.getElementById(id); }
4
5 if (typeof cat == 'undefined') var cat = {};
6 cat.util = {};
7
8 cat.util.EXPORT_OK    = [ 
9     'spawn_copy_editor', 'add_copies_to_bucket', 'show_in_opac', 'spawn_spine_editor', 'transfer_copies', 
10     'transfer_title_holds', 'mark_item_missing', 'mark_item_damaged', 'replace_barcode', 'fast_item_add', 
11     'make_bookable', 'edit_new_brsrc', 'edit_new_bresv', 'batch_edit_volumes', 'render_fine_level',
12     'render_loan_duration', 'mark_item_as_missing_pieces', 'render_callnumbers_for_bib_menu',
13     'render_cn_prefix_menuitems', 'render_cn_suffix_menuitems', 'render_cn_class_menu',
14     'render_cn_prefix_menu', 'render_cn_suffix_menu', 'transfer_specific_title_holds',
15     'request_items', 'mark_for_overlay', 'get_cbs_for_bre_id'
16 ];
17 cat.util.EXPORT_TAGS    = { ':all' : cat.util.EXPORT_OK };
18
19 cat.util.replace_barcode = function(old_bc) {
20     try {
21         JSAN.use('util.network');
22         var network = new util.network();
23
24         if (!old_bc) old_bc = window.prompt($("catStrings").getString('staff.cat.util.replace_barcode.old_bc_window_prompt.prompt'),
25             '',
26             $("catStrings").getString('staff.cat.util.replace_barcode.old_bc_window_prompt.title'));
27         if (!old_bc) return;
28
29         var copy;
30         try {
31             copy = network.simple_request('FM_ACP_RETRIEVE_VIA_BARCODE',[ old_bc ]);
32             if (typeof copy.ilsevent != 'undefined') throw(copy); 
33             if (!copy) throw(copy);
34         } catch(E) {
35             alert($("catStrings").getFormattedString('staff.cat.util.replace_barcode.error_alert', [old_bc]) + '\n');
36             return old_bc;
37         }
38     
39         // Why did I want to do this twice?  Because this copy is more fleshed?
40         try {
41             copy = network.simple_request('FM_ACP_RETRIEVE',[ copy.id() ]);
42             if (typeof copy.ilsevent != 'undefined') throw(copy);
43             if (!copy) throw(copy);
44         } catch(E) {
45             try {
46                 alert($("catStrings").getFormattedString('staff.cat.util.replace_barcode.error_alert', [old_bc]) +
47                      '\n' + (typeof E.ilsevent == 'undefined' ? '' : E.textcode + ' : ' + E.desc));
48             } catch(F) {
49                 alert(E + '\n' + F);
50             }
51             return old_bc;
52         }
53     
54         var new_bc = window.prompt($("catStrings").getString('staff.cat.util.replace_barcode.new_bc_window_prompt.prompt'),
55             '',
56             $("catStrings").getString('staff.cat.util.replace_barcode.new_bc_window_prompt.title'));
57         new_bc = String( new_bc ).replace(/\s/g,'');
58         /* Casting a possibly null input value to a String turns it into "null" */
59         if (!new_bc || new_bc == 'null') {
60             alert($("catStrings").getString('staff.cat.util.replace_barcode.new_bc.failed'));
61             return old_bc;
62         }
63     
64         var test = network.simple_request('FM_ACP_RETRIEVE_VIA_BARCODE',[ new_bc ]);
65         if (typeof test.ilsevent == 'undefined') {
66             alert('Rename aborted.  Another copy has barcode "' + new_bc + '".');
67             return old_bc;
68         } else {
69             if (test.ilsevent != 1502 /* ASSET_COPY_NOT_FOUND */) {
70                 obj.error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.util.replace_barcode.testing_error', [new_bc]), test);
71                 return old_bc;
72             }    
73         }
74
75         copy.barcode(new_bc); copy.ischanged('1');
76         var r = network.simple_request('FM_ACP_FLESHED_BATCH_UPDATE', [ ses(), [ copy ] ]);
77         if (typeof r.ilsevent != 'undefined') { 
78             if (r.ilsevent != 0) {
79                 if (r.ilsevent == 5000 /* PERM_FAILURE */) {
80                     alert($("catStrings").getString('staff.cat.util.replace_barcode.insufficient_permission_for_rename'));
81                     return old_bc;
82                 } else {
83                     obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.util.replace_barcode.item_rename_error'),r);
84                     return old_bc;
85                 }
86             }
87         }
88
89         return new_bc;
90     } catch(E) {
91         JSAN.use('util.error'); var error = new util.error();
92         error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.util.replace_barcode.rename_error'),E);
93         return old_bc;
94     }
95 }
96
97 cat.util.transfer_title_holds = function(old_targets) {
98     JSAN.use('OpenILS.data'); var data = new OpenILS.data();
99     JSAN.use('util.network'); var network = new util.network();
100     try {
101         data.stash_retrieve();
102         var target = data.marked_record_for_hold_transfer;
103         if (!target) {
104             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.destination_needed.label');
105             alert(m);
106             return;
107         }
108         var robj = network.simple_request('TRANSFER_TITLE_HOLDS',[ ses(), target, old_targets ]);
109         if (robj == 1) {
110             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.success.label');
111             alert(m);
112         } else {
113             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.failure.label');
114             alert(m);
115         }
116     } catch(E) {
117         alert('Error in cat.util.transfer_title.holds(): ' + E);
118     }
119 }
120
121 cat.util.transfer_specific_title_holds = function(hold_ids,unique_targets) {
122     JSAN.use('OpenILS.data'); var data = new OpenILS.data();
123     JSAN.use('util.network'); var network = new util.network();
124     try {
125         data.stash_retrieve();
126         var target = data.marked_record_for_hold_transfer;
127         if (!target) {
128             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.destination_needed.label');
129             alert(m);
130             return;
131         }
132         if (unique_targets.length > 1) {
133             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.many_bibs.warning');
134             if (! window.confirm(m)) {
135                 return;
136             }
137         }
138         var robj = network.simple_request('TRANSFER_SPECIFIC_TITLE_HOLDS',[ ses(), target, hold_ids ]);
139         if (robj == 1) {
140             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.success.label');
141             alert(m);
142         } else {
143             var m = $("catStrings").getString('staff.cat.opac.title_for_hold_transfer.failure.label');
144             alert(m);
145         }
146     } catch(E) {
147         alert('Error in cat.util.transfer_title.holds(): ' + E);
148     }
149 }
150
151
152 cat.util.transfer_copies = function(params) {
153     JSAN.use('util.error'); var error = new util.error();
154     JSAN.use('OpenILS.data'); var data = new OpenILS.data();
155     JSAN.use('util.network'); var network = new util.network();
156     try {
157         data.stash_retrieve();
158         if (!data.marked_volume) {
159             alert($("catStrings").getString('staff.cat.util.transfer_copies.unmarked_volume_alert'));
160             return;
161         }
162         var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: auto">';
163         if (!params.message) {
164             params.message = $("catStrings").getFormattedString('staff.cat.util.transfer_copies.params_message', [data.hash.aou[ params.owning_lib ].shortname(), params.volume_label]);
165             //params.message = 'Transfer items from their original volumes to ';
166             //params.message += data.hash.aou[ params.owning_lib ].shortname() + "'s volume labelled ";
167             //params.message += '"' + params.volume_label + '" on the following record (and change their circ libs to match)?';
168         }
169
170         xml += '<description>' + params.message.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</description>';
171         xml += '<hbox><button label="' + $("catStrings").getString('staff.cat.util.transfer_copies.transfer.label')+ '" name="fancy_submit"/>';
172         xml += '<button label="' + $("catStrings").getString('staff.cat.util.transfer_copies.cancel.label');
173         xml += '" accesskey="'+ $("catStrings").getString('staff.cat.util.transfer_copies.cancel.accesskey') +'" name="fancy_cancel"/></hbox>';
174         xml += '<iframe style="overflow: scroll" flex="1" src="' + urls.XUL_BIB_BRIEF + '?docid=' + params.docid + '" oils_force_external="true"/>';
175         xml += '</vbox>';
176         //data.temp_transfer = xml; data.stash('temp_transfer');
177         JSAN.use('util.window'); var win = new util.window();
178         var fancy_prompt_data = win.open(
179             urls.XUL_FANCY_PROMPT,
180             'fancy_prompt', 'chrome,resizable,modal,width=500,height=300',
181             { 'xml' : xml, 'title' : $("catStrings").getString('staff.cat.util.transfer_copies.window_title') }
182         );
183         if (fancy_prompt_data.fancy_status == 'incomplete') { alert($("catStrings").getString('staff.cat.util.transfer_copies.aborted_transfer')); return; }
184
185         JSAN.use('util.functional');
186
187         var copies = network.simple_request('FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative', [ params.copy_ids ]);
188
189         for (var i = 0; i < copies.length; i++) {
190             copies[i].call_number( data.marked_volume );
191             copies[i].circ_lib( params.owning_lib );
192             copies[i].ischanged( 1 );
193         }
194
195         var robj = network.simple_request(
196             'FM_ACP_FLESHED_BATCH_UPDATE', 
197             [ ses(), copies, true ], 
198             null,
199             {
200                 'title' : $("catStrings").getString('staff.cat.util.transfer_copies.override_transfer_failure'),
201                 'overridable_events' : [
202                     1208 /* TITLE_LAST_COPY */,
203                     1227 /* COPY_DELETE_WARNING */,
204                 ]
205             }
206         );
207         
208         if (typeof robj.ilsevent != 'undefined') {
209             if (
210                 (robj.ilsevent != 0)
211                 && (robj.ilsevent != 1227 /* COPY_DELETE_WARNING */)
212                 && (robj.ilsevent != 1208 /* TITLE_LAST_COPY */)
213                 && (robj.ilsevent != 5000 /* PERM_DENIED */)
214             ) {
215                 throw(robj);
216             }
217         } else {
218             alert($("catStrings").getString('staff.cat.util.transfer_copies.successful_transfer'));
219         }
220
221     } catch(E) {
222         error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.util.transfer_copies.transfer_error'),E);
223     }
224 }
225
226 cat.util.spawn_spine_editor = function(selection_list) {
227     JSAN.use('util.error'); var error = new util.error();
228     try {
229         JSAN.use('util.functional');
230         xulG.new_tab(
231             xulG.url_prefix('XUL_SPINE_LABEL'),
232             { 'tab_name' : $("catStrings").getString('staff.cat.util.spine_editor.tab_name') },
233             {
234                 'barcodes' : util.functional.map_list( selection_list, function(o){return o.barcode;}) 
235             }
236         );
237     } catch(E) {
238         error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.util.spine_editor.spine_editor_error'),E);
239     }
240 }
241
242 cat.util.show_in_opac = function(selection_list, params) {
243     JSAN.use('util.error'); var error = new util.error();
244     JSAN.use('util.network'); var network = new util.network();
245     var doc_id; var seen = {};
246     if(!params) params = {};
247     try {
248         for (var i = 0; i < selection_list.length; i++) {
249             doc_id = selection_list[i].doc_id;
250             if (!doc_id) {
251                 var barcode = selection_list[i].barcode;
252                 doc_id = network.simple_request('FM_BRE_ID_VIA_BARCODE',[barcode]);
253                 if (typeof doc_id.ilsevent != 'undefined' || doc_id == -1) {
254                     alert($("catStrings").getFormattedString('staff.cat.util.show_in_opac.unknown_barcode', [barcode]));
255                     continue;
256                 }
257             }
258             if (doc_id == -1 ) {
259                 continue; /* pre-cat */
260             }
261             if (typeof seen[doc_id] != 'undefined') {
262                 continue;
263             }
264             seen[doc_id] = true;
265             var opac_url = xulG.url_prefix('opac_rdetail') + doc_id;
266             var content_params = {};
267             for (var value in params) content_params[value] = params[value];
268             content_params['session'] = ses();
269             content_params['authtime'] = ses('authtime');
270             content_params['opac_url'] = opac_url;
271             xulG.new_tab(
272                 xulG.url_prefix('XUL_OPAC_WRAPPER'), 
273                 {'tab_name':$('catStrings').getString('staff.cat.util.show_in_opac.retrieving_title')}, 
274                 content_params
275             );
276         }
277     } catch(E) {
278         error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.util.show_in_opac.catalog_error_for_doc_id', [doc_id]),E);
279     }
280 }
281
282 cat.util.add_copies_to_bucket = function(selection_list) {
283     JSAN.use('util.functional');
284     JSAN.use('util.window'); var win = new util.window();
285     JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
286     data.cb_temp_copy_ids = js2JSON(
287         util.functional.map_list(
288             selection_list,
289             function (o) {
290                 if (typeof o.copy_id != 'undefined' && o.copy_id != null) {
291                     return o.copy_id;
292                 } else {
293                     return o;
294                 }
295             }
296         )
297     );
298     data.stash('cb_temp_copy_ids');
299     win.open( 
300         xulG.url_prefix('XUL_COPY_BUCKETS_QUICK'),
301         '_blank',
302         'chrome,resizable,center'
303     );
304 }
305
306 cat.util.add_titles_to_bucket = function(record_ids) {
307     JSAN.use('util.window'); var win = new util.window();
308     JSAN.use('util.functional');
309     var filtered_record_ids = util.functional.filter_list(
310         record_ids,
311         function(o) {
312             return o != -1; // don't allow the magic pre-cat bib
313         }
314     );
315     if (filtered_record_ids.length != record_ids.length) {
316         alert($("catStrings").getFormattedString(
317             'staff.cat.util.add_titles_to_bucket.number_of_precats_skipped',
318             [ record_ids.length - filtered_record_ids.length ]
319         ));
320     }
321     if (filtered_record_ids.length > 0) {
322         win.open(
323             xulG.url_prefix('XUL_RECORD_BUCKETS_QUICK'),
324             '_blank',
325             'chrome,resizable,modal,center',
326             {
327                 record_ids: filtered_record_ids
328             }
329         );
330     }
331 }
332
333 cat.util.spawn_copy_editor = function(params) {
334     try {
335         if (!params.copy_ids && !params.copies) return;
336         if (params.copy_ids && params.copy_ids.length == 0) return;
337         if (params.copies && params.copies.length == 0) return;
338         if (params.copy_ids) params.copy_ids = js2JSON(params.copy_ids); // legacy
339         if (!params.caller_handles_update) params.handle_update = 1; // legacy
340
341         var obj = {};
342         JSAN.use('util.network'); obj.network = new util.network();
343         JSAN.use('util.error'); obj.error = new util.error();
344     
345         var title = '';
346         if (params.copy_ids && params.copy_ids.length > 1 && params.edit == 1)
347             title = $("catStrings").getString('staff.cat.util.copy_editor.batch_edit');
348         else if(params.copies && params.copies.length > 1 && params.edit == 1)
349             title = $("catStrings").getString('staff.cat.util.copy_editor.batch_view');
350         else if(params.copy_ids && params.copy_ids.length == 1)
351             title = $("catStrings").getString('staff.cat.util.copy_editor.edit');
352         else
353             title = $("catStrings").getString('staff.cat.util.copy_editor.view');
354
355         JSAN.use('util.window'); var win = new util.window();
356         var my_xulG = win.open(
357             (urls.XUL_COPY_EDITOR),
358             title,
359             'chrome,modal,resizable',
360             params
361         );
362         if (!my_xulG.copies && params.edit) {
363         } else {
364             return my_xulG.copies;
365         }
366         return [];
367     } catch(E) {
368         JSAN.use('util.error'); var error = new util.error();
369         error.standard_unexpected_error_alert('error in cat.util.spawn_copy_editor',E);
370     }
371 }
372
373 cat.util.mark_item_damaged = function(copy_ids) {
374     var error;
375     try {
376         JSAN.use('util.error'); error = new util.error();
377         JSAN.use('util.functional'); JSAN.use('util.date');
378         JSAN.use('util.network'); var network = new util.network();
379         if (!copy_ids) { return; }
380         copy_ids = util.functional.filter_list( copy_ids, function(o) { return o != null; } );
381         if (copy_ids.length < 1) { return; }
382         var copies = network.simple_request('FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative', [ copy_ids ]);
383         if (typeof copies.ilsevent != 'undefined') throw(copies);
384         var magic_status = false;
385         for (var i = 0; i < copies.length; i++) {
386             var status = copies[i].status(); if (typeof status == 'object') status = status.id();
387             if (typeof my_constants.magical_statuses[ status ] != 'undefined') 
388                 if (my_constants.magical_statuses[ status ].block_mark_item_damaged) magic_status = true;
389         }
390         if (magic_status) {
391         
392             error.yns_alert($("catStrings").getString('staff.cat.util.mark_item_damaged.af_message'),
393                 $("catStrings").getString('staff.cat.util.mark_item_damaged.af_title'),
394                 $("catStrings").getString('staff.cat.util.mark_item_damaged.af_ok_label'), null, null,
395                 $("catStrings").getString('staff.cat.util.mark_item_damaged.af_confirm_action'));
396
397         } else {
398
399             var r = error.yns_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.md_message', [util.functional.map_list( copies, function(o) { return o.barcode(); } ).join(", ")]),
400                 $("catStrings").getString('staff.cat.util.mark_item_damaged.md_title'),
401                 $("catStrings").getString('staff.cat.util.mark_item_damaged.md_ok_label'),
402                 $("catStrings").getString('staff.cat.util.mark_item_damaged.md_cancel_label'), null,
403                 $("catStrings").getString('staff.cat.util.mark_item_damaged.md_confirm_action'));
404
405             if (r == 0) {
406                 var count = 0;
407                 for (var i = 0; i < copies.length; i++) {
408                     try {
409
410                         var my_circ = network.simple_request('FM_CIRC_RETRIEVE_VIA_COPY',[ses(),copies[i].id(),1]);
411                         if (typeof my_circ.ilsevent == 'undefined') { 
412                             JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
413                             my_circ = my_circ[0];
414                             if (typeof my_circ != 'undefined') {
415                                 if (! my_circ.checkin_time() ) {
416                                     var due_date = my_circ.due_date() ? util.date.formatted_date( my_circ.due_date(), '%F' ) : null;
417                                     var auto_checkin = String( data.hash.aous['circ.auto_checkin_on_mark_damage'] ) == 'true';
418                                     /* short-circuit this behavior.  We don't want to mark an item damaged and still have it circulating.  At least for now.  Wait until someone asks for it. */
419                                     auto_checkin = true; 
420                                     JSAN.use('patron.util');
421
422                                     if (!my_circ.usr()) {
423                                         // Since we are looking at copies that are still checked out,
424                                         // we should never get here.  Best to be safe, though.
425                                         alert(
426                                             document.getElementById('catStrings')
427                                             .getFormattedString(
428                                                 'staff.cat.util.mark_item_damaged.item_no_linked_patron',
429                                                 copies[i].barcode()
430                                             )
431                                         );
432                                         continue;
433                                     }
434
435                                     var patron_obj = patron.util.retrieve_fleshed_au_via_id( ses(), my_circ.usr() );
436                                     var patron_name = patron.util.format_name( patron_obj ) + ' : ' + patron_obj.card().barcode();
437                                     var msg = $("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.item_circulating_to_patron', [ 
438                                         copies[i].barcode(),
439                                         patron_name,
440                                         util.date.formatted_date( my_circ.due_date(), '%{localized}' )]);
441                                     JSAN.use('util.date'); var today = util.date.formatted_date(new Date(),'%F');
442                                     var r2 = auto_checkin ? 1 : error.yns_alert(
443                                         msg,
444                                         document.getElementById('catStrings').getString('staff.cat.util.mark_item_damaged.checkin.title'),
445                                         document.getElementById('catStrings').getString('staff.cat.util.mark_item_damaged.checkin.no_checkin'),
446                                         document.getElementById('catStrings').getString('staff.cat.util.mark_item_damaged.checkin.normal_checkin'),
447                                         due_date ? (today > due_date ? document.getElementById('catStrings').getString('staff.cat.util.mark_item_damaged.checkin.forgiving_checkin') : null) : null,
448                                         document.getElementById('catStrings').getString('staff.cat.util.mark_item_damaged.checkin.confirm_action')
449                                     );
450                                     JSAN.use('circ.util');
451                                     switch(r2) {
452                                         case 1:
453                                             circ.util.checkin_via_barcode( ses(), { 'barcode' : copies[i].barcode(), 'noop' : 1 } );
454                                         break;
455                                         case 2:
456                                             circ.util.checkin_via_barcode( ses(), { 'barcode' : copies[i].barcode(), 'noop' : 1 }, due_date );
457                                         break;
458                                     }
459                                 }
460                             }
461                         }
462
463                         var robj = network.simple_request('MARK_ITEM_DAMAGED',[ses(),copies[i].id()]);
464                         if (typeof robj.ilsevent != 'undefined') {
465                             switch(robj.textcode) {
466                                 case 'DAMAGE_CHARGE' :
467                                     var params = {};
468                                     JSAN.use('util.money');
469                                     var circ_obj = robj.payload.circ;
470                                     var patron_obj = circ_obj.usr();
471                                     JSAN.use('patron.util'); 
472                                     var patron_name = patron.util.format_name( patron_obj ) + ' : ' + patron_obj.card().barcode(); 
473                                     var r1 = error.yns_alert( 
474                                         $("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.charge_patron_prompt.message', [  
475                                             copies[i].barcode(),  
476                                             patron_name,  
477                                             util.date.formatted_date( circ_obj.checkin_time(), '%{localized}' ),
478                                             util.money.sanitize(robj.payload.charge) ]), 
479                                         $("catStrings").getString('staff.cat.util.mark_item_damaged.charge_patron_prompt.title'), 
480                                         $("catStrings").getString('staff.cat.util.mark_item_damaged.charge_patron_prompt.ok_label'), 
481                                         $("catStrings").getString('staff.cat.util.mark_item_damaged.charge_patron_prompt.change_amount_label'), 
482                                         $("catStrings").getString('staff.cat.util.mark_item_damaged.charge_patron_prompt.cancel_label'), 
483                                         $("catStrings").getString('staff.cat.util.mark_item_damaged.charge_patron_prompt.confirm_action')); 
484                                     if (r1 == 0) {
485                                         params.apply_fines = 'apply';
486                                     } else if (r1 == 1) { 
487                                         JSAN.use('util.window'); var win = new util.window();
488                                         var my_xulG = win.open(
489                                             urls.XUL_PATRON_BILL_WIZARD,
490                                             'billwizard',
491                                             'chrome,resizable,modal',
492                                             { 
493                                                 'patron' : patron_obj, 
494                                                 'patron_id' : patron_obj.id(), 
495                                                 'circ' : circ_obj, 
496                                                 'xact_id' : circ_obj.id(), 
497                                                 'do_not_process_bill' : true,
498                                                 /* 'override_default_billing_type' : 7, FIXME: maybe reintroduce this with an org setting for the specific btype? */
499                                                 'override_default_price' : util.money.sanitize( robj.payload.charge ) 
500                                             }
501                                         );
502
503                                         params.apply_fines = my_xulG.proceed ? 'apply' : 'noapply';
504                                         if (my_xulG.proceed) {
505                                             params.override_amount = my_xulG.amount;
506                                             params.override_btype = my_xulG.cbt_id;
507                                             params.override_note = my_xulG.note;
508                                         }
509                                     } else {
510                                         params.apply_fines = 'noapply';
511                                     }
512                                     robj = network.simple_request('MARK_ITEM_DAMAGED',[ ses(), copies[i].id(), params ]);
513                                     if (typeof robj.ilsevent != 'undefined') { throw(robj); }
514                                     break;
515                                 default: throw(robj);
516                             }
517                         }
518                         count++;
519                     } catch(E) {
520                         error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.marking_error', [copies[i].barcode()]),E);
521                     }
522                 }
523                 alert(count == 1 ? $("catStrings").getString('staff.cat.util.mark_item_damaged.one_item_damaged') :
524                     $("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.multiple_item_damaged', [count]));
525             }
526         }
527
528     } catch(E) {
529         if (error) error.standard_unexpected_error_alert('cat.util.mark_item_damaged',E); else alert('FIXME: ' + E);
530     }
531 }
532
533 cat.util.mark_item_missing = function(copy_ids) {
534     var error;
535     try {
536         JSAN.use('util.error'); error = new util.error();
537         JSAN.use('util.functional');
538         JSAN.use('util.network'); var network = new util.network();
539         if (!copy_ids) { return; }
540         copy_ids = util.functional.filter_list( copy_ids, function(o) { return o != null; } );
541         if (copy_ids.length < 1) { return; }
542         var copies = network.simple_request('FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative', [ copy_ids ]);
543         if (typeof copies.ilsevent != 'undefined') throw(copies);
544         var magic_status = false;
545         for (var i = 0; i < copies.length; i++) {
546             var status = copies[i].status(); if (typeof status == 'object') status = status.id();
547             if (typeof my_constants.magical_statuses[ status ] != 'undefined') 
548                 if (my_constants.magical_statuses[ status ].block_mark_item_action) magic_status = true;
549         }
550         if (magic_status) {
551         
552             error.yns_alert($("catStrings").getString('staff.cat.util.mark_item_missing.af_message'),
553                 $("catStrings").getString('staff.cat.util.mark_item_missing.af_title'),
554                 $("catStrings").getString('staff.cat.util.mark_item_missing.af_ok_label'), null, null,
555                 $("catStrings").getString('staff.cat.util.mark_item_missing.af_confirm_action'));
556
557         } else {
558
559             var r = error.yns_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_missing.ms_message', [util.functional.map_list( copies, function(o) { return o.barcode(); } ).join(", ")]),
560                 $("catStrings").getString('staff.cat.util.mark_item_missing.ms_title'),
561                 $("catStrings").getString('staff.cat.util.mark_item_missing.ms_ok_label'),
562                 $("catStrings").getString('staff.cat.util.mark_item_missing.ms_cancel_label'), null,
563                 $("catStrings").getString('staff.cat.util.mark_item_missing.ms_confirm_action'));
564
565             if (r == 0) {
566                 var count = 0;
567                 for (var i = 0; i < copies.length; i++) {
568                     try {
569                         var robj = network.simple_request('MARK_ITEM_MISSING',[ses(),copies[i].id()]);
570                         if (typeof robj.ilsevent != 'undefined') throw(robj);
571                         count++;
572                     } catch(E) {
573                         error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_missing.marking_error', [copies[i].barcode()]),E);
574                     }
575                 }
576                 alert(count == 1 ? $("catStrings").getString('staff.cat.util.mark_item_missing.one_item_missing') :
577                     $("catStrings").getFormattedString('staff.cat.util.mark_item_missing.multiple_item_missing', [count]));
578             }
579         }
580
581     } catch(E) {
582         if (error) error.standard_unexpected_error_alert('cat.util.mark_item_missing',E); else alert('FIXME: ' + E);
583     }
584 }
585
586 cat.util.fast_item_add = function(doc_id,cn_label,cp_barcode) {
587     var error;
588     JSAN.use('OpenILS.data'); var data = new OpenILS.data();
589     try {
590
591         JSAN.use('util.error'); error = new util.error();
592         JSAN.use('util.network'); var network = new util.network();
593
594         var acn_blob = network.simple_request(
595             'FM_ACN_FIND_OR_CREATE',
596             [ ses(), cn_label, doc_id, ses('ws_ou') ]
597         );
598
599         if (typeof acn_blob.ilsevent != 'undefined') {
600             error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.volume_copy_creator.stash_and_close.problem_with_volume', [cn]), acn_blob);
601             return;
602         }
603
604         // Get the default copy status; default to available if unset, per 1.6
605         var fast_ccs = data.hash.aous['cat.default_copy_status_fast'] || 0;
606
607         var copy_obj = new acp();
608         copy_obj.id( -1 );
609         copy_obj.isnew('1');
610         copy_obj.barcode( cp_barcode );
611         copy_obj.call_number( acn_blob.acn_id );
612         copy_obj.circ_lib( ses('ws_ou') );
613         /* FIXME -- use constants */
614         copy_obj.deposit(0);
615         copy_obj.price(0);
616         copy_obj.deposit_amount(0);
617         copy_obj.fine_level(2); // Normal
618         copy_obj.loan_duration(2); // Normal
619         copy_obj.location(1); // Stacks
620         copy_obj.status(fast_ccs);
621         copy_obj.circulate(get_db_true());
622         copy_obj.holdable(get_db_true());
623         copy_obj.opac_visible(get_db_true());
624         copy_obj.ref(get_db_false());
625         copy_obj.mint_condition(get_db_true());
626
627         JSAN.use('util.window'); var win = new util.window();
628         JSAN.use('cat.util');
629
630         var unified_interface = String( data.hash.aous['ui.unified_volume_copy_editor'] ) == 'true';
631         if (unified_interface) {
632             var horizontal_interface = String( data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true';
633             var url = window.xulG.url_prefix( horizontal_interface ? 'XUL_VOLUME_COPY_CREATOR_HORIZONTAL' : 'XUL_VOLUME_COPY_CREATOR' );
634             var w = xulG.set_tab(
635                 url,
636                 {
637                     'tab_name' : document.getElementById('offlineStrings').getFormattedString(
638                         'cat.bib_record',
639                         [ doc_id ]
640                     )
641                 },
642                 {
643                     'doc_id' : doc_id, 
644                     'existing_copies' : [ copy_obj ],
645                     'load_opac_when_done' : true,
646                     'labels_in_new_tab' : true
647                 }
648             );
649
650         } else {
651             return cat.util.spawn_copy_editor( { 'handle_update' : 1, 'edit' : 1, 'docid' : doc_id, 'copies' : [ copy_obj ] });
652         }
653
654     } catch(E) {
655         if (error) error.standard_unexpected_error_alert('cat.util.fast_item_add',E); else alert('FIXME: ' + E);
656     }
657 }
658
659 cat.util.make_bookable = function(copy_ids) {
660     if (!copy_ids) { return; }
661     copy_ids = util.functional.filter_list( copy_ids, function(o) { return o != null; } );
662     if (copy_ids.length < 1) { return; }
663     var results = fieldmapper.standardRequest(
664         ["open-ils.booking", "open-ils.booking.resources.create_from_copies"],
665         [ses(), copy_ids]
666     );
667     if (results == null) {
668         alert(document.getElementById("catStrings").getString(
669             "staff.cat.copy_browser.make_bookable.create_failed_silent"
670         ));
671     }
672     else if (typeof results.ilsevent != "undefined") {
673         alert(document.getElementById("catStrings").getFormattedString(
674             "staff.cat.copy_browser.make_bookable.create_failed",
675             [results.ilsevent, results.textcode, results.desc, results.debug]
676         ));
677     }
678     return results;
679 }
680
681 cat.util.edit_new_brsrc = function(brsrc_list) {
682     /* Spawn new tab to allow editing new resources. */
683     try {
684         xulG.resultant_brsrc = brsrc_list.map(function(o) { return o[0]; });
685         xulG.new_tab(
686             urls.XUL_BROWSER + "?url=" + window.encodeURIComponent(
687                 xulG.url_prefix("BOOKING_RESOURCE")
688             ), {
689                 "tab_name": offlineStrings.getString(
690                     "menu.cmd_booking_resource.tab"
691                  ),
692                 "browser" : true
693             }, {
694                 "no_xulG": false,
695                 "show_print_button": false,
696                 "show_nav_buttons": true,
697                 "passthru_content_params": xulG
698             }
699         );
700     } catch(E) {
701         alert(
702             document.getElementById("catStrings").getFormattedString(
703                 "staff.cat.copy_browser.make_bookable.newtab_failed"
704             ), E
705         );
706     }
707 }
708
709 cat.util.edit_new_bresv = function(booking_results) {
710     /* Spawn new tab to allow editing new reservations. */
711     try {
712         if (xulG.auth == undefined) {
713             xulG.auth = {"session": {"key": ses()}};
714         }
715         xulG.bresv_interface_opts = {"booking_results": booking_results};
716         xulG.new_tab(
717             xulG.url_prefix("/eg/booking/reservation"),
718             {
719                 "tab_name": offlineStrings.getString(
720                     "menu.cmd_booking_reservation.tab"
721                  ),
722                 "browser" : false
723             }, xulG
724         );
725     } catch(E) {
726         alert(
727             document.getElementById("catStrings").getString(
728                 "staff.cat.copy_browser.make_bookable.newtab_failed"
729             ) + E
730         );
731     }
732 }
733
734 cat.util.batch_edit_volumes = function(fleshed_volumes) {
735     try {
736         if (!fleshed_volumes || fleshed_volumes.length < 1) { return false; }
737
738         JSAN.use('util.functional');
739         JSAN.use('util.network'); var net = new util.network();
740         JSAN.use('util.window'); var win = new util.window();
741
742         var can_edit = net.simple_request(
743             'PERM_MULTI_ORG_CHECK',
744             [
745                 ses(),
746                 ses('staff_id'),
747                 util.functional.map_list(
748                     fleshed_volumes,
749                     function(v) {
750                         return v.owning_lib();
751                     }
752                 ),
753                 ['UPDATE_VOLUME']
754             ]
755         );
756         if (!can_edit) {
757             alert(document.getElementById('catStrings').getString('staff.cat.edit_volume.permission_error'));
758             return false;
759         }
760         var title;
761         if (fleshed_volumes.length == 1) {
762             title = document.getElementById('catStrings').getString('staff.cat.edit_volume.title');
763         } else {
764             title = document.getElementById('catStrings').getString('staff.cat.edit_volume.title.plural');
765         }
766
767         function clone_list(o) {
768             var list = JSON2js( js2JSON( o ) );
769             // now that it is safe to clear copies, let's do so, otherwise may get an error from volume edit method
770             for (var i = 0; i < list.length; i++) { list[i].copies( [] ); } 
771             return list;
772         }
773
774         var my_xulG = win.open(
775             xulG.url_prefix('XUL_VOLUME_EDITOR'),
776             title,
777             'chrome,modal,resizable',
778             { 'volumes' : clone_list( fleshed_volumes ) }
779         );
780
781         if (typeof my_xulG.update_these_volumes == 'undefined') { return false; }
782
783         var volumes = util.functional.filter_list(
784             my_xulG.volumes,
785             function(v) {
786                 return get_bool( v.ischanged() );
787             }
788         );
789
790         if (volumes.length < 1) { return false; }
791
792         volumes = util.functional.map_list( volumes, function(o){
793             if (typeof o.suffix() == 'object') { o.suffix( o.suffix().id() ); }
794             if (typeof o.prefix() == 'object') { o.prefix( o.prefix().id() ); }
795             if (typeof o.label_class() == 'object') { o.label_class( o.label_class().id() ); }
796             return o;
797         });
798
799         var r = net.simple_request(
800             'FM_ACN_TREE_UPDATE',
801             [ ses(), volumes, false, { 'auto_merge_vols' : my_xulG.auto_merge } ],
802             null,
803             {
804                 'title' : document.getElementById('catStrings').getString('staff.cat.edit_volumes.override.confirm'),
805                 'overridable_events' : [
806                     1705 /* VOLUME_LABEL_EXISTS */
807                 ],
808                 'text' : {
809                     '1705' : function(r) {
810                         var payload_acn = util.functional.find_id_object_in_list( volumes, r.payload );
811                         return document.getElementById('catStrings').getFormattedString('staff.cat.edit_volumes.label_exists.details',[payload_acn.label()]);
812                     }
813                 }
814             }
815         );
816         if (!r) { throw('Update method returned null or false.'); }
817         if (typeof r.ilsevent != 'undefined') {
818             if (r.ilsevent == 1705 /* VOLUME_LABEL_EXISTS */) {
819                /* not overridden, but otherwise handled, so ignore */
820                 return false;
821             } else {
822                 throw(r);
823             }
824         }
825
826         return true;
827
828     } catch(E) {
829         alert('Error in cat.util.batch_edit_volumes: ' + E);
830         return false;
831     }
832 }
833
834 cat.util.render_fine_level = function(value) {
835     var text;
836     switch(Number(value)){
837         case 1: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.fine_level.low"); break;
838         case 2: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.fine_level.normal"); break;
839         case 3: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.fine_level.high"); break; 
840     }
841     return text;
842 }
843 cat.util.render_loan_duration = function(value) {
844     var text;
845     switch(Number(value)){
846         case 1: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.loan_duration.short"); break;
847         case 2: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.loan_duration.normal"); break;
848         case 3: text = document.getElementById("catStrings").getString("staff.cat.copy_editor.field.loan_duration.extended"); break;
849     }
850     return text;
851 }
852
853 cat.util.mark_item_as_missing_pieces = function(copy_ids) {
854     var error;
855     try {
856         JSAN.use('util.error'); error = new util.error();
857         JSAN.use('util.functional'); JSAN.use('util.date');
858         JSAN.use('util.network'); var network = new util.network();
859         JSAN.use('util.print'); var print = new util.print();
860         JSAN.use('util.window'); var win = new util.window();
861         if (!copy_ids) { return; }
862         copy_ids = util.functional.filter_list( copy_ids, function(o) { return o != null; } );
863         if (copy_ids.length < 1) { return; }
864         var copies = network.simple_request('FM_ACP_FLESHED_BATCH_RETRIEVE.authoritative', [ copy_ids ]);
865         if (typeof copies.ilsevent != 'undefined') throw(copies);
866
867         var r = error.yns_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_missing_pieces.ms_message', [util.functional.map_list( copies, function(o) { return o.barcode(); } ).join(", ")]),
868             $("catStrings").getString('staff.cat.util.mark_item_missing_pieces.ms_title'),
869             $("catStrings").getString('staff.cat.util.mark_item_missing_pieces.ms_ok_label'),
870             $("catStrings").getString('staff.cat.util.mark_item_missing_pieces.ms_cancel_label'), null,
871             $("catStrings").getString('staff.cat.util.mark_item_missing_pieces.ms_confirm_action'));
872
873         if (r == 0) {
874             var count = 0;
875             JSAN.use('cat.util');
876             for (var i = 0; i < copies.length; i++) {
877                 try {
878                     var robj = network.simple_request('MARK_ITEM_MISSING_PIECES',[ses(),copies[i].id()]);
879                     if (typeof robj.ilsevent != 'undefined') {
880                         if (robj.ilsevent == 0 /* SUCCESS */) {
881                             count++;
882                             // Print Slip
883                             if (robj.payload && robj.payload.slip) {
884                                 print.simple( robj.payload.slip.template_output().data() );
885                             }
886                             // Item Note
887                             cat.util.spawn_copy_editor( { 'copy_ids' : [ copies[i].id() ], 'edit' : 1 } );
888                             // Patron Message
889                             var my_xulG = win.open(
890                                 urls.XUL_NEW_STANDING_PENALTY,
891                                 'new_standing_penalty',
892                                 'chrome,resizable,modal',
893                                 {}
894                             );
895                             if (my_xulG.id) {
896                                 var penalty = new ausp();
897                                 penalty.usr( robj.payload.circ.usr() );
898                                 penalty.isnew( 1 );
899                                 penalty.standing_penalty( my_xulG.id );
900                                 penalty.org_unit( ses('ws_ou') );
901                                 penalty.note( my_xulG.note );
902                                 network.simple_request(
903                                     'FM_AUSP_APPLY',
904                                     [ ses(), penalty ]
905                                 );
906                             }
907                             // Patron Letter
908
909                             var txt_file = new util.file('letter.txt');
910                             txt_file.write_content('truncate',robj.payload.letter.template_output().data());
911                             var text_path = '"' + txt_file._file.path + '"';
912                             txt_file.close();
913
914                             var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces['nsIPrefBranch']);
915                             var key = 'oils.text_editor.external.cmd';
916                             var has_key = prefs.prefHasUserValue(key);
917                             var oils_external_letter_opener_cmd = has_key ? prefs.getCharPref(key) : 'C:\\Windows\\notepad.exe %letter.txt%';
918
919                             var cmd = oils_external_letter_opener_cmd.replace('%letter.txt%',text_path);
920
921                             var file = new util.file('letter.bat');
922                             file.write_content('truncate+exec',cmd);
923                             file.close();
924                             file = new util.file('letter.bat');
925
926                             dump('letter exec: ' + cmd + '\n');
927                             var process = Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
928                             process.init(file._file);
929
930                             var args = [];
931
932                             dump('process.run = ' + process.run(false, args, args.length) + '\n');
933
934                             file.close();
935
936                         } else if (robj.ilsevent == 1500 /* ACTION_CIRCULATION_NOT_FOUND */) {
937                             alert( $("catStrings").getFormattedString('staff.cat.util.mark_item_missing_pieces.circ_not_found',[ copies[i].barcode() ]) );
938                         } else {
939                             throw(robj);
940                         }
941                     } else {
942                         throw(robj);
943                     }
944                 } catch(E) {
945                     error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.util.mark_item_missing_pieces.marking_error', [copies[i].barcode()]),E);
946                 }
947             }
948             /*alert(count == 1 ? $("catStrings").getString('staff.cat.util.mark_item_missing_pieces.one_item_missing_pieces') :
949                 $("catStrings").getFormattedString('staff.cat.util.mark_item_missing_pieces.multiple_item_missing_pieces', [count]));*/
950         }
951
952         return true;
953     } catch(E) {
954         alert('Error in cat.util.mark_item_as_missing_pieces: ' + E);
955         return false;
956     }
957 }
958
959 cat.util.render_callnumbers_for_bib_menu = function(node, doc_id, label_class) {
960     try {
961         var cn_blob;
962         try {
963             cn_blob = g.network.simple_request('BLOB_MARC_CALLNUMBERS_RETRIEVE',[doc_id, label_class]);
964         } catch(E) {
965             cn_blob = [];
966         }
967         var hbox = typeof node == 'string' ? document.getElementById(node) : node;
968         JSAN.use('util.widgets');
969         JSAN.use('util.functional');
970         var ml = util.widgets.make_menulist(
971             [
972                 [ '', '' ]
973             ].concat(
974                 util.functional.map_list(
975                     cn_blob,
976                     function(o) {
977                         for (var i in o) {
978                             return [ o[i], i ];
979                         }
980                     }
981                 )
982             )
983         ); hbox.appendChild(ml);
984         ml.setAttribute('editable','true');
985         ml.setAttribute('width', '200');
986         ml.setAttribute('id', hbox.id + '_menulist');
987     } catch(E) {
988         alert('Error in cat.util.render_callnumbers_for_bib_menu: ' + E);
989     }
990 }
991
992 cat.util.render_cn_prefix_menuitems = function(menupopup,ou_id) {
993     try {
994         JSAN.use('OpenILS.data');
995         var data = new OpenILS.data(); data.stash_retrieve();
996         JSAN.use('util.network');
997         var network = new util.network();
998
999         if (typeof data.list['acnp_for_lib_'+ou_id] == 'undefined') {
1000             data.list['acnp_for_lib_'+ou_id] = network.simple_request(
1001                 'FM_ACNP_RETRIEVE_VIA_PCRUD',
1002                 [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acnp":"label_sortkey"}} ]
1003             );
1004             data.stash('list');
1005         }
1006         for (var i = 0; i < data.list['acnp_for_lib_'+ou_id].length; i++) {
1007             var my_acnp = data.list['acnp_for_lib_'+ou_id][i];
1008             var menuitem = document.createElement('menuitem');
1009             menupopup.appendChild(menuitem);
1010                 menuitem.setAttribute(
1011                     'label',
1012                     my_acnp.id() == -1 ? '' :
1013                     $('catStrings').getFormattedString(
1014                         'staff.cat.volume_copy_creator.call_number_prefix.menuitem_label',
1015                         [
1016                             my_acnp.label(),
1017                             data.hash.aou[ ou_id ].shortname()
1018                         ]
1019                     )
1020                 );
1021                 menuitem.setAttribute('value',my_acnp.id());
1022         }
1023     } catch(E) {
1024         alert('Error in cat.util.render_cn_prefix_menuitems: ' + E);
1025     }
1026 }
1027
1028 cat.util.render_cn_suffix_menuitems = function(menupopup,ou_id) {
1029     try {
1030         JSAN.use('OpenILS.data');
1031         var data = new OpenILS.data(); data.stash_retrieve();
1032         JSAN.use('util.network');
1033         var network = new util.network();
1034
1035         if (typeof data.list['acns_for_lib_'+ou_id] == 'undefined') {
1036             data.list['acns_for_lib_'+ou_id] = network.simple_request(
1037                 'FM_ACNS_RETRIEVE_VIA_PCRUD',
1038                 [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acns":"label_sortkey"}} ]
1039             );
1040             data.stash('list');
1041         }
1042         for (var i = 0; i < data.list['acns_for_lib_'+ou_id].length; i++) {
1043             var my_acns = data.list['acns_for_lib_'+ou_id][i];
1044             var menuitem = document.createElement('menuitem');
1045             menupopup.appendChild(menuitem);
1046                 menuitem.setAttribute(
1047                     'label',
1048                     my_acns.id() == -1 ? '' :
1049                     $('catStrings').getFormattedString(
1050                         'staff.cat.volume_copy_creator.call_number_suffix.menuitem_label',
1051                         [
1052                             my_acns.label(),
1053                             data.hash.aou[ ou_id ].shortname()
1054                         ]
1055                     )
1056                 );
1057                 menuitem.setAttribute('value',my_acns.id());
1058         }
1059     } catch(E) {
1060         alert('Error in cat.util.render_cn_suffix_menuitems: ' + E);
1061     }
1062 }
1063
1064 cat.util.render_cn_class_menu = function(extra_menuitems,menu_default) {
1065     try {
1066         JSAN.use('util.widgets');
1067         JSAN.use('OpenILS.data');
1068         var data = new OpenILS.data(); data.stash_retrieve();
1069
1070         var menulist = util.widgets.make_menulist(
1071             (extra_menuitems || []).concat(
1072                 util.functional.map_list(
1073                     data.list.acnc,
1074                     function(o) {
1075                         return [ o.name(), o.id() ];
1076                     }
1077                 )
1078             )
1079         );
1080
1081         if (typeof menu_default != 'undefined') {
1082             menulist.setAttribute('value',menu_default);
1083         }
1084         return menulist;
1085
1086     } catch(E) {
1087         alert('Error in cat.util.render_cn_class_menu: ' + E);
1088     }
1089 }
1090
1091 cat.util.render_cn_prefix_menu = function(ou_ids,extra_menuitems,menu_default) {
1092     try {
1093         JSAN.use('util.widgets');
1094         var menulist = util.widgets.make_menulist(extra_menuitems||[],menu_default);
1095             var menupopup = menulist.firstChild;
1096             var org_list;
1097             if (ou_ids.length == 1) {
1098                 JSAN.use('OpenILS.data');
1099                 var data = new OpenILS.data(); data.stash_retrieve();
1100                 var org = data.hash.aou[ ou_ids[0] ];
1101                 org_list = []; // order from top of consortium to owning lib
1102                 while(org) {
1103                     org_list.unshift(org.id());
1104                     org = org.parent_ou();
1105                     if (org && typeof org != 'object') {
1106                         org = data.hash.aou[ org ];
1107                     }
1108                 }
1109             } else {
1110                 org_list = ou_ids;
1111             }
1112             for (var i = 0; i < org_list.length; i++) {
1113                 cat.util.render_cn_prefix_menuitems(menupopup,org_list[i]);
1114             }
1115         if (typeof menu_default != 'undefined') {
1116             menulist.setAttribute('value',menu_default);
1117         }
1118         return menulist;
1119     } catch(E) {
1120         alert('Error in cat.util.render_cn_prefix_menu('+ou_id+'): ' + E);
1121     }
1122 }
1123
1124 cat.util.render_cn_suffix_menu = function(ou_ids,extra_menuitems,menu_default) {
1125     try {
1126         JSAN.use('util.widgets');
1127         var menulist = util.widgets.make_menulist(extra_menuitems||[],menu_default);
1128             var menupopup = menulist.firstChild;
1129             var org_list;
1130             if (ou_ids.length == 1) {
1131                 JSAN.use('OpenILS.data');
1132                 var data = new OpenILS.data(); data.stash_retrieve();
1133                 var org = data.hash.aou[ ou_ids[0] ];
1134                 org_list = []; // order from top of consortium to owning lib
1135                 while(org) {
1136                     org_list.unshift(org.id());
1137                     org = org.parent_ou();
1138                     if (org && typeof org != 'object') {
1139                         org = data.hash.aou[ org ];
1140                     }
1141                 }
1142             } else {
1143                 org_list = ou_ids;
1144             }
1145             for (var i = 0; i < org_list.length; i++) {
1146                 cat.util.render_cn_suffix_menuitems(menupopup,org_list[i]);
1147             }
1148         if (typeof menu_default != 'undefined') {
1149             menulist.setAttribute('value',menu_default);
1150         }
1151         return menulist;
1152     } catch(E) {
1153         alert('Error in cat.util.render_cn_suffix_menu('+ou_id+'): ' + E);
1154     }
1155 }
1156
1157 cat.util.request_items = function(copy_ids) {
1158     var error;
1159     try {
1160         JSAN.use('util.error');
1161         error = new util.error();
1162
1163         JSAN.use('util.functional');
1164         if (!copy_ids) { return; }
1165         copy_ids = util.functional.filter_list(
1166             copy_ids,
1167             function(o) { return o != null; }
1168         );
1169         if (copy_ids.length < 1) { return; }
1170
1171         xulG.new_tab(
1172             urls.XUL_HOLD_PLACEMENT,
1173             {},
1174             {
1175                 'copy_ids' : copy_ids
1176             }
1177         );
1178
1179     } catch(E) {
1180         alert('Error in cat.util.request_items: ' + E);
1181     }
1182 }
1183
1184 cat.util.mark_for_overlay = function(doc_id,doc_mvr) {
1185
1186     try {
1187
1188         JSAN.use('OpenILS.data'); var data = new OpenILS.data();
1189         data.stash_retrieve();
1190         JSAN.use('util.network'); var network = new util.network();
1191
1192         function gen_statusbar_click_handler(data_key) {
1193             return function (ev) {
1194
1195                 if (! data[data_key]) {
1196                     return;
1197                 }
1198
1199                 if (ev.button == 0 /* left click, spawn opac */) {
1200                     var opac_url = xulG.url_prefix('opac_rdetail')
1201                         + data[data_key];
1202                     var content_params = {
1203                         'session' : ses(),
1204                         'authtime' : ses('authtime'),
1205                         'opac_url' : opac_url,
1206                     };
1207                     xulG.new_tab(
1208                         xulG.url_prefix('XUL_OPAC_WRAPPER'),
1209                         {'tab_name':'Retrieving title...'},
1210                         content_params
1211                     );
1212                 }
1213
1214                 if (ev.button == 2 /* right click, remove mark */) {
1215                     if ( window.confirm( $('offlineStrings').getString(
1216                             'cat.opac.clear_statusbar')
1217                     ) ) {
1218                         data[data_key] = null;
1219                         data.stash(data_key);
1220                         ev.target.setAttribute('label','');
1221                         if (ev.target.hasAttribute('tooltiptext')) {
1222                             ev.target.removeAttribute('tooltiptext');
1223                         }
1224                     }
1225                 }
1226             }
1227         }
1228
1229         data.marked_record = doc_id;
1230         data.stash('marked_record');
1231         if (!doc_mvr) {
1232             var robj = network.simple_request(
1233                 'MODS_SLIM_RECORD_RETRIEVE.authoritative',[doc_id]);
1234             if (typeof robj.ilsevent == 'undefined') {
1235                 data.marked_record_mvr = robj;
1236             } else {
1237                 data.marked_record_mvr = null;
1238                 alert('Error in cat.util.mark_for_overlay #2: ', js2JSON(robj));
1239             }
1240         } else {
1241             data.marked_record_mvr = doc_mvr;
1242         }
1243         data.stash('marked_record_mvr');
1244         if (data.marked_record_mvr) {
1245             alert(
1246                 $('offlineStrings').getFormattedString(
1247                     'cat.opac.record_marked_for_overlay.tcn.alert',
1248                     [ data.marked_record_mvr.tcn() ]
1249                 )
1250             );
1251             xulG.set_statusbar(
1252                 1,
1253                 $("offlineStrings").getFormattedString(
1254                     'staff.cat.z3950.marked_record_for_overlay_indicator.tcn.label',
1255                     [data.marked_record_mvr.tcn()]
1256                 ),
1257                 $("offlineStrings").getFormattedString(
1258                     'staff.cat.z3950.marked_record_for_overlay_indicator.record_id.label',
1259                     [data.marked_record]
1260                 ),
1261                 gen_statusbar_click_handler('marked_record')
1262             );
1263         } else {
1264             alert(
1265                 $('offlineStrings').getFormattedString(
1266                     'cat.opac.record_marked_for_overlay.record_id.alert',
1267                     [ data.marked_record  ]
1268                 )
1269             );
1270             xulG.set_statusbar(
1271                 1,
1272                 $("offlineStrings").getFormattedString(
1273                     'staff.cat.z3950.marked_record_for_overlay_indicator.record_id.label',
1274                     [data.marked_record]
1275                 ),
1276                 '',
1277                 gen_statusbar_click_handler('marked_record')
1278             );
1279         }
1280     } catch(E) {
1281         alert('Error in cat.util.mark_for_overlay(): ' + E);
1282     }
1283 }
1284
1285 cat.util.get_cbs_for_bre_id = function(doc_id) {
1286     try {
1287         JSAN.use('util.network'); var network = new util.network();
1288         var bibObj = network.simple_request(
1289             'FM_BRE_RETRIEVE_VIA_ID',
1290             [ ses(), [ doc_id ] ]
1291         );
1292         bibObj = bibObj[0];
1293         var cbsObj = network.simple_request(
1294             'FM_CBS_RETRIEVE_VIA_PCRUD',
1295             [ ses(), bibObj.source() ]
1296         );
1297         return cbsObj;
1298     } catch(E) {
1299         alert('Error in cat.util.cbs_can_have_copies(): ' + E);
1300     }
1301 }
1302
1303 dump('exiting cat/util.js\n');