]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/opac/skin/default/js/holds.js
Place holds on issuances from the OPAC. Not backporting yet as it might be too
[working/Evergreen.git] / Open-ILS / web / opac / skin / default / js / holds.js
1 var holdsOrgSelectorBuilt = false;
2 var holdArgs;
3
4 /* 
5 note: metarecord holds have a holdable_formats field that contains
6 item_type(s)-item_forms(s)-language
7 item_form and language are optional - if language exist and no 
8 item_form is specified, use item_type(s)--language
9 */
10
11 var noEmailMessage;
12 var noEmailMessageXUL;
13
14 var holdTargetTypeMap = {
15     M : 'metarecord',
16     T : 'record',
17     V : 'volume',
18     I : 'issuance',
19     C : 'copy'
20 };
21
22
23
24 function holdsHandleStaff() {
25
26     // if we know the recipient's barcode, use it
27     if(xulG.patron_barcode) return _holdsHandleStaff();
28
29         swapCanvas($('xulholds_box'));
30         $('xul_recipient_barcode').focus();
31         $('xul_recipient_barcode').onkeypress = function(evt) 
32                 {if(userPressedEnter(evt)) { _holdsHandleStaff(); } };
33         $('xul_recipient_barcode_submit').onclick = _holdsHandleStaff;
34         $('xul_recipient_me').onclick = _holdsHandleStaffMe;
35
36         $('xul_recipient_barcode').onkeyup = function(evt) {
37         if($('xul_recipient_barcode').value == '') 
38             $('xul_recipient_me').disabled = false;
39         else
40             $('xul_recipient_me').disabled = true;
41     };
42 }
43
44 $('holds_frozen_thaw_input').onchange = 
45         function(){holdsVerifyThawDateUI('holds_frozen_thaw_input');}
46 $('holds_frozen_thaw_input').onkeyup = 
47         function(){holdsVerifyThawDateUI('holds_frozen_thaw_input');}
48
49 function _holdsHandleStaffMe() {
50         holdArgs.recipient = G.user;
51         holdsDrawEditor();
52 }
53
54 function _holdsHandleStaff() {
55         var barcode = xulG.patron_barcode || $('xul_recipient_barcode').value;
56         var user = grabUserByBarcode( G.user.session, barcode );
57
58         var evt;
59         if(evt = checkILSEvent(user)) {
60                 alertILSEvent(user);
61                 return;
62         }
63
64         if(!barcode || !user) {
65                 alertId('holds_invalid_recipient', barcode);
66                 return
67         }
68
69         grabUserPrefs(user);
70
71         holdArgs.recipient = user;
72         holdsDrawEditor();
73 }
74
75
76
77 /** args:
78   * record, volume, copy (ids)
79   * request, recipient, editHold (objects)
80   */
81
82 function holdsDrawEditor(args) {
83
84         holdArgs = (args) ? args : holdArgs;
85
86     if(!noEmailMessage)
87         noEmailMessage = $('holds_email').removeChild($('holds.no_email'));
88
89     if(!noEmailMessageXUL)
90         noEmailMessageXUL = $('holds_email').removeChild($('holds.no_email.xul'));
91
92         if(isXUL() && holdArgs.recipient == null 
93                         && holdArgs.editHold == null) {
94                 holdsHandleStaff();
95                 return;
96         }
97
98         if(!holdArgs.recipient) holdArgs.recipient = G.user;
99         if(!holdArgs.requestor) holdArgs.requestor = G.user;
100
101         if(!(holdArgs.requestor && holdArgs.requestor.session)) {
102                 detachAllEvt('common','locationChanged');
103                 attachEvt('common','loggedIn', holdsDrawEditor)
104                 initLogin();
105                 return;
106         }
107
108         if(holdArgs.editHold) // flesh the args with the existing hold 
109                 holdArgsFromHold(holdArgs.editHold, holdArgs);
110
111         holdsDrawWindow();
112 }
113
114
115 // updates the edit window with the existing hold's data 
116 function _holdsUpdateEditHold() {
117
118         var hold = holdArgs.editHold;
119         var qstats = holdArgs.status;
120
121         var orgsel = $('holds_org_selector');
122     var frozenbox = $('holds_frozen_chkbox');
123
124         setSelector(orgsel, hold.pickup_lib());
125
126         if( hold.capture_time() || qstats.status > 2 ) {
127         frozenbox.disabled = true;
128         $('holds_frozen_thaw_input').disabled = true;
129         if(qstats.status == 3) {
130             // no pickup lib changes while in-transit
131                     orgsel.disabled = true;
132         } else {
133             var orgs = fetchPermOrgs('UPDATE_PICKUP_LIB_FROM_HOLDS_SHELF');
134             if(orgs[0] == -1)
135                         orgsel.disabled = true;
136         }
137     } else {
138                 orgsel.disabled = false;
139         frozenbox.disabled = false;
140     }
141
142
143         $('holds_submit').onclick = holdsEditHold;
144         $('holds_update').onclick = holdsEditHold;
145
146         if(hold.phone_notify()) {
147                 $('holds_enable_phone').checked = true;
148                 $('holds_phone').value = hold.phone_notify();
149
150         } else {
151                 $('holds_phone').disabled = true;
152                 $('holds_enable_phone').checked = false;
153         }
154
155         if(isTrue(hold.email_notify())) {
156                 $('holds_enable_email').checked = true;
157
158         } else {
159                 $('holds_enable_email').checked = false;
160         }
161
162     dijit.byId('holds_expire_time').setValue(dojo.date.stamp.fromISOString(hold.expire_time()));
163
164     /* populate the hold freezing info */
165     if(!frozenbox.disabled && isTrue(hold.frozen())) {
166         frozenbox.checked = true;
167         unHideMe($('hold_frozen_thaw_row'));
168         if(hold.thaw_date()) {
169             dijit.byId('holds_frozen_thaw_input').setValue(dojo.date.stamp.fromISOString(hold.thaw_date()));
170         } else {
171             dijit.byId('holds_frozen_thaw_input').setValue('');
172         }
173     } else {
174         frozenbox.checked = false;
175         dijit.byId('holds_frozen_thaw_input').setValue('');
176         hideMe($('hold_frozen_thaw_row'));
177     }
178 }
179
180 function holdsEditHold() {
181         var hold = holdsBuildHoldFromWindow();
182         if(!hold) return;
183         holdsUpdate(hold);
184         showCanvas();
185         if(holdArgs.onComplete)
186                 holdArgs.onComplete(hold);
187 }
188
189 function holdArgsFromHold(hold, oargs) {
190         var args = (oargs) ? oargs : {};
191         args.type = hold.hold_type();
192         var target = hold.target();
193     args[holdTargetTypeMap[args.type]] = target;
194         return args;
195 }
196
197 function holdFetchObjects(hold, doneCallback) {
198
199         var args = (hold) ? holdArgsFromHold(hold) : holdArgs;
200
201         var type = args.type;
202
203         if( type == 'C' ) {
204
205                 if( args.copyObject ) {
206
207                         args.copy = args.copyObject.id();
208                         args.volume = args.copyObject.call_number();
209                         _h_set_vol(args, doneCallback);
210
211                 } else {
212                         var creq = new Request(FETCH_COPY, args.copy);
213
214                         creq.callback(
215                                 function(r) {
216                                         var cp = r.getResultObject();
217                                         args.copyObject = cp;
218                                         args.volume = args.copyObject.call_number();
219                                         _h_set_vol(args, doneCallback);
220                                 }
221                         );
222                         creq.send();
223                 }
224         } else {
225                 if( type == 'V' ) {
226                         _h_set_vol(args, doneCallback);
227
228         } else if( type == 'I' ) {
229             _h_set_issuance(args, doneCallback);
230
231                 } else {
232                         if( type == 'T' ) {
233                                 _h_set_rec(args, doneCallback);
234                         } else {
235                                 _h_set_rec_descriptors(args, doneCallback);
236                         }
237                 }
238         }
239
240         return args;
241 }
242
243 function _h_set_vol(args, doneCallback) {
244
245         if( args.volumeObject ) {
246                 args.volume = args.volumeObject.id();
247                 args.record = args.volumeObject.record();
248                 _h_set_rec(args, doneCallback);
249
250         } else {
251
252                 var vreq = new Request(FETCH_VOLUME, args.volume);
253                 vreq.callback(
254                         function(r) {
255                                 var vol = r.getResultObject();
256                                 args.volumeObject = vol;
257                                 args.record = vol.record();
258                                 _h_set_rec(args, doneCallback);
259                         }
260                 );
261                 vreq.send();
262         }
263 }
264
265 function _h_set_issuance(args, doneCallback) {
266
267         if( args.issuanceObject ) {
268                 args.issuance = args.issuanceObject.id();
269                 args.record = args.issuanceObject.subscription().record_entry();
270                 _h_set_rec(args, doneCallback);
271
272         } else {
273
274                 var vreq = new Request(FETCH_ISSUANCE, [args.issuance]);
275                 vreq.callback(
276                         function(r) {
277                                 var issuance = r.getResultObject()[0];
278                                 args.issuanceObject = issuance;
279                                 args.record = issuance.subscription().record_entry();
280                                 _h_set_rec(args, doneCallback);
281                         }
282                 );
283                 vreq.send();
284         }
285 }
286
287 function _h_set_rec(args, doneCallback) {
288
289         if(args.recordObject) 
290                 args.record = args.recordObject.doc_id();
291         else 
292                 args.recordObject = findRecord( args.record, 'T' );
293         
294         if( args.type == 'T' || args.type == 'M' ) 
295                 _h_set_rec_descriptors(args, doneCallback);
296         else 
297                 if(doneCallback) doneCallback(args);
298 }
299
300
301 function _h_set_rec_descriptors(args, doneCallback) {
302
303         // grab the list of record desciptors attached to this records metarecord 
304         if( ! args.recordDescriptors )  {
305                 var params = {};
306
307         if (args.type == 'M') {
308                 if( !args.metarecord && args.record) {
309                 params.metarecord = args.metarecord = args.record;
310                 delete(args.record);
311                 } else {
312                                 params = { metarecord : args.metarecordObject.doc_id() };
313                 }
314         } else {
315                 params = { record: args.record };
316         }
317
318                 if( ! args.record ) {
319                         if( args.metarecord )
320                                 params = { metarecord : args.metarecord };
321                         else 
322                                 params = { metarecord : args.metarecordObject.doc_id() };
323                 }
324
325                 var req = new Request(FETCH_MR_DESCRIPTORS, params );
326                 req.callback(
327                         function(r) {
328                                 var data = r.getResultObject();
329                                 holdArgs.recordDescriptors = args.recordDescriptors = data.descriptors;
330                                 holdArgs.metarecord = args.metarecord = data.metarecord;
331                                 if( args.type == 'M' && ! args.metarecordObject) 
332                                         holdArgs.metarecordObject = args.metarecordObject = findRecord(args.metarecord, 'M');   
333
334                                 if(doneCallback) doneCallback(args);
335                         }
336                 );
337                 req.send();
338
339         } else {
340                 if(doneCallback) doneCallback(args);
341         }
342
343         return args;
344 }
345
346
347
348 function holdsDrawWindow() {
349         swapCanvas($('holds_box'));
350         $('holds_cancel').onclick = function(){ runEvt('common', 'holdUpdateCanceled'), showCanvas() };
351         $('holds_submit').onclick = function(){holdsPlaceHold(holdsBuildHoldFromWindow())};
352         $('holds_update').onclick = function(){holdsPlaceHold(holdsBuildHoldFromWindow())};
353         holdFetchObjects(null, 
354                 function(){
355                         __holdsDrawWindow();
356
357                         if(holdArgs.editHold) {
358                                 hideMe($('holds_submit'));
359                                 unHideMe($('holds_update'));
360                                 var req = new Request(FETCH_HOLD_STATUS, 
361                                         G.user.session, holdArgs.editHold.id());
362                                 req.send(true);
363                                 holdArgs.status = req.result();
364                                 _holdsUpdateEditHold();
365                         }  
366                 }
367         );
368 }
369
370 function __holdsDrawWindow() {
371
372         var rec = holdArgs.recordObject;
373         var vol = holdArgs.volumeObject;
374         var copy = holdArgs.copyObject;
375         var mr = holdArgs.metarecordObject;
376
377         rec = (rec) ? rec : mr;
378
379         if(!holdsOrgSelectorBuilt) {
380                 holdsBuildOrgSelector(null,0);
381                 holdsOrgSelectorBuilt = true;
382                 var selector = $('holds_org_selector');
383
384                 /*
385                 var o_loc = findOrgUnit(getOrigLocation());
386                 var t = findOrgType(o_loc.ou_type());
387                 if( t.can_have_users() ) 
388                         setSelector(selector, o_loc.id());
389                 else 
390                 */
391
392                 setSelector(selector, holdArgs.recipient.home_ou());
393         
394         }
395
396         /*
397         if(isXUL()) {
398                 var dsel = $('holds_depth_selector');
399                 unHideMe($('holds_depth_selector_row'));
400                 if(dsel.getElementsByTagName('option').length == 0) {
401                         var types = globalOrgTypes;
402                         var depth = findOrgDepth(G.user.ws_ou());
403                         iterate(types, 
404                                 function(t) {
405                                         if(t.depth() > depth) return;
406                                         insertSelectorVal(dsel, -1, t.opac_label(), t.depth());
407                                 }
408                         );
409                 }
410         }
411         */
412
413         appendClear($('holds_recipient'), text(
414                 holdArgs.recipient.family_name() + ', ' +  
415                         holdArgs.recipient.first_given_name()));
416         appendClear($('holds_title'), text(rec.title()));
417         appendClear($('holds_author'), text(rec.author()));
418
419     if( holdArgs.type == 'I' ) {
420                 unHideMe($('holds_type_row'));
421         unHideMe($('holds_is_issuance'));
422         unHideMe($('holds_issuance_row'));
423         appendClear($('holds_issuance_label'), text(holdArgs.issuanceObject.label()));
424
425     } else if( holdArgs.type == 'V' || holdArgs.type == 'C' ) {
426
427                 unHideMe($('holds_type_row'));
428                 unHideMe($('holds_cn_row'));
429                 appendClear($('holds_cn'), text(holdArgs.volumeObject.label()));
430
431                 if( holdArgs.type == 'V'  ) {
432                         unHideMe($('holds_is_cn'));
433                         hideMe($('holds_is_copy'));
434
435                 } else {
436                         hideMe($('holds_is_cn'));
437                         unHideMe($('holds_is_copy'));
438                         unHideMe($('holds_copy_row'));
439                         appendClear($('holds_copy'), text(holdArgs.copyObject.barcode()));
440                 }
441
442         } else {
443                 hideMe($('holds_type_row'));
444                 hideMe($('holds_copy_row'));
445                 hideMe($('holds_cn_row'));
446                 hideMe($('holds_issuance_row'));
447         }
448
449         removeChildren($('holds_format'));
450
451         var mods_formats = rec.types_of_resource();
452         var formats;
453
454         if(holdArgs.recordDescriptors)
455                 formats = holdArgs.recordDescriptors[0].item_type();
456
457         if( holdArgs.type == 'T' ) {
458                 var desc = grep( holdArgs.recordDescriptors,
459                         function(i) {
460                                 return (i.record() == holdArgs.record); 
461                         }
462                 );
463                 formats = desc[0].item_type();
464         }
465
466         if( holdArgs.type == 'M' ) {
467                 var mr_formats;
468                 if(holdArgs.editHold){
469                         mr_formats = holdArgs.editHold.holdable_formats();
470                 }else{
471                         mr_formats = ''; // collect the item_type()s from all holdArgs.recordDescriptors
472                         for(var desc in holdArgs.recordDescriptors){
473                 if (!holdArgs.recordDescriptors[desc].item_type()) continue;
474                                 mr_formats += holdArgs.recordDescriptors[desc].item_type();
475                         }
476
477             var first_form = 1;
478                         for(var desc in holdArgs.recordDescriptors){
479                 if (!holdArgs.recordDescriptors[desc].item_form()) continue;
480                 if (first_form) {
481                     mr_formats += '-';
482                     first_form = 0;
483                 }
484                                 mr_formats += holdArgs.recordDescriptors[desc].item_form();
485                         }
486
487
488                 }
489                 
490                 var data = holdsParseMRFormats(mr_formats);
491                 mods_formats = data.mods_formats;
492                 formats = data.formats;
493         }
494
495
496         for( var i in mods_formats ) {
497                 var res = mods_formats[i];
498                 var img = elem("img");
499                 setResourcePic(img, res);
500                 $('holds_format').appendChild(img);
501                 if(formats)
502                         $('holds_format').appendChild(text(' '+ MARCTypeToFriendly(formats[i]) +' '));
503                 else
504                         $('holds_format').appendChild(text(' '+ mods_formats[i] +' '));
505                 $('holds_format').appendChild(elem('br'));
506         }
507
508
509         $('holds_phone').value = holdArgs.recipient.day_phone();
510         appendClear( $('holds_email'), text(holdArgs.recipient.email()));
511
512         var pref = holdArgs.recipient.prefs[PREF_HOLD_NOTIFY];
513
514         if(pref) {
515                 if( ! pref.match(/email/i) ) {
516                         $('holds_enable_email').checked = false;
517                 } else {
518                         $('holds_enable_email').checked = true;
519                 }
520
521                 if( ! pref.match(/phone/i) ) {
522                         $('holds_phone').disabled = true;
523                         $('holds_enable_phone').checked = false;
524                 } else {
525                         $('holds_phone').disabled = false;
526                         $('holds_enable_phone').checked = true;
527                 }
528         }
529
530     if(!holdArgs.recipient.email()) {
531                 $('holds_enable_email').checked = false;        
532                 $('holds_enable_email').disabled = true;
533         var message;
534         if(isXUL()) {
535             message = noEmailMessageXUL.cloneNode(true);
536                 appendClear($('holds_email'), message);
537         } else {
538             message = noEmailMessage.cloneNode(true);
539                 appendClear($('holds_email'), message);
540             $('holds.no_email.my_account').setAttribute('href', buildOPACLink({page:MYOPAC},null,true));
541         }
542         unHideMe(message);
543     }
544
545         if(!$('holds_phone').value) 
546                 $('holds_enable_phone').checked = false;        
547
548         appendClear($('holds_physical_desc'), text(rec.physical_description()));
549
550         if(holdArgs.type == 'M') hideMe($('hold_physical_desc_row'));
551
552         holdsSetFormatSelector();
553
554     $('holds_frozen_chkbox').checked = false;
555     hideMe($('hold_frozen_thaw_row'));
556
557     var interval = fetchOrgSettingDefault(holdArgs.recipient.home_ou(), 'circ.hold_expire_interval');
558     var secs = 0;
559     if(interval) {
560         secs = interval_to_seconds(interval);
561         var expire = new Date();
562         expire.setTime(expire.getTime() + Number(secs + '000'));
563         dijit.byId('holds_expire_time').setValue(expire);
564     }
565 }
566
567 function holdsParseMRFormats(str) {
568         var data = str.split(/-/);      
569
570         var formats = [];
571         var mods_formats = [];
572
573         for( var i = 0; i < data[0].length; i++ ) {
574                 formats.push( data[0].charAt(i) );
575                 mods_formats.push( MARCFormatToMods( formats[i] ) );
576         }
577         
578         formats = uniquify(formats);
579         mods_formats = uniquify(mods_formats);
580
581         return {
582                 formats                 : formats,
583                 mods_formats    : mods_formats,
584                 lang                            : data[2],
585                 largeprint              : data[1]
586         };
587 }
588
589
590 function holdsSetFormatSelector() {
591         var type = holdArgs.type;
592         if( type == 'C' || type == 'V' || type == "I" || holdArgs.editHold ) return;
593
594         var data                                = holdsGetFormats();
595         var avail_formats       = data.avail_formats;
596         var sel_formats = data.sel_formats;
597         holdArgs.language = data.lang;
598         if( type=='M'){         
599                 hideMe($('holds_alt_formats_row_extras'));
600                 unHideMe($('holds_alt_formats_row'));   
601         }else{
602                 unHideMe($('holds_alt_formats_row_extras'));
603         }
604
605         var selector = $('hold_alt_form_selector');
606
607     for( var i = 0; i < selector.options.length; i++ ) {
608         if (selector.options[i].className.indexOf('hide_me') == -1)
609             hideMe(selector.options[i]);
610     }
611
612         for( var i = 0; i < avail_formats.length; i++ ) {
613                 var form = avail_formats[i];
614                 var opt = findFormatSelectorOptByParts(selector,form);
615         if (!opt) continue;
616                 if(type=='M') opt.selected=true;
617                 unHideMe(opt);
618         }
619 }
620
621 function findFormatSelectorOptByParts( sel, val ) {
622     var parts = val.split('-');
623     for( var i = 0; i < sel.options.length; i++ ) {
624         var opt = sel.options[i];
625         var oval = opt.value;
626         var oparts = oval.split('-');
627         if( oparts[0].indexOf(parts[0]) > -1 && (!parts[1] || oparts[1].indexOf(parts[1]) > -1) ) return opt;
628     }
629     return null;
630 }
631
632 function holdsGetFormats() {
633
634         var lang;
635         var formats = [];
636         var sformats = []; // selected formats 
637
638         var type = holdArgs.type;
639         var desc = holdArgs.recordDescriptors;
640         var rec = holdArgs.record;
641         var mrec = holdArgs.metarecord;
642
643         for( var i = 0; i < desc.length; i++ ) {
644                 var d = desc[i];
645                 if( type == 'T' && d.item_lang() != lang ) continue;
646                 formats.push( _t_f_2_format(d.item_type(), d.item_form()));
647         }
648
649         formats = uniquify(formats);
650
651         if( type == 'T') {
652
653                 for( var i = 0; i < desc.length; i++ ) {
654                         var d = desc[i];
655                         if( d.record() == holdArgs.record ) {
656                                 lang = d.item_lang();
657                                 holdArgs.myFormat =  _t_f_2_format(d.item_type(), d.item_form());
658                                 sformats.push(holdArgs.myFormat);
659                                 break;
660                         }
661                 }
662         } else if( type =='M') {
663
664         // All available formats are selected by default in MR holds
665                 for( var i = 0; i < formats.length; i++ ) {
666                         sformats.push(formats[i]);
667                 }
668         }
669
670         return {
671                 lang : lang,
672                 avail_formats : formats, 
673                 sel_formats : sformats
674         };
675 }
676
677
678
679 function _t_f_2_format(type, form) {
680         if( (type == 'a' || type == 't') && form == 's' ) return 'at-s';
681         if( form == 'd' ) return 'at-d';
682         return (type == 'a' || type == 't') ? 'at' : type;
683 }
684
685 function holdsSetSelectedFormats() {
686
687         var cn = $('holds_alt_formats_row').className;
688         if( cn && cn.match(/hide_me/) ) return;
689
690         var selector = $('hold_alt_form_selector');
691         var vals = getSelectedList(selector);
692
693         if(vals.length == 0) return;
694
695         var fstring = "";
696
697         if( contains(vals, 'at-d') || contains(vals, 'at-s')) {
698                 if( contains(vals, 'at') ) {
699                         fstring = 'at';
700                 } else if (contains(vals, 'at-s') && contains(vals, 'at-d')) {
701                         fstring = 'at-sd';
702                 } else if (!contains(vals, 'at-s')) {
703                         fstring = 'at-d';
704         } else {
705                         fstring = 'at-s';
706                 }
707         }
708
709         for( var i = 0; i < vals.length; i++ ) {
710                 var val = vals[i];
711                 if( !val.match(/at/) ) fstring = val + fstring;
712         }
713
714         if( holdArgs.language ) {
715                 if( fstring.match(/-/) )
716                         fstring = fstring + '-' + holdArgs.language;
717                 else
718                         fstring = fstring + '--' + holdArgs.language;
719         }
720
721
722         return fstring;
723 }
724
725
726 function holdsCheckPossibility(pickuplib, hold, recurse) {
727
728         var args = { 
729                 titleid : holdArgs.record,
730                 mrid : holdArgs.metarecord,
731                 volume_id : holdArgs.volume,
732                 issuanceid : holdArgs.issuance,
733                 copy_id : holdArgs.copy,
734                 hold_type : holdArgs.type,
735                 patronid : holdArgs.recipient.id(),
736                 depth : 0, 
737                 pickup_lib : pickuplib 
738         };
739
740         if(recurse) {
741                 /* if we're calling create again (recursing), 
742                         we know that the hold possibility check already succeeded */
743                 holdHandleCreateResponse({_recurse:true, _hold:hold}, true );
744
745         } else {
746                 _debug("hold possible args = "+js2JSON(args));
747         
748                 var req = new Request(CHECK_HOLD_POSSIBLE, G.user.session, args );
749         
750                 req.request.alertEvent = false;
751                 req.request._hold = hold;
752                 req.request._recurse = recurse;
753                 req.callback(holdHandleCreateResponse);
754                 req.send();
755         }
756 }
757
758
759 function holdsBuildOrgSelector(node) {
760
761         if(!node) node = globalOrgTree;
762         if(!isTrue(node.opac_visible())) return;
763
764         var render_this_org = true;
765         var orgHiding = checkOrgHiding(); // value here is cached so not too painful with the recursion
766         if (orgHiding) {
767                 if (node.id() == globalOrgTree.id()) {
768                         node = orgHiding.org; // top of tree = org hiding context org
769                 }
770                 if ( ! orgIsMine( orgHiding.org, node, orgHiding.depth ) ) {
771                         render_this_org = false;
772                 }
773         }
774
775         if (render_this_org) {
776                 var selector = $('holds_org_selector');
777                 var index = selector.options.length;
778
779                 var type = findOrgType(node.ou_type());
780                 var indent = type.depth() - 1;
781
782                 var opt = setSelectorVal( selector, index, node.name(), node.id(), null, indent );
783                 if(!type.can_have_users()) {
784                         opt.disabled = true;
785                         addCSSClass(opt, 'disabled_option');
786                 }
787         }
788         
789         for( var i in node.children() ) {
790                 var child = node.children()[i];
791                 if(child) holdsBuildOrgSelector(child);
792         }
793 }
794
795 function holdsBuildHoldFromWindow() {
796
797         var org = getSelectorVal($('holds_org_selector'));
798         var node = findOrgUnit(org);
799         var ntype = findOrgType(node.ou_type());
800         if(!ntype.can_have_users()) {
801                 alertId('holds_pick_good_org');
802                 return;
803         }
804
805     fieldmapper.IDL.load(['ahr']);
806         var hold = new ahr();
807         if(holdArgs.editHold) {
808                 hold = holdArgs.editHold;
809                 holdArgs.editHold = null;
810         }
811
812         if( $('holds_enable_phone').checked ) {
813                 var phone = $('holds_phone').value;
814                 if( !phone || !phone.match(REGEX_PHONE) ) {
815                         alert($('holds_bad_phone').innerHTML);
816                         return null;
817                 }
818                 hold.phone_notify(phone);
819
820         } else {
821                 hold.phone_notify("");
822         }
823
824         if( $('holds_enable_email').checked ) 
825                 hold.email_notify(1);
826         else
827                 hold.email_notify(0);
828
829         var target = holdArgs[holdTargetTypeMap[holdArgs.type]];
830
831         hold.pickup_lib(org); 
832         //hold.request_lib(org); 
833         hold.requestor(holdArgs.requestor.id());
834         hold.usr(holdArgs.recipient.id());
835         hold.target(target);
836         hold.hold_type(holdArgs.type);
837
838     var expDate = dijit.byId('holds_expire_time').getValue();
839     if(expDate) {
840         var expireDate = dojo.date.stamp.toISOString(expDate);
841         expireDate = holdsVerifyThawDate(expireDate); 
842         if(expireDate)
843             hold.expire_time(expireDate);
844         else 
845             return;
846     }
847
848     // see if this hold should be frozen and for how long
849     if($('holds_frozen_chkbox').checked) {
850         hold.frozen('t');
851         unHideMe($('hold_frozen_thaw_row'));
852         var thawDate = dijit.byId('holds_frozen_thaw_input').attr('value');
853         if(thawDate) {
854             thawDate = dojo.date.stamp.toISOString(thawDate);
855             thawDate = holdsVerifyThawDate(thawDate); 
856             if(thawDate) 
857                 hold.thaw_date(thawDate);
858             else
859                 return;
860         } else {
861             hold.thaw_date(null);
862         }
863     } else {
864         hold.frozen('f');
865         hold.thaw_date(null);
866     }
867
868         //check for alternate hold formats 
869         var fstring = holdsSetSelectedFormats();
870         if(fstring) { 
871                 hold.hold_type('M'); 
872                 hold.holdable_formats(fstring);
873                 hold.target(holdArgs.metarecord);
874         }
875         return hold;
876 }
877         
878 function holdsPlaceHold(hold, recurse) {
879         if(!hold) return;
880         swapCanvas($('check_holds_box'));
881         holdsCheckPossibility(hold.pickup_lib(), hold, recurse);
882 }
883
884
885 function holdHandleCreateResponse(r, recurse) {
886
887         if(!recurse) {
888                 var res = r.getResultObject();
889                 if(checkILSEvent(res) || res.success != 1) {
890                         if(res.success != 1) {
891                                 alert($('hold_not_allowed').innerHTML);
892                         } else {
893                                 if( res.textcode == 'PATRON_BARRED' ) {
894                                         alertId('hold_failed_patron_barred');
895                         } else {
896                                         alert($('hold_not_allowed').innerHTML);
897                                 }
898                         }
899                         swapCanvas($('holds_box'));
900                         return;
901                 }
902         r._hold.selection_depth(res.depth);
903         }       
904
905         holdCreateHold(r._recurse, r._hold);
906 }
907
908
909 function holdCreateHold( recurse, hold ) {
910         var method = CREATE_HOLD;
911         if(recurse) method = CREATE_HOLD_OVERRIDE;
912         var req = new Request( method, holdArgs.requestor.session, hold );
913         req.request.alertEvent = false;
914         req.send(true);
915         var res = req.result();
916         holdProcessResult(hold, res, recurse);
917         
918         showCanvas();
919
920         runEvt('common', 'holdUpdated');
921 }
922
923
924 function holdProcessResult( hold, res, recurse ) {
925
926         if( res && res > -1 ) {
927                 alert($('holds_success').innerHTML);
928                 holdArgs = null;
929         if(isXUL() && typeof xulG.opac_hold_placed == 'function')
930             xulG.opac_hold_placed(res);
931
932         } else {
933
934                 if( recurse ) {
935                         alert($('holds_failure').innerHTML);
936                         return;
937                 }
938
939                 if( grep(res, function(e) { return (e.textcode == 'HOLD_EXISTS'); }) ) {
940                         if( fetchPermOrgs('HOLD_EXISTS.override')[0] != -1 ) {
941                                 if( confirm($('hold_dup_exists_override').innerHTML) ) {
942                                         return holdsPlaceHold(hold, true);
943                                 }
944                 return;
945
946                         } else {
947                                 return alert($('hold_dup_exists').innerHTML);
948                         }
949                 }
950
951                 if( grep(res, function(e) { return (e.textcode == 'HOLD_ITEM_CHECKED_OUT'); }) ) {
952                         if( fetchPermOrgs('HOLD_ITEM_CHECKED_OUT.override')[0] != -1 ) {
953                                 if( confirm($('hold_checked_out_override').innerHTML) ) {
954                                         return holdsPlaceHold(hold, true);
955                                 }
956                 return;
957
958                         } else {
959                                 return alert($('hold_checked_out').innerHTML);
960                         }
961                 }
962
963
964                 alert($('holds_failure').innerHTML);
965         }
966 }
967
968
969 function holdsCancel(holdid, user) {
970         if(!user) user = G.user;
971         var req = new Request(CANCEL_HOLD, user.session, holdid, /* Patron via OPAC */ 6);
972         req.send(true);
973         return req.result();
974         runEvt('common', 'holdUpdated');
975 }
976
977 function holdsUpdate(hold, user) {
978         if(!user) user = G.user;
979         var req = new Request(UPDATE_HOLD, user.session, hold);
980         req.send(true);
981         var x = req.result(); // cause an exception if there is one 
982         runEvt('common', 'holdUpdated');
983 }
984
985 /* verify that the thaw date is valid and after today */
986 function holdsVerifyThawDate(dateString, isGreater) {
987     thawDate = dojo.date.stamp.fromISOString(dateString);
988     if(thawDate) {
989         if(isGreater) {
990             if(dojo.date.compare(thawDate) > 0) {
991                 return dojo.date.stamp.toISOString(thawDate);
992             }
993         } else {
994             return dojo.date.stamp.toISOString(thawDate);
995         }
996     }
997     return null;
998 }
999
1000 function holdsVerifyThawDateUI(element) {
1001     value = dojo.date.stamp.toISOString(dijit.byId(element).getValue());
1002
1003     if(!value) {
1004         removeCSSClass($(element), 'invalid_field');
1005         return;
1006     }
1007
1008     if(!holdsVerifyThawDate(value, true)) {
1009         addCSSClass($(element), 'invalid_field');
1010     } else {
1011         removeCSSClass($(element), 'invalid_field');
1012     }
1013 }
1014