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