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