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