1;(function ( $, window, document, undefined ) { 2 3 "use strict"; 4 var pluginName = "amRoomInfo", 5 defaults = { 6 timeZone:'America/New_York', 7 allowOtherDates:true, 8 pinlessAuth:false, 9 loginRequired:false 10 }; 11 var self=false; 12 13 // The actual plugin constructor'email' 14 function Plugin ( element, options ) { 15 self=this; 16 this.element = element; 17 this.$element = $(element); 18 this.settings = $.extend(true, {}, defaults, options ); 19 this._defaults = defaults; 20 this._name = pluginName; 21 this.currentTime=moment(self.settings.startTime).tz(self.settings.timeZone); 22 this.currentNavigationDate=self.currentTime.clone().startOf('day'); 23 this.init(); 24 } 25 26 $.extend(Plugin.prototype, { 27 init: function () { 28 self._buildInterface(); 29 self._bindEvents(); 30 self._updateRoomList(); 31 self._update(); 32 }, 33 _buildInterface:function(){ 34 self.$element.css({ 35 backgroundColor: '#171717', 36 color:'##949494', 37 width: '100%', 38 height: '100%', 39 position: 'absolute', 40 top: '0', 41 left: '0' 42 43 }); 44 45 self.holder = $('<div class="amri-holder"></div>').appendTo(self.element); 46 47 self.leftCol = $('<div class="amri-left-col"></div>').appendTo(self.holder); 48 self.rightCol = $('<div class="amri-right-col"></div>').appendTo(self.holder); 49 50 self.bookingFormHolder = $('<div class="amri-booking-form-holder"></div>').appendTo(self.leftCol).hide(); 51 self.bookingForm = $('<div class="amri-booking-form"></div>').appendTo(self.bookingFormHolder); 52 53 self.statusBox = $('<div class="amri-status-box"></div>').appendTo(self.leftCol); 54 self.statusBoxContent = $('<div class="amri-status-box-content"></div>').appendTo(self.statusBox); 55 self.roomName = $('<div class="amri-room-name"></div>').appendTo(self.statusBoxContent); 56 self.roomStatus = $('<div class="amri-room-status"></div>').appendTo(self.statusBoxContent); 57 self.roomStatusUntil = $('<div class="amri-room-status-until"></div>').appendTo(self.statusBoxContent); 58 self.roomStatusTitle = $('<div class="amri-room-status-title"></div>').appendTo(self.statusBoxContent); 59 self.roomStatusRemaining = $('<div class="amri-room-status-remaining"></div>').appendTo(self.statusBoxContent); 60 61 self.upnextBox = $('<div class="amri-next-box"></div>').appendTo(self.leftCol); 62 63 self.topBox = $('<div class="amri-top-box"></div>').appendTo(self.rightCol); 64 self.timeBox = $('<div class="amri-time-box"><div class="amri-date"></div><div class="amri-time"></div></div>').appendTo(self.topBox); 65 $('<div class="amri-choose-label">Choose a room</div>').appendTo(self.topBox); 66 67 self.availabilityList = $('<div class="amri-availability-box"></div>').appendTo(self.rightCol); 68 69 self.roomList=$('<select class="form-control"></select>').appendTo(self.topBox); 70 if(self.settings.allowOtherDates){ 71 self.todayButton=$('<button class="amri-today-btn">Today</button>').appendTo(self.topBox); 72 } 73 self.navigation=$('<div class="amri-navigation"></div>').appendTo(self.topBox); 74 75 if(self.settings.allowOtherDates){ 76 $('<div class="amri-prev amri-nav" data-amount="-1"><i class="fa fa-angle-left"></i></div><div class="amri-navigation-date"></div><div class="amri-next amri-nav" data-amount="1"><i class="fa fa-angle-right"></i></div>').appendTo(self.navigation); 77 self.topBox.addClass('amri-has-navigation'); 78 self.availabilityList.addClass('amri-has-navigation'); 79 }else{ 80 $('<div class="amri-bookings-title">Today\'s schedule</div>').appendTo(self.topBox); 81 } 82 83 self.todaysHours=$('<div class="amri-opening-hours"></div>').appendTo(self.topBox); 84 self.bookingsListHolder=$('<div class="amri-bookings-list-holder"></div>').appendTo(self.availabilityList); 85 self.bookingsList=$('<ul class="amri-bookings-list"></ul>').appendTo(self.bookingsListHolder); 86 87 self.bookBox = $('<div class="amri-book-box"></div>').appendTo(self.rightCol); 88 self.bookButton=$('<button class="amri-book-btn">Book a room</button>').appendTo(self.bookBox); 89 90 }, 91 _bindEvents:function(){ 92 $(window).on('resize.amc', function(event) {//TODO: debounce this 93 event.preventDefault(); 94 self.updateFonts(); 95 }); 96 self.roomList.on('change', function(event) { 97 event.preventDefault(); 98 self._updateNavigation(); 99 }); 100 if(self.settings.allowOtherDates){ 101 self.todayButton.on('click', function(event) { 102 event.preventDefault(); 103 self.currentNavigationDate=self.currentTime.clone().startOf('day'); 104 self._updateNavigation(); 105 }); 106 } 107 self.navigation.on('click', '.amri-nav', function(event) { 108 event.preventDefault(); 109 var amount = parseInt($(this).attr('data-amount'), 10); 110 self.currentNavigationDate.add(amount, 'days'); 111 self._updateNavigation(); 112 }); 113 self.bookButton.on('click', function(event) { 114 event.preventDefault(); 115 116 self.statusBox.hide(); 117 self.upnextBox.hide(); 118 self.bookButton.hide(); 119 self.bookingFormHolder.show(); 120 }); 121 self.bookingForm.on('click', '.amri-cancel-btn', function(event) { 122 event.preventDefault(); 123 self.statusBox.show(); 124 self.upnextBox.show(); 125 self.bookButton.show(); 126 self.bookingFormHolder.hide(); 127 }).on('change', '.agreed', function(event) { 128 event.preventDefault(); 129 if($(this).is(':checked')){ 130 $('.amri-submit-book-btn').removeProp('disabled'); 131 }else{ 132 $('.amri-submit-book-btn').prop('disabled', 'true'); 133 } 134 }).on('click', '.amri-submit-book-btn', function(event) { 135 event.preventDefault(); 136 137 var clean=true; 138 var requiredFields = $('.required-field', self.bookingForm); 139 for (var i = requiredFields.length - 1; i >= 0; i--) { 140 var input = $(requiredFields[i]).closest('label').parent().find('input'); 141 if(input.length>0&&input.val().length===0){ 142 var input=$(requiredFields[i]).closest('label').parent().find('input').addClass('amrp-missing-field'); 143 clean=false; 144 } 145 } 146 if(!clean){ 147 $('.events2-error-message', self.bookingForm).text('Please enter information for all required fields').fadeIn().delay(3000).fadeOut(); 148 return; 149 } 150 151 var start = moment(self.currentNavigationDate.format('YYYY-MM-DD')+' '+$('#start_hours').val()+':'+$('#start_mins').val()+$('#start_ampm').val(), 'YYYY-MM-DD h:mma'); 152 var end = start.clone().add($('#duration', self.bookingForm).val(), 'minute'); 153 154 var open=moment(self.openingHours.raw_open_time); 155 var close=moment(self.openingHours.raw_close_time); 156 157 if(start<open||end>close){ 158 $('.events2-error-message', self.bookingForm).text('Sorry, the room is unavailable at this time.').fadeIn().delay(3000).fadeOut(); 159 return; 160 } 161 162 for (var i = self.currentRoom.bookings.length - 1; i >= 0; i--) { 163 var booking = self.currentRoom.bookings[i]; 164 var lStart=moment(booking.start_time); 165 var lEnd=moment(booking.end_time); 166 167 if(lStart<end && lEnd>start){ 168 $('.events2-error-message', self.bookingForm).text('Sorry, the room is unavailable at this time.').fadeIn().delay(3000).fadeOut(); 169 return; 170 } 171 } 172 var booking={ 173 assets:[], 174 contact:{ 175 first_name:$('#first_name', self.bookingForm).val(), 176 last_name:$('#last_name', self.bookingForm).val(), 177 email:$('#email', self.bookingForm).val(), 178 booking_title:$('#booking_title', self.bookingForm).val(), 179 group_name: $('#first_name', self.bookingForm).val()+' '+$('#last_name', self.bookingForm).val(), 180 librarycard:$('#librarycard', self.bookingForm).val(), 181 phone:$('#tel', self.bookingForm).val() 182 }, 183 customQuestions:false, 184 start_time: start.format('YYYY-MM-DD HH:mm:ss'), 185 end_time: end.format('YYYY-MM-DD HH:mm:ss'), 186 room_id: self.roomList.val(), 187 } 188 $.getJSON('/reserve?action=submit', booking, function(json, textStatus) { 189 self.statusBox.show(); 190 self.upnextBox.show(); 191 self.bookButton.show(); 192 self.bookingFormHolder.hide(); 193 self._update(); 194 self._updateNavigation(); 195 }); 196 197 }).on('focus', '.amrp-missing-field', function(event) { 198 $(this).removeClass('amrp-missing-field'); 199 }); 200 self.bookingForm.on('click', '.amri-login-btn', function(event) { 201 event.preventDefault(); 202 var b=$(this).button('loading'); 203 204 // CH#1942 - Trim the card/pin fields to prevent blanks. 205 var card= $.trim($('#login_card', self.holder).val()); 206 var pin= $.trim($('#login_pin', self.holder).val()); 207 208 $.getJSON(self.settings.apiServer+'/v1/'+self.settings.client+'/patron', {u:card, p:pin, 'type':'reserve'}, function(json, textStatus) { 209 if(json.result=='ok'){ 210 if(json.data.names.length>0){ 211 $('#first_name', self.bookingForm).val(json.data.names[0].first_name); 212 $('#last_name', self.bookingForm).val(json.data.names[0].last_name); 213 214 $('#booking_title', self.bookingForm).val(json.data.names[0].first_name+' '+json.data.names[0].last_name); 215 } 216 if(json.data.phones.length>0){
217 $('#tel', self.bookingForm).val(json.data.phones[0]); 218 } 219 if(json.data.emails.length>0){ 220 $('#email', self.bookingForm).val(json.data.emails[0]); 221 } 222 if(json.data.barcodes.length>0){ 223 $('#librarycard', self.bookingForm).val(json.data.barcodes[0]); 224 } 225 }else{ 226 $('.events2-error-message', self.holder).text(json.message||'Sorry, pin or card number not found').fadeIn().delay(3000).fadeOut(); 227 } 228 b.button('reset'); 229 }).error(function() { 230 $('.events2-error-message', self.holder).text('Sorry, there was a problem making the request').fadeIn().delay(3000).fadeOut(); 231 b.button('reset'); 232 }); 233 234 }); 235 setInterval(function(){ 236 self.currentTime.add(1, 'minute'); 237 self._update(); 238 }, 60000) 239 }, 240 _reset:function(){ 241 242 }, 243 _createField:function(id, type, label, required, append, desc, min){ 244 if(!type)type='text'; 245 if(!label)label=''; 246 if(!desc)desc=''; 247 var field = $('<div class="row"></div>'); 248 var input = ''; 249 250 switch(type){ 251 case 'number': 252 var minV = 0; 253 if(typeof(min) !== "undefined"){ 254 minV = min; 255 input='<input type="'+type+'" class="form-control" id="'+id+'" min="'+minV+'">'; 256 }else{ 257 input='<input type="'+type+'" class="form-control" id="'+id+'">'; 258 } 259 260 break; 261 case 'select': 262 input='<select class="form-control" id="'+id+'"></select>'; 263 break; 264 default: 265 input='<input type="'+type+'" class="form-control" id="'+id+'">'; 266 break; 267 } 268 269 if(append){ 270 $('<div class="">'+ 271 '<div class="col-sm-5">'+ 272 '<div class="input-group">'+ 273 input+ 274 '</div>'+ 275 '</div>'+ 276 '</div>').appendTo(field); 277 $('.input-group', field).append(append); 278 279 }else{ 280 $('<div class="">'+ 281 '<div class="col-sm-5">'+ 282 input+ 283 '</div>'+ 284 '</div>').appendTo(field); 285 } 286 287 var labelEl = $('<label for="'+id+'" class="col-sm-3 control-label">'+label+' </label>').prependTo(field); 288 if(required)labelEl.append('<span class="required-field"></span>'); 289 $('<div class="col-sm-4 amrp-field-desc">'+desc+'</div>').appendTo(field); 290 return field; 291 }, 292 _updateBookingForm:function(room){ 293 self.bookingForm.empty(); 294 $('<div class="amri-book-room-title">'+room.title+'</div>').appendTo(self.bookingForm); 295 $('<div class="amri-book-room-date">'+this.currentNavigationDate.format('dddd DD MMMM YYYY')+'</div>').appendTo(self.bookingForm); 296 297 298 self._createField('login_card', 'text', 'Library card number', true).appendTo(self.bookingForm); 299 var pin=self._createField('login_pin', 'password', 'PIN / Password', false, '<span data-loading-text="<i class=\'fa fa-circle-o-notch fa-spin\'></i>" class="btn btn-edit amri-login-btn input-group-addon">Look up details</span>', 'Enter your Library card number and PIN and we\'ll look up your details to fill in the next felds.').appendTo(self.bookingForm); 300 301 var start_hours = self._createField('start_hours', 'select', 'Booking from:', false, false).addClass('amri-booking-start').appendTo(self.bookingForm); 302 var start_select = $('#start_hours', start_hours); 303 304 for (var i = 1; i <= 12; i++) { 305 $('<option>'+i+'</option>').appendTo(start_select); 306 } 307 308 var h=this.currentTime.format('h'); 309 var r=Math.round(this.currentTime.format('mm') /15) * 15; 310 if(r==60){ 311 r='00'; 312 h++; 313 if(h>12)h=1; 314 }else if(r==0){ 315 r='00'; 316 } 317 318 start_select.val(h); 319 320 321 var startMin=$('<select id="start_mins" class="form-control">'+ 322 '<option>00</option>'+ 323 '<option>15</option>'+ 324 '<option>30</option>'+ 325 '<option>45</option>'+ 326 '</select>').insertAfter(start_select).val(r); 327 328 var startAMPM=$('<select id="start_ampm" class="form-control">'+ 329 '<option>am</option>'+ 330 '<option>pm</option>'+ 331 '</select>').insertAfter(startMin).val(this.currentTime.format('a')); 332 333 334 var duration = self._createField('duration', 'select', 'Duration:', false, false).appendTo(self.bookingForm); 335 336 for (var i = 15; i <= parseInt(room.restrictions.maximum_booking_time, 10); i+=15) { 337 $('<option value="'+i+'">'+self._formatMinsAsString(i)+'</option>').appendTo($('#duration', duration)); 338 } 339 340 self._createField('first_name', 'text', 'First name:', true, false).appendTo(self.bookingForm); 341 self._createField('last_name', 'text', 'Last name:', true, false).appendTo(self.bookingForm); 342 343 self._createField('email', 'text', 'Email', self.settings.emailRequired, false, 'We\'ll use the email address to confirm the booking.').appendTo(self.bookingForm); 344 $('<input type="hidden" id="tel" value="">').appendTo(self.bookingForm); 345 $('<input type="hidden" id="librarycard" value="">').appendTo(self.bookingForm); 346 347 self._createField('booking_title', 'text', 'Display name:', true, false, '(This <b>will be displayed</b> to the public)').appendTo(self.bookingForm); 348 349 if(room.app_tc_link&&room.app_tc_link.length>0){ 350 $('<label><input class="agreed" type="checkbox"> I have read and agree to the room booking <a target="_blank" href="'+room.app_tc_link+'">terms and conditions</a></label>').appendTo(self.bookingForm); 351 }else{ 352 $('<label><input class="agreed" type="checkbox"> I have read and agree to the room booking terms and conditions</label>').appendTo(self.bookingForm); 353 } 354
355 // $('<div class="amrp-spacer"></div>').appendTo(self.bookingForm); 356 $('<div class="amri-booking-buttons"><div class="events2-error-message"></div><button class="btn btn-edit amri-cancel-btn">Cancel</button><button class="btn btn-success amri-submit-book-btn" disabled="disabled">Book</button></div>').appendTo(self.bookingForm); 357 358 }, 359 _updateNavigation:function(){ 360 var dateString = self.currentNavigationDate.format('YYYY-MM-DD'); 361 var selected=parseInt(self.roomList.val(), 10); 362 363 $.getJSON(self.settings.apiServer+'/v2/'+self.settings.client+'/rooms/'+self.settings.location_id, {date:dateString, classtype:1}, function(json, textStatus) { 364 self.bookingsList.empty(); 365 $('.amri-navigation-date', self.navigation).text(self.currentNavigationDate.format('MMMM DD, YYYY')); 366 $.each(json, function (index, room) { 367 if(parseInt(room.id, 10)===selected){ 368 self.currentRoom=room; 369 self._updateBookingForm(room); 370 if(room.bookings.length>0){ 371 $.each(room.bookings, function(index, val) { 372 var start=moment.tz(val.start_time, self.settings.timeZone); 373 var end=moment.tz(val.end_time, self.settings.timeZone); 374 $('<li><div>' + start.format('h:mma') + ' - ' + end.format('h:mma') + '</div>' + val.display_name + '</li>').appendTo(self.bookingsList); 375 }); 376 }else{ 377 $('<li class="amri-no-bookings">Free all day</li>').appendTo(self.bookingsList); 378 } 379 } 380 }); 381 }); 382 }, 383 _updateRoomList:function(){ 384 self.roomList.empty(); 385 $.getJSON(self.settings.apiServer+'/v2/'+self.settings.client+'/rooms/'+self.settings.location_id, {classtype:1, unmediated_only:1}, function(json, textStatus) { 386 $.each(json, function (index, val) { 387 if(self.settings.room_id==val.id){ 388 $('<option value="'+val.id+'">'+val.title+' (This room)</option>').appendTo(self.roomList); 389 }else if(val.patron_viewable==="0"){ 390 return; 391 }else{ 392 $('<option value="'+val.id+'">'+val.title+'</option>').appendTo(self.roomList); 393 } 394 }); 395 self.roomList.val(self.settings.room_id); 396 self._updateNavigation(); 397 }); 398 }, 399 _update:function(){ 400 self.updateFonts(); 401 self._updateTime(); 402 403 var dateString = self.currentTime.clone().startOf('day').format('YYYY-MM-DD'); 404 405 406 $.getJSON(self.settings.apiServer+'/v2/'+self.settings.client+'/rooms/'+self.settings.location_id, {date:dateString, classtype:1, unmediated_only:1}, function(json, textStatus) { 407 self.rooms=json; 408 $.each(self.rooms, function(index, val) { 409 if(parseInt(val.id, 10)===parseInt(self.settings.room_id, 10)){ 410 self.currentRoom=val; 411 self._renderStatus(); 412 } 413 }); 414 415 $.getJSON(self.settings.apiServer+'/v1/'+self.settings.client+'/opening-hours/'+dateString+'/1', function(openingTimes, textStatus) { 416 self.openingHours=openingTimes[self.settings.location_id][dateString]; 417 var d = moment(dateString).startOf('day'); 418 self.openingHours.raw_open_time=d.clone().add(moment(self.openingHours.open, 'h:mma').diff(moment().startOf('day'), 'seconds'), 'seconds').format('YYYY-MM-DD HH:mm:ss'); 419 self.openingHours.raw_close_time=d.clone().add(moment(self.openingHours.close, 'h:mma').diff(moment().startOf('day'), 'seconds'), 'seconds').format('YYYY-MM-DD HH:mm:ss'); 420 421 self.todaysHours.text('Opening hours: '+self.openingHours.open.toLowerCase() + ' - '+self.openingHours.close.toLowerCase()); 422 }); 423 }); 424 425 426 }, 427 _renderStatus:function(){ 428 self.roomName.text(self.currentRoom.title); 429 var inuse=false; 430 var nextBooking=false; 431 for (var i = self.currentRoom.bookings.length - 1; i >= 0; i--) { 432 var booking = self.currentRoom.bookings[i]; 433 if(self.currentTime>moment.tz(booking.start_time, self.settings.timeZone)&&self.currentTime<moment.tz(booking.end_time, self.settings.timeZone)){ 434 inuse=booking; 435 } 436 if(!nextBooking&&moment.tz(booking.start_time, self.settings.timeZone)>
436self.currentTime){ 437 nextBooking=booking; 438 } 439 } 440 if(inuse){ 441 var end=moment.tz(inuse.end_time, self.settings.timeZone); 442 var left=Math.ceil(moment.duration(end.diff(self.currentTime)).asMinutes()); 443 self.statusBox.css({backgroundColor: '#C6001F'}); 444 self.roomStatus.text('In use'); 445 self.roomStatusUntil.text('Until '+end.format('h:mma')); 446 self.roomStatusTitle.text(inuse.display_name); 447 self.roomStatusRemaining.text(self._formatMinsAsString(left)+' remaining'); 448 }else{ 449 self.statusBox.css({backgroundColor: '#17B603'}); 450 self.roomStatus.text('Available'); 451 } 452 self.upnextBox.empty(); 453 if(nextBooking){ 454 var nextStart=moment.tz(nextBooking.start_time, self.settings.timeZone); 455 var nextend=moment.tz(nextBooking.end_time, self.settings.timeZone); 456 $('<div><span>Next:</span> ' + nextStart.format('h:mma') + ' - ' + nextend.format('h:mma') + ' ' + nextBooking.display_name + '</div>').appendTo(self.upnextBox); 457 } 458 }, 459 updateFonts:function(){ 460 var self=this; 461 $('>div', self.element).each(function(index, el) { 462 $(el).css('font-size', self._getFontSize() + 'px'); 463 }); 464 }, 465 _getFontSize: function(){ 466 var self=this; 467 return (self.$element.innerWidth() / 350)*5; 468 }, 469 _updateTime:function(){ 470 $('.amri-date', self.timeBox).text(self.currentTime.format('dddd DD MMMM YYYY')); 471 $('.amri-time', self.timeBox).text(self.currentTime.format('h:mma')); 472 }, 473 _formatMinsAsString:function(mins){ 474 var min=parseInt(mins, 10); 475 if(min<60){ 476 return mins+" minutes"; 477 } 478 var hours = Math.floor( min / 60); 479 var minutes = min % 60; 480 if(minutes>0){ 481 if(hours>1){ 482 return hours+' hours '+minutes+' minutes'; 483 }else{ 484 return hours+' hour '+minutes+' minutes'; 485 } 486 }else{ 487 if(hours>1){ 488 return hours+' hours'; 489 }else{ 490 return hours+' hour'; 491 } 492 } 493 } 494 }); 495 496 $.fn[ pluginName ] = function ( options ) { 497 return this.each(function() { 498 if ( !$.data( this, "plugin_" + pluginName ) ) { 499 $.data( this, "plugin_" + pluginName, new Plugin( this, options ) ); 500 } 501 }); 502 }; 503 504})( jQuery, window, document );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.