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