PageSourceSearch

https://www.faylib.org/js/am-room-picker.js?v=1480

js faylib.org collected 2026-09-24 09:57:10 UTC 55,560 bytes, 1,049 lines download raw bytes

1;
2(function ($, window, document, undefined) {
3	"use strict";
4	var pluginName = "amPatronRoomPicker",
5		defaults = {
6			singleScene: false,
7			animation: true,
8			templates: false,
9			enablePatronNotes: false,
10			enableExpectedAttendees: false,
11			expectedAttendeesRequired: false,
12			emailRequired: false,
13			layoutRequired: false,
14			chargingEnabled: false,
15			ecommerceEnabled: false,
16			pinlessAuth: false,
17			phoneRequired: false,
18			loginRequired: false,
19			useSetupBreakdown: true,
20			adjustSetupBreakdown: false,
21			roomsPatronDetailsReadonly: false,
22			timezone: 'America/New_York',
23			scenes: []
24		};
25	var self = false;
26	// The actual plugin constructor
27	function Plugin(element, options) {
28		self = this;
29		this.element = element;
30		this.$element = $(element);
31		this.settings = $.extend(true, {}, defaults, options);
32		this._defaults = defaults;
33		this._name = pluginName;
34		this.booking = {};
35		self.locations = {};
36		self.rooms = [];
37		self.moving = false;
38		self.singleBranch = false;
39		this.init();
40	}
41	$.extend(true, Plugin.prototype, $.fn['amShared']);
42	$.extend(Plugin.prototype, {
43		init: function () {
44			self._buildInterface();
45			self._bindEvents();
46		},
47		_buildInterface: function () {
48			// self.extras=$('.amrp-room-extras').clone(true).removeAttr('style');
49			self.holder = $('<div class="amrp-holder"></div>').appendTo(self.element);
50			self.screenHeader = $('<div class="amrp-screen-header"></div>').appendTo(self.holder).hide();
51			self.chooseScreen = $('<div class="amrp-choose-screen"></div>').appendTo(self.holder);
52			self.reserveScreen = $('<div class="amrp-reserve-screen"></div>').appendTo(self.holder).hide();
53			self.confirmScreen = $('<div class="amrp-confirm-screen"></div>').appendTo(self.holder).hide();
54			self.screenThankyou = $('<div class="amrp-thanks-screen"></div>').appendTo(self.holder).hide();
55			// self.chooseTabs = $('<ul class="nav nav-tabs" role="tablist">'+
56			// 	'<li class="active"><a href="#bblocation" data-toggle="tab" data-type="location">Book by location</a></li>'+
57			// 	'<li><a href="#bbresource" data-toggle="tab"  data-type="resource">Book by resource</a></li>'+
58			// 	// '<li><a href="#bbavailability" data-toggle="tab"  data-type="availability">Book by availability</a></li>'+
59			// 	'</ul>').appendTo(self.chooseScreen);
60			/*=============start book by location===========*/
61			var topRow = $('<div></div>').addClass('row').appendTo(self.chooseScreen);
62			self.locationBottomRow = $('<div></div>').appendTo(self.chooseScreen).hide();
63			self.locationCol = $('<div></div>').addClass('col-md-4').prepend('<h2>Choose a branch</h2>').appendTo(topRow);
64			self.locationClosed = $('<div>Sorry this branch is closed on the selected day</div>').appendTo(self.chooseScreen).hide();
65			var dateCol = $('<div>' + '<div class="form-group">' + '<div class="input-group amrp-datepicker">' + '<input type="text" class="form-control" />' + '<span class="input-group-addon">' + '<span class="am-events"></span>' + '</span>' + '</div>' + '</div>' + +'</div>').addClass('col-md-4').prepend('<h2>Choose a date</h2>').appendTo(topRow);
66			var timeCol = $('<div></div>').addClass('col-md-4').prepend('<h2>Choose a time</h2>').appendTo(topRow);
67			self.timeRange = $('<div class="amrp-time-range input-group"></div>').appendTo(timeCol);
68			self.startTime = $('<input autocomplete="off" id="stime" class="form-control input-small" value="9:00am"/>').appendTo(self.timeRange)
69			$('<span class="input-group-addon"></span>').text('to').appendTo(self.timeRange);
70			self.endTime = $('<input autocomplete="off" id="etime" class="form-control input-small" value="10:00am"/>').appendTo(self.timeRange);
71			self.locationList = $('<select class="form-control input-sm"></select>').appendTo(self.locationCol);
72			$.getJSON(self.settings.apiServer + '/v1/' + self.settings.client + '/locations', {
73				only_with_external_rooms: 1,
74				only_with_patron_rooms: 1
75			}, function (json, textStatus) {
76				$('<option></option>').text('Please choose....').attr('value', '').appendTo(self.locationList);
77				for (var i = 0; i < json.length; i++) {
78					self.locations[json[i].id] = json[i];
79					$('<option></option>').text(json[i].name).attr('value', json[i].id).appendTo(self.locationList);
80				}
81				if (json.length <= 1) {
82					self.singleBranch = true;
83					self.locationList.val(json[0].id).trigger('change');
84					self.locationCol.hide();
85				}
86			});
87			self.roomList = $('<div class="amrp-rooms-list"></div>').appendTo(self.locationBottomRow);
88			var key = $('<div class="amrp-rooms-key">' + '<span><span class="amrp-rooms-key-i"></span>Available</span>' + '<span><span class="amrp-rooms-key-i" data-status="1"></span>Unavailable</span>' + '</div>').appendTo(self.locationBottomRow);
89			if (self.settings.useSetupBreakdown) {
90				$('<span class="amrp-key-message">Time is needed for library staff to set up and break down some rooms.</span>').prependTo(key);
91				$('<span><span class="amrp-rooms-key-i amrp-setup-breakdown"></span>Setup &amp; Breakdown</span>').appendTo(key);
92			}
93			/*=============end book by location===========*/
94			/*=============start book by resource===========*/
95			self.ressourceCol = $('<div></div>').addClass('col-md-4').prepend('<h2>Choose a resource</h2>').prependTo(topRow).hide();
96			self.resourceList = $('<select class="form-control input-sm"></select>').appendTo(self.ressourceCol);
97			$.getJSON(self.settings.apiServer + '/v1/' + self.settings.client + '/resources', function (json, textStatus) {
98				$('<option></option>').text('Please choose....').attr('value', '').appendTo(self.resourceList);
99				$.each(json, function (index, val) {
100					self.resources[val.id] = val;
101					$('<option></option>').text(val.name).attr('value', val.id).appendTo(self.resourceList);
102				});
103			});
104			/*=============end book by resource===========*/
105			$('<div class="amrp-room-summary-back link amrp-back-to-picker">Back to room picker</div>').appendTo(self.screenHeader);
106			self.reserveRoomSummary = $('<div class="amrp-room-summary"></div>').appendTo(self.screenHeader);
107			self.loginForm = $('<div class="amrp-login-form"></div>').appendTo(self.reserveScreen);
108			if(self.settings.pinlessAuth){
109				if (self.settings.loginRequired) {
110					self._createField('login_card', 'text', 'Library card number', false, '<span data-loading-text="<i class=\'fa fa-circle-o-notch fa-spin\'></i>" class="btn btn-edit amrp-lookup input-group-addon">Login</span>', 'A library card is required to book a room.')
111					.appendTo(self.loginForm);
112				}
113			}else{
114				self._createField('login_card', 'text', 'Library card number', false).addClass('amrp-card').appendTo(self.loginForm);
115				if (self.settings.loginRequired) {
116					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 amrp-lookup input-group-addon">Login</span>', 'A library card is required to book a room.')
117					.appendTo(self.loginForm);
118				} else{
119					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 amrp-lookup 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 fields.').appendTo(self.loginForm);
120				}
121			}
122			$('<div class="amrp-spacer"><div class="events2-error-message"></div></div>').appendTo(self.reserveScreen);
123			self.detailsForm = $('<div></div>').appendTo(self.reserveScreen);
124
125			self._createField('contact_first_name', 'text', 'First name', true).appendTo(self.detailsForm);
126			self._createField('contact_last_name', 'text', 'Last name', true).appendTo(self.detailsForm);
127			// self._createField('zip_code', 'text', 'Zip code', true).appendTo(self.detailsForm);
128			self._createField('contact_email', 'text', 'Email', self.settings.emailRequired, false, 'We\'ll use the email address to confirm the booking and to notify if the room becomes unavailable.').appendTo(self.detailsForm);
129			self._createField('contact_tel', 'text', 'Phone', self.settings.phoneRequired, false).appendTo(self.detailsForm);
130			$('<input type="hidden" id="librarycard" value="">').appendTo(self.detailsForm);
131			self._createField('group_name', 'text', 'Group name', true, false, '(This is <b>not displayed</b> to the public)').appendTo(self.detailsForm);
132			self._createField('booking_title', 'text', 'Booking title', true, false, '(This <b>will be displayed</b> to the public)').appendTo(self.detailsForm);
133			if (self.settings.enableExpectedAttendees) {
134				self._createField('expected_attendees', 'number', 'Attendees', self.settings.expectedAttendeesRequired, false, 'Number of attendees expected.',0).appendTo(self.detailsForm);
135			}
136			if (self.settings.enablePatronNotes) {
137				self._createField('patron_notes', 'textarea', 'Notes', false, false, false).appendTo(self.detailsForm);
138			}
139			self.customQuestions = $('<div class="amrp-custom-questions"></div>').appendTo(self.detailsForm);
140			$('<div class="amrp-buttons">' + '<span class="amrp-required-label"><span class="required-field"></span> field required</span>' + '<button class="btn btn-edit amrp-back-to-picker">Cancel</button>' + '<button class="btn btn-success amrp-to-confirm-screen">Next</button>' + '</div>').appendTo(self.detailsForm);
141			$('<div class="row">' + '<div class="col-sm-3 amrp-booking-room-details"></div>' + '<div class="col-sm-6 amrp-booking-options"></div>' + '<div class="col-sm-3 amrp-booking-final-summary"></div>' + '</div>').appendTo(self.confirmScreen);
142			$('<div class="amrp-buttons">' + '<button class="btn btn-edit amrp-back-to-reserve">Back</button>' + '<button class="btn btn-success amrp-submit-booking" disabled="true">Reserve</button>' + '</div>').appendTo(self.confirmScreen);
143
144			if (self.settings.loginRequired) {
145				self.detailsForm.hide();
146			} else {
147				self.loginForm.hide();
148
149				if(self.settings.librarycardnumber) $('#librarycard', self.detailsForm).val(self.settings.librarycardnumber);
150				if(self.settings.patron.data && self.settings.patron.data.names[0]) $('#contact_first_name', self.detailsForm).val(self.settings.patron.data.names[0].first_name);
151				if(self.settings.patron.data && self.settings.patron.data.names[0]) $('#contact_last_name', self.detailsForm).val(self.settings.patron.data.names[0].last_name);
152				if(self.settings.patron.data && self.settings.patron.data.emails[0]) $('#contact_email', self.detailsForm).val(self.settings.patron.data.emails[0]);
153				if(self.settings.patron.data && self.settings.patron.data.phones[
1530]) $('#contact_tel', self.detailsForm).val(self.settings.patron.data.phones[0]);
154			}
155		},
156		_reset: function () {
157			self.booking = {};
158			$('.amrp-submit-booking').prop('disabled', 'true');
159			$('input[type=text],input[type=password]', self.reserveScreen).val("");
160			if($('option', self.locationList).length>2){
161				self.locationList.val("").trigger('change');
162			}
163		},
164		_bindEvents: function () {
165			// $('a[data-toggle="tab"]', self.chooseTabs).on('shown.bs.tab', function (e) {
166			// 	var target = $(this).attr("data-type");
167			// 	switch(target){
168			// 		case 'resource':
169			// 			self.locationCol.hide();
170			// 			self.ressourceCol.show();
171			// 		break;
172			// 		case 'location':
173			// 		default:
174			// 			self.locationCol.show();
175			// 			self.ressourceCol.hide();
176			// 		break;
177			// 	}
178			// });
179			self.confirmScreen.on('change', '.amrp-bookable-assets-list input, .amrp-bookable-assets-list select', function (event) {
180				event.preventDefault();
181				self._updateBookedAssets();
182			}).on('change', '.agreed', function (event) {
183				event.preventDefault();
184				if ($(this).is(':checked')) {
185					$('.amrp-submit-booking').removeProp('disabled');
186				} else {
187					$('.amrp-submit-booking').prop('disabled', 'true');
188				}
189			});
190			self.holder.on('click', '.amrp-submit-booking', function (event) {
191				event.preventDefault();
192				// if(self.booking.layout.length)
193				var start = moment(self.booking.start_time);
194				var end = moment(self.booking.end_time);
195				var booking = JSON.parse(JSON.stringify(self.booking));
196				booking.room_id = booking.room.id;
197				booking.layout_id = booking.layout.id;
198				booking.customQuestions = JSON.stringify(booking.customQuestions);
199				var room = JSON.parse(JSON.stringify(booking.room));
200				if(self.settings.layoutRequired && room.layouts && room.layouts.length>0 && !self.booking.layout.id){
201					alert('Please select a room layout');
202					return;
203				}
204
205				delete booking.room;
206				delete booking.layout;
207
208				$.post('/reserve?action=submit', booking, function (json, textStatus) {
209					if(!json.ok){
210						alert(json.message || 'Sorry there was a problem creating your booking, please try again.');
211						return;
212					}
213					self.chooseScreen.hide();
214					self.reserveScreen.hide();
215					self.confirmScreen.hide();
216					self.screenHeader.hide();
217					self.screenThankyou.show().empty();
218
219					var extra = '';
220					if (room.restrictions.unmediated) {
221						$('<div class="events2-reg-thanks-stage">' + '<div class="events2-reg-thanks-title">Thank you!</div>' + '<div class="events2-reg-thanks-details">Your booking for the following room is now confirmed: ' + '<div class="events2-reg-thanks-evtitle">' + self.booking.room.title + '</div>' + '<div class="events2-reg-thanks-evlocation">' + self.locations[self.booking.room.location_id].name + '</div>' + '<div class="events2-reg-thanks-evdate">' + start.format('MMMM DD, YYYY') + '</div>' + '<div class="events2-reg-thanks-evtime">' + start.format('h:mma') + ' - ' + end.format('h:mma') + '</div>' + extra + '</div>' + '<div class="events2-reg-thanks-online">' + '<div class="events2-reg-thanks-online-title">View online</div>' + '<div class="events2-reg-thanks-message">You can view the status of your room bookings at:</div>' + '<div><a href="/myreservations">' + window.location.hostname + '/myreservations</a></div>' + '<div class="events2-reg-thanks-ref">This event registration reference: <b>' + json.reference + '<b></div>' + '</div>' + '</div>').appendTo(self.screenThankyou);
222					} else {
223						$('<div class="events2-reg-thanks-stage">' + '<div class="events2-reg-thanks-title">Thank you!</div>' + '<div class="events2-reg-thanks-details">We have received your request for a room booking for: ' + '<div class="events2-reg-thanks-evtitle">' + self.booking.room.title + '</div>' + '<div class="events2-reg-thanks-evlocation">' + self.locations[self.booking.room.location_id].name + '</div>' + '<div class="events2-reg-thanks-evdate">' + start.format('MMMM DD, YYYY') + '</div>' + '<div class="events2-reg-thanks-evtime">' + start.format('h:mma') + ' - ' + end.format('h:mma') + '</div>' + extra + '<div class="events2-reg-thanks-message">Once your reservation has been approved you will receive confirmation.</div>' + '</div>' + '<div class="events2-reg-thanks-online">' + '<div class="events2-reg-thanks-online-title">View online</div>' + '<div class="events2-reg-thanks-message">You can view the status of your room bookings at:</div>' + '<div><a href="/myreservations">' + window.location.hostname + '/myreservations</a></div>' + '<div class="events2-reg-thanks-ref">This event registration reference: <b>' + json.reference + '<b></div>' + '</div>' + '</div>').appendTo(self.screenThankyou);
224					}
225					$('<div class="amrp-room-summary-back link amrp-back-to-picker">Make another reservation</div>').appendTo(self.screenThankyou);
226					self._reset();
227				}, 'json');
228			}).on('click', '.amrp-back-to-picker', function (event) {
229				event.preventDefault();
230				self.chooseScreen.show();
231				self.reserveScreen.hide();
232				self.confirmScreen.hide();
233				self.screenHeader.hide();
234				self.screenThankyou.hide();
235				if (self.settings.loginRequired) {
236					self.loginForm.show();
237					self.detailsForm.hide();
238				}
239			}).on('click', '.amrp-back-to-reserve', function (event) {
240				event.preventDefault();
241				self.chooseScreen.hide();
242				self.reserveScreen.show();
243				self.confirmScreen.hide();
244				self.screenHeader.show();
245			}).on('click', '.amrp-to-confirm-screen', function (event) {
246				event.preventDefault();
247				if (!self.booking.contact) self.booking.contact = {};
248				self.booking.contact.first_name = $('#contact_first_name', self.holder).val();
249				self.booking.contact.last_name = $('#contact_last_name', self.holder).val();
250				self.booking.contact.phone = $('#contact_tel', self.holder).val();
251				self.booking.contact.email = $('#contact_email', self.holder).val();
252				self.booking.contact.librarycard = $('#librarycard', self.holder).val();
253				self.booking.contact.group_name = $('#group_name', self.holder).val();
254				self.booking.contact.booking_title = $('#booking_title', self.holder).val();
255				self.booking.expected_attendees = parseInt($('#expected_attendees', self.holder).val()||0, 10);
256				self.booking.patron_notes = $('#patron_notes', self.holder).val();
257
258				// CH#1957 - CK - Fix for when max is 0 to ignore the message.
259				// 2-26-18 - Fix for wrong room object being used in V1 as compared to other pickers.
260				var maxCapacity = Math.max(parseInt(self.booking.room.capacity_standing, 10), parseInt(self.booking.room.capacity_chairs, 10), parseInt(self.booking.room.capacity_tables, 10));
261				if (self.settings.enforceMaxAttendees && (self.booking.expected_attendees > maxCapacity) && maxCapacity > 0) {
262					$('.events2-error-message', self.holder).text('Expected attendees exceeds capacity').fadeIn().delay(3000).fadeOut();
263					$('#expected_attendees', self.holder).addClass('amrp-missing-field');
264					return;
265				}
266				self.booking.customQuestions = {};
267				var reemail = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
268				var rephone = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im;
269				if (!self._isFormClean(self.reserveScreen)) {
270					$('.events2-error-message', self.holder).text('Please enter information for all required fields').fadeIn().delay(3000).fadeOut();
271					return;
272				}
273				if (!$('#contact_email', self.holder).prop('disabled') && self.booking.contact.email.length > 0 && !reemail.test(self.booking.contact.email)) {
274					$('.events2-error-message', self.holder).text('Please enter a valid email address').fadeIn().delay(3000).fadeOut();
275					$('#contact_email', self.holder).addClass('amrp-missing-field');
276					return;
277				}
278				if (!$('#contact_tel', self.holder).prop('disabled') && self.booking.contact.phone.length > 0 && !rephone.test(self.booking.contact.phone)) {
279					$('.events2-error-message', self.holder).text('Please enter a valid phone number').fadeIn().delay(3000).fadeOut();
280					$('#contact_tel', self.holder).addClass('amrp-missing-field');
281					return;
282				}
283				if (self.booking.room.custom_fields && self.booking.room.custom_fields.fields && self.booking.room.custom_fields.fields.length > 0) {
284					for (var i = self.booking.room.custom_fields.fields.length - 1; i >= 0; i--) {
285						var field = self.booking.room.custom_fields.fields[i];
286						var val = false;
287						switch (field.type) {
288							case 'checkbox':
289								val = $('#' + field.name, self.customQuestions).is(':checked');
290								break;
291							default:
292								val = $('#' + field.name, self.customQuestions).val();
293								break;
294						}
295						switch (field.validate) {
296							case 'custom':
297								var flags = field.regex.replace(/.*\/([gimy]*)$/, '$1');
298								var pattern = field.regex.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
299								var regex = new RegExp(pattern, flags);
300								if (val.length > 0 && !regex.test(val)) {
301									$('.events2-error-message', self.holder).text('The information entered is not valid').fadeIn().delay(3000).fadeOut();
302									$('#' + field.name, self.holder).addClass('amrp-missing-field');
303									return;
304								}
305							break;
306							case 'email':
307								if (val.length > 0 && !reemail.test(val)) {
308									$('.events2-error-message', self.holder).text('Please enter a valid email address').fadeIn().delay(3000).fadeOut();
309									$('#' + field.name, self.holder).addClass('amrp-missing-field');
310									return;
311								}
312								break;
313							case 'phone':
314								if (val.length > 0 && !rephone.test(val)) {
315									$('.events2-error-message', self.holder).text('Please enter a valid phone number').fadeIn().delay(3000).fadeOut();
316									$('#' + field.name, self.holder).addClass('amrp-missing-field');
317									return;
318								}
319								break;
320						}
321						if (val) self.booking.customQuestions[field.name] = val;
322					}
323				}
324				self._confirmScreen();
325			}).on('click', '.amrp-lookup', function (event) {
326				event.preventDefault();
327				var b = $(this).button('loading');
328
329				// CH#1942 - Trim the card/pin fields to prevent blanks.
330				var card = $.trim($('#login_card', self.holder).val());
331				var loginData  = {u:card, 'type':'reserve'};
332				if(!self.settings.pinlessAuth){
333					loginData.p = $.trim($('#login_pin', self.holder).val());
334				}
335				$.getJSON(self.settings.apiServer + '/v1/' + self.settings.client + '/patron', loginData, function (json, textStatus) {
336					if (json.result == 'ok') {
337						if(!self.settings.pinlessAuth){
338							if (json.data.names.length > 0) {
339								$('#contact_first_name', self.holder).val(json.data.names[0].first_name);
340								$('#contact_last_name', self.holder).val(json.data.names[0].last_name);
341								if(self.settings.roomsPatronDetailsReadonly){
342									$('#contact_first_name', self.holder).prop('disabled', true);
343									$('#contact_last_name', self.holder).prop('disabled', true);
344								}
345							}
346							if (json.data.phones.length > 0) {
347								$('#contact_tel', self.holder).val(json.data.phones[0]);
348								if(self.settings.roomsPatronDetailsReadonly){
349									$('#contact_tel', self.holder).prop('disabled', true);
350								}
351							}
352							if (json.data.emails.length > 0) {
353								$('#contact_email', self.holder).val(json.data.emails[0]);
354								if(self.settings.roomsPatronDetailsReadonly){
355									$('#contact_email', self.holder).prop('disabled', true);
356								}							}
357						}
358						if (json.data.barcodes.length > 0) {
359							$('#librarycard', self.holder).val(json.data.barcodes[0]);
360						}
361						if (self.settings.loginRequired) {
362							self.loginForm.hide();
363							self.detailsForm.show();
364						}
365					} else {
366						var msg = self.settings.pinlessAuth?'Sorry, card number not found':'Sorry, pin or card number not found';
367						if( json.message && json.message.length>0 ){
368							msg = json.message;
369						}
370						$('.events2-error-message', self.holder).text(json.message||msg).fadeIn().delay(3000).fadeOut();
371					}
372					b.button('reset');
373				}).error(function () {
374					$('.events2-error-message', self.holder).text('Sorry, there was a problem making the request').fadeIn().delay(3000).fadeOut();
375					b.button('reset');
376				});
377			});
378			self.reserveScreen.on('focus', '.amrp-missing-field', function (event) {
379				$(this).removeClass('amrp-missing-field');
380			});
381			self.datePicker = $('.amrp-datepicker', self.holder);
382			self.startTime.timepicker({
383				minuteStep: 5,
384				showInputs: false
385			}).on('changeTime.timepicker', function (event) {
386				if (!self.moving) self._changed();
387			});
388			self.endTime.timepicker({
389				minuteStep: 5,
390				showInputs: false
391			}).on('changeTime.timepicker', function (event) {
392				if (!self.moving) self._changed();
393			});
394			self.datePicker.datetimepicker({
395				icons: {
396					previous: 'fa fa-chevron-left',
397					next: 'fa fa-chevron-right'
398				},
399				minDate:  moment().format('MM/DD/YYYY'),
400				defaultDate: moment().format('MM/DD/YYYY'),
401				format: 'LL'
402			}).on('dp.change', function (event) {
403				self._update();
404			});
405			self.locationList.on('change', function (event) {
406				event.preventDefault();
407				self.openingHours = false;
408				self._update();
409			});
410			self.resourceList.on('change', function (event) {
411				event.preventDefault();
412				self._changed();
413			});
414			interact('.amrp-booking', {
415				context: self.roomList[0]
416			}).draggable({
417				restrict: {
418					restriction: "parent",
419					endOnly: false,
420					elementRect: {
421						top: 0,
422						left: 0,
423						bottom: 1,
424						right: 1
425					}
426				},
427				onstart: function (event) {
428					self.moving = true;
429				},
430				onmove: function (event) {
431					var $target = $(event.target);
432					var start = (parseFloat($target.css('left')) || 0)
433					var x = start + event.dx;
434					$('.amrp-booking', self.roomList).css({
435						left: x + 'px'
436					});
437					self._updateBookingTime();
438					self._updateButtons();
439				},
440				onend: function (event) {
441					self.moving = false;
442					self._updateCurrent(event);
443				}
444			});
445			self.roomList.on('click', '.amrp-ava-btn', function (event) {
446				event.preventDefault();
447				self.booking.room = $(this).closest('.amrp-room').data('room');
448				self.booking.contact = {};
449				self.booking.layout = {};
450				self.booking.assets = [];
451				self._reserveScreen();
452			}).on('mouseenter', '.amrp-room-title img', function (event) {
453				event.preventDefault();
454				var room = $(this).closest('.amrp-room').data('room');
455				$(this).qtip({
456					overwrite: false, // Don't overwrite tooltips already bound
457					content: {
458						text: function (event, api) {
459							var $data = $('<div></div>');
460							var $top = $('<div class="amrp-room-popup-top"></div>').appendTo($data);
461							var $row = $('<div class="row"></div>').appendTo($data);
462							var $left = $('<div class="col-sm-7"></div>').appendTo($row);
463							var $right = $('<div class="col-sm-5"></div>').appendTo($row);
464							var min = parseInt(room.restrictions.minimum_booking_time, 10);
465							var max = parseInt(room.restrictions.maximum_booking_time, 10);
466							var near = parseInt(room.restrictions.nearest_booking_days, 10);
467							var far = parseInt(room.restrictions.furthest_booking_days, 10);
468
469							if (room.image && room.image.length > 0) {
470								$('<img/>').attr('src', '//' + self.settings.client + '.libnet.info/images/roombooking/' + self.settings.client + '/' + room.image).attr('alt', room.title).appendTo(img);
471							}
472
473							$('<h3></h3>').text(room.title).appendTo($top);
474							if (max > 0) {
475								$('<div class="amrp-room-popup-item"><span class="amrp-room-popup-info">'+ self.settings.roomsMaxBookingMessage.replace('{{amount}}', self._formatMinsAsString(max)) +'</span></div>').appendTo($top);
476							}
477							if (min > 0) {
478								$('<div class="amrp-room-popup-item"><span class="amrp-room-popup-info">'+ self.settings.roomsMinBookingMessage.replace('{{amount}}', self._formatMinsAsString(min)) +'</span></div>').appendTo($top);
479							}
480							if (near > 0) {
481								$('<div class="amrp-room-popup-item"><span class="amrp-room-popup-info">' + self.settings.roomsNearBookingMessage.replace('{{amount}}', near) + '</span></div>').appendTo($top);
482							}
483							if (far > -1) {
484								$('<div class="amrp-room-popup-item"><span class="amrp-room-popup-info">' + self.settings.roomsFarBookingMessage.replace('{{amount}}', far) + '</span></div>').appendTo($top);
485							}
486							if (room.length || room.width || room.area) {
487								$('<div class="amrp-rrom-popup-title">Room size</div>').appendTo($left);
488								if (room.length) $('<div class="amrp-room-popup-item"><span class="amrp-room-popup-label">Length</span><span class="amrp-room-popup-value">' + room.length + ' ft</span></div>').appendTo($left);
489								if (room.width) $('<div class="amrp-room-popup-item"><span class="amrp-room-popup-label">Width</span><span class="amrp-room-popup-value">' + room.width + ' ft</span></div>').appendTo($left);
490								if (room.area) $('<div class="amrp-room-popup-item"><span class="amrp-room-popup-label">Area</span><span class="amrp-room-popup-value">' + room.area + ' sq ft</span></div>').appendTo($left);
491							}
492							// #4762 - CK
493							var maxCap = Math.max(room.capacity_standing, room.capacity_chairs, room.capacity_tables, 0);
494							$('<div class="events2-room-popup-title">
494Room capacity</div>').appendTo($left);
495							$('<div class="events2-room-popup-item"><span class="events2-room-popup-label">Max Capacity</span><span class="events2-room-popup-value">' + maxCap + '</span></div>').appendTo($left);
496
497							var $bottom = $('<div class="amrp-room-popup-bottom"></div>').appendTo($data);
498							if (room.description) $('<div class="amrp-room-popup-description"><div class="amrp-rrom-popup-title">Room description</div>' + room.description + '</div>').appendTo($bottom);
499							if (room.assets && room.assets.length > 0) {
500								$('<div class="amrp-rrom-popup-title">Room features</div>').appendTo($bottom);
501								$.each(room.assets, function (index, asset) {
502									var a = $('<div class="amrp-room-popup-asset"></div>').appendTo($bottom);
503									$('<span class="amrp-room-popup-asset-name">' + asset.name + '</span>').appendTo(a);
504									if (asset.qty > 1) {
505										$('<span class="amrp-room-popup-asset-qty">' + asset.qty + '</span>').appendTo(a);
506									}
507									if (asset.description) {
508										$('<span class="amrp-room-popup-asset-description"> (' + asset.description + ') </span>').appendTo(a);
509									}
510								});
511							}
512							if (room.layouts && room.layouts.length > 0) {
513								var layoutsList = $('<div class="amrp-room-popup-bottom"></div>').appendTo($data);
514								$.each(room.layouts, function (index, layout) {
515									layoutsList.append(self._getLayoutDetailView(layout));
516									layoutsList.append('<br>');
517								});
518							} else if (self.settings.useSetupBreakdown) {
519								if (room.setup_time && parseInt(room.setup_time) > 0) $('<div class="amrp-room-popup-item"><span class="amrp-room-popup-label">Setup time</span><span class="amrp-room-popup-value">' + room.setup_time + ' minutes</span></div>').appendTo($data);
520								if (room.breakdown_time && parseInt(room.breakdown_time) > 0) $('<div class="amrp-room-popup-item"><span class="amrp-room-popup-label">Breakdown time</span><span class="amrp-room-popup-value">' + room.breakdown_time + ' minutes</span></div>').appendTo($data);
521							}
522							return $data;
523						}
524					},
525					position: {
526						my: 'left top',
527						at: 'top right'
528					},
529					style: {
530						classes: 'qtip-shadow qtip-light qtip-rounded amrp-room-popup'
531					},
532					show: {
533						delay: 500,
534						ready: true // Show immediately - important!
535					},
536					hide: {
537						delay: 200,
538						fixed: true, // <--- add this
539						effect: function () {
540							$(this).fadeOut(250);
541						}
542					},
543				});
544			});
545		},
546		_updateBookingTime: function () {
547			var elementTime = self._getElTime($('.amrp-booking', self.roomList).first());
548			var start = moment(elementTime.from);
549			var end = moment(elementTime.to);
550			self.booking.start_time = start.format('YYYY-MM-DD HH:mm:ss');
551			self.booking.end_time = end.format('YYYY-MM-DD HH:mm:ss');
552			self.startTime.timepicker('setTime', start.format('h:mma'));
553			self.endTime.timepicker('setTime', end.format('h:mma'));
554			return true;
555		},
556		getSecondsFromMidnight: function (time, date) {
557			return moment(date+' '+time, 'YYYY-MM-DD h:mma').diff(moment(date, 'YYYY-MM-DD').startOf('day'), 'seconds');
558		},
559		_updateCurrent: function (event) {
560			var $target = $(event.target);
561			var elementTime = self._getElTime($(event.target));
562			self.booking.start_time = elementTime.from.format('YYYY-MM-DD HH:mm:ss');
563			self.booking.end_time = elementTime.to.format('YYYY-MM-DD HH:mm:ss');
564			self._renderList();
565		},
566		_update: function () {
567			var loc = self.locationList.val();
568			if (loc == '') {
569				self.locationBottomRow.hide();
570			} else {
571				var dateString = self.datePicker.data("DateTimePicker").date().clone().startOf('day').format('YYYY-MM-DD');
572				$.getJSON(self.settings.apiServer + '/v2/' + self.settings.client + '/rooms/' + loc, {
573					date: dateString,
574					external_only: 1,
575					classtype: 1
576				}, function (json, textStatus) {
577					self.rooms = json;
578					if (self.settings.useSetupBreakdown) {
579						$.each(self.rooms, function (index, room) {
580							$.each(room.bookings, function (index, booking) {
581								if (room.setup_time) {
582									booking.start_time = moment(booking.start_time).add(room.setup_time * -1, 'minutes').format('YYYY-MM-DD HH:mm:ss');
583								}
584								if (room.breakdown_time) {
585									booking.end_time = moment(booking.end_time).add(room.breakdown_time, 'minutes').format('YYYY-MM-DD HH:mm:ss');
586								}
587							});
588						});
589					}
590					$.getJSON(self.settings.apiServer + '/v1/' + self.settings.client + '/opening-hours/' + dateString + '/1', function (openingTimes, textStatus) {
591						self.openingHours = openingTimes[loc][dateString];
592						var d = moment(dateString).startOf('day');
593						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');
594						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');
595						if (self.openingHours.raw_open_time == self.openingHours.raw_close_time) {
596							self.locationBottomRow.hide();
597							self.locationClosed.show();
598						} else {
599							self.locationBottomRow.show();
600							self.locationClosed.hide();
601							self._changed();
602							self._renderList();
603						}
604					});
605				});
606			}
607		},
608		_changed: function () {
609			var d = self.datePicker.data("DateTimePicker").date().clone().startOf('day');
610			var s = self.getSecondsFromMidnight(self.startTime.val(), d.format('YYYY-MM-DD'));
611			var e = self.getSecondsFromMidnight(self.endTime.val(), d.format('YYYY-MM-DD'));
612			var new_start_time = d.clone().add(Math.min(s, e), 'seconds');
613			var new_end_time = d.clone().add(Math.max(s, e), 'seconds');
614			var badTime = e <= s;
615
616			if (badTime) {
617				self.moving = true;
618				self.endTime.timepicker('setTime', d.clone().add(s, 'seconds').add(1, 'hour').format('h:mma'));
619				e = self.getSecondsFromMidnight(self.endTime.val(), d.format('YYYY-MM-DD'));
620				self.moving = false;
621			}
622
623			self.booking.start_time = d.clone().add(Math.min(s, e), 'seconds').format('YYYY-MM-DD HH:mm:ss');
624			self.booking.end_time = d.clone().add(Math.max(s, e), 'seconds').format('YYYY-MM-DD HH:mm:ss');
625			self._renderList();
626		},
627		_renderList: function () {
628			self.roomList.empty();
629			var markers = $('<div class="amrp-roomlist-markers"></div>').appendTo(self.roomList);
630			var w = markers.width() - 22;
631			for (var i = 3; i < 24; i += 3) {
632				var left = ((w / 24) * i);
633				$('<span class="l"></span>').css('left', left + 'px').appendTo(markers);
634				left -= 18;
635				$('<span class="t">' + moment(i + ':00', ["HH:mm"]).format("h:mma") + '</span>').css('left', left + 'px').appendTo(markers);
636			}
637			$.each(self.rooms, function (index, val) {
638				var b = val.bookings;
639				// CH#2461 - This fix below was already here but applies to this ticket.
640				if(val.restrictions.viewable===false) return;
641				var room = $('<div class="amrp-room"></div>').data('bookings', b).data('room', val).attr('data-roomid', val.id).appendTo(self.roomList);
642				var title = $('<div class="amrp-room-title"></div>').text(val.title).appendTo(room);
643				if (val.image.length > 0) {
644					$('<img/>').attr('src', '//' + self.settings.client + '.libnet.info/images/roombooking/' + self.settings.client + '/' + val.image).attr('alt', val.title).prependTo(title);
645				}
646				var timeline = $('<div class="amrp-room-timeline"><div class="amrp-booking"><div class="amrp-setup"></div>&nbsp;<div class="amrp-breakdown"></div></div></div>').appendTo(room);
647				$('<div class="amrp-room-status"><button class="btn btn-info amrp-ava-btn">Select</button><div class="amrp-reason"></div></div>').appendTo(room);
648				$.each(b, function (index, booking) {
649					$('<div class="amrp-ex-booking" data-status="1">&nbsp;</div>').data('booking', booking).attr('data-booking-id', booking.id).appendTo(timeline);
650				});
651				if (val.sub_rooms && val.sub_rooms.length > 0) {
652					room.wrap('<div class="amrp-parent-room"></div>').attr('data-roomid', val.id);
653					room.parent().append('<div class="amrp-sub-room"></div>');
654				}
655				var opt;
656				if(self.singleBranch){
657					opt = $('<div class="amrp-room-opening-hours">Library hours</div>').appendTo(timeline);
658				}else{
659					opt = $('<div class="amrp-room-opening-hours">Branch hours</div>').appendTo(timeline);
660				}
661				self._updateEl({
662					start_time: self.openingHours.raw_open_time,
663					end_time: self.openingHours.raw_close_time
664				}, opt);
665			});
666			$.each(self.rooms, function (index, val) {
667				$.each(val.childRooms, function (cIndex, child) {
668					$.each(val.bookings, function (bIndex, booking) {
669						self._addBooking(booking, child);
670					});
671				});
672				$.each(val.parentRooms, function (pIndex, parent) {
673					$.each(val.bookings, function (bIndex, booking) {
674						self._addBooking(booking, parent);
675					});
676				});
677			});
678			$('>.amrp-parent-room', self.roomList).each(function (index, el) {
679				var subs = $('.amrp-sub-room', el);
680				var r = $('>.amrp-room', el).first().data('room');
681				$.each(r.sub_rooms, function (index, val) {
682					$('>.amrp-room[data-roomid=' + val + ']', self.roomList).prependTo(subs);
683				});
684			});
685			$('.amrp-booking', self.roomList).each(function (index, el) {
686				self._updateEl(self.booking, $(el));
687			});
688			self._updateExBookings(self.roomList);
689			self._updateButtons();
690		},
691		_addBooking: function (booking, room_id) {
692			var timeline = $('[data-roomid=' + room_id + ']').find('.amrp-room-timeline').first();
693			if ($('[data-booking-id=' + booking.id + ']', timeline).length === 0 && parseInt(booking.status, 10) !== 3 && parseInt(booking.status, 10) !== 2) {
694				$('<div class="amrp-ex-booking" data-status="' + booking.status + '">&nbsp;</div>').data('booking', booking).attr('data-booking-id', booking.id).appendTo(timeline);
695			}
696		},
697		_formatMinsAsString: function (mins) {
698			var min = parseInt(mins, 10);
699			if (min < 60) {
700				return mins + " minutes";
701			}
702			var hours = Math.floor(min / 60);
703			var minutes = min % 60;
704			if (minutes > 0) {
705				if (hours > 1) {
706					return hours + ' hours ' + minutes + ' minutes';
707				} else {
708					return hours + ' hour ' + minutes + ' minutes';
709				}
710			} else {
711				if (hours > 1) {
712					return hours + ' hours';
713				} else {
714					return hours + ' hour';
715				}
716			}
717		},
718		_updateButtons: function () {
719			$('.amrp-ava-btn', self.roomList).each(function (index, el) {
720				var reason = $('.amrp-reason', $(el).closest('.amrp-room')).text('').hide();
721				var elementTime = self._getElTime($('.amrp-booking', self.roomList).first());
722				var b = $(el).closest('.amrp-room').find('.amrp-ex-booking');
723				var bookings = [];
724				for (var i = 0; i < b.length; i++) {
725					bookings.push($(b[i]).data('booking'));
726				}
727				var roomData = $(el).closest('.amrp-room').data('room');
728				if (!roomData.restrictions.bookable) {
729					$(el).text('Please call').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
730					reason.text(self.settings.roomsNotBookableMessage.replace('{{branch_number}}', self.locations[roomData.location_id].tel)).show();
731					return;
732				}
733				var min = parseInt(roomData.restrictions.minimum_booking_time, 10) || 0;
734				var max = parseInt(roomData.restrictions.maximum_booking_time, 10) || 0;
735				var near = parseInt(roomData.restrictions.nearest_booking_days, 10) || 0;
736				var far = parseInt(roomData.restrictions.furthest_booking_days, 10) || -1;
737
738				var day_start_buffer = parseInt(roomData.restrictions.day_start_buffer, 10) || 0;
739				var day_end_buffer = parseInt(roomData.restrictions.day_end_buffer, 10) || 0;
740
741				var open = moment(self.openingHours.raw_open_time);
742				var close = moment(self.openingHours.raw_close_time);
743
744				var roomOpens = open.clone().add(day_start_buffer, 'minutes');
745				var roomCloses = close.clone().add(day_end_buffer * -1, 'minutes');
746
747				if (self.settings.useSetupBreakdown) {
748					roomOpens.add(roomData.setup_time * -1, 'minutes');
749					roomCloses.add(roomData.breakdown_time, 'minutes');
750
751					elementTime.to.add(roomData.breakdown_time, 'minutes');
752					elementTime.from.add(roomData.setup_time * -1, 'minutes');
753				}
754				if (elementTime.from < roomOpens || elementTime.to > roomCloses) {
755					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
756					reason.text('The room is closed at this time.').show();
757					return;
758				}
759
760
761				if (elementTime.from < open || elementTime.to > close) {
762					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
763					return;
764				}
765				var duration = moment.duration(elementTime.to.diff(elementTime.from)).asMinutes();
766				if (max > 0 && duration > max) {
767					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
768
769					reason.text(self.settings.roomsMaxBookingMessage.replace('{{branch_number}}', self.locations[roomData.location_id].tel).replace('{{amount}}', self._formatMinsAsString(max))).show();
770					return;
771				}
772				if (min > 0 && duration < min) {
773					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
774					reason.text(self.settings.roomsMinBookingMessage.replace('{{branch_number}}', self.locations[roomData.location_id].tel).replace('{{amount}}', self._formatMinsAsString(min))).show();
775					return;
776				}
777				var when = Math.round(moment.duration(elementTime.to.diff(moment())).asDays());
778				if (near > 0 && when < near) {
779					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
780					reason.text(self.settings.roomsNearBookingMessage.replace('{{branch_number}}', self.locations[roomData.location_id].tel).replace('{{amount}}', near)).show();
781					return;
782				}
783				if (far > -1 && when > far) {
784					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
785					reason.text(self.settings.roomsFarBookingMessage.replace('{{branch_number}}', self.locations[roomData.location_id].tel).replace('{{amount}}', far)).show();
786					return;
787				}
788				if (moment(self.booking.start_time).isBefore(moment())) {
789					$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
790					return;
791				}
792				$(el).text($(el).closest('.amrp-room').hasClass('amrp-room-selected') ? 'Chosen' : 'Select').removeClass('btn-info btn-edit').addClass($(el).closest('.amrp-room').hasClass('amrp-room-selected') ? 'btn-primary' : 'btn-info').removeProp('disabled');
793
794				$.each(bookings, function (index, val) {
795					var lStart = moment(val.start_time);
796					var lEnd = moment(val.end_time);
797					if (lStart < elementTime.to && lEnd > elementTime.from) {
798						$(el).text('Unavailable').removeClass('btn-info btn-edit').addClass('btn-edit').prop('disabled', true);
799					}
800				});
801			});
802		},
803		_getElTime: function (el) {
804			var mmtMidnight = moment(self.booking.start_time).clone().startOf('day');
805			var width = $('.amrp-room-timeline', self.roomList).width();
806			var roomData = $(el).closest('.amrp-room').data('room');
807			var x = (parseFloat(el.css('left')) || 0);
808			var w = el.width();
809			var startMin = Math.floor(x / (width / 1440) / 5) * 5;
810			var endMin = startMin + (Math.ceil(w / (width / 1440) / 5) * 5);
811			var start = mmtMidnight.clone().add(startMin, 'minutes');
812			var end = mmtMidnight.clone().add(endMin, 'minutes');
813			return {
814				from: start,
815				to: end
816			};
817		},
818		_updateEl: function (time, el) {
819			if (!time) return;
820			var from = moment(time.start_time);
821			var to = moment(time.end_time);
822			var roomData = $(el).closest('.amrp-room').data('room');
823			var mmtMidnight = from.clone().startOf('day');
824			var start = from.clone().diff(mmtMidnight, 'minutes');
825			var end = to.clone().diff(mmtMidnight, 'minutes');
826			var width = $(el).parent().width();
827			var x = Math.ceil(start * (width / 1440));
828			var w = Math.floor(end * (width / 1440)) - x;
829			if (self.settings.useSetupBreakdown && roomData && roomData.breakdown_time) {
830				var bw = roomData.breakdown_time * (width / 1440);
831				$('.amrp-breakdown', el).css({
832					width: bw + 'px',
833					right: (bw * -1) + 'px'
834				});
835			}
836			if (self.settings.useSetupBreakdown && roomData && roomData.setup_time) {
837				var sw = roomData.setup_time * (width / 1440);
838				$('.amrp-setup', el).css({
839					width: sw + 'px',
840					left: (sw * -1) + 'px'
841				});
842			}
843			el.css({
844				left: x + 'px'
845			});
846			el.width(w + 'px');
847		},
848		_updateExBookings: function (list) {
849			$('.amrp-ex-booking', list).each(function (index, el) {
850				var booking = $(this).data('booking');
851				self._updateEl(booking, $(el));
852			});
853		},
854		_reserveScreen: function () {
855			var customQuestions = self.customQuestions.empty();
856			var summary = self.reserveRoomSummary.empty();
857			$('<div class="amrp-room-name"></div>').html('<i class="am-locations"></i><b>' + self.booking.room.title + '</b>, ' + self.locations[self.booking.room.location_id].name).appendTo(summary);
858			var start = moment(self.booking.start_time);
859			var end = moment(self.booking.end_time);
860			$('<div class="amrp-room-time"></div>').html('<b>' + start.format('h:mma') + ' - ' + end.format('h:mma') + '</b> ' + start.format('MMMM DD, YYYY')).appendTo(summary);
861			if (self.booking.room.custom_fields && self.booking.room.custom_fields.fields && self.booking.room.custom_fields.fields.length > 0) {
862				$.each(self.booking.room.custom_fields.fields, function (index, val) {
863					var field = self._createField(val.name, val.type, val.title, val.required, false, val.helpText).data('fieldSettings', val).addClass('amrp-custom-field').appendTo(customQuestions);
864					if (val.type === 'select') {
865						var select = $('select', field);
866						if (!val.required) {
867							$('<option>Please choose...</option>').appendTo(select);
868						}
869						var options = val.options.trim().split(/\r?\n/);
870						for (var i = 0; i < options.length; i++) {
871							$('<option>' + options[i] + '</option>').appendTo(select);
872						}
873					}
874				});
875			}
876			//
877			self.chooseScreen.hide();
878			self.confirmScreen.hide();
879			self.reserveScreen.show();
880			self.screenHeader.show();
881		},
882		_confirmScreen: function () {
883			self.confirmScreen.show();
884			self.screenHeader.show();
885			self.chooseScreen.hide();
886			self.reserveScreen.hide();
887			self._updateFinalSummary();
888			var roomDetails = $('.amrp-booking-room-details', self.confirmScreen).empty();
889			var bookingOptions = $('.amrp-booking-options', self.confirmScreen).empty();
890			var room = self.booking.room;
891			if (room.image && room.image.length > 0) {
892				$('<img/>').attr('src', '//' + self.settings.client + '.libnet.info/images/roombooking/' + self.settings.client + '/' + room.image).attr('alt', room.title).appendTo(roomDetails);
893			}
894			$('<div class="amrp-booking-room-desc"></div>').html(room.description).appendTo(roomDetails);
895			if (room.assets && room.assets.length > 0) {
896				var included = $('<div class="amrp-booking-room-included"><b>What\'s included?<b><br></div>').appendTo(roomDetails);
897				var includedList = $('<ul><ul>').appendTo(included);
898				$.each(room.assets, function (index, asset) {
899					var a = $('<li></li>').appendTo(includedList);
900					if (asset.qty > 1) {
901						$('<span class="amrp-booking-room-asset-qty">' + asset.qty + '&nbsp;</span>').appendTo(a);
902					}
903					$('<span class="amrp-booking-room-asset-name">' + asset.name + '</span>').appendTo(a);
904					if (asset.description) {
905						$('<span class="amrp-booking-room-asset-description"> (' + asset.description + ') </span>').appendTo(a);
906					}
907				});
908			}
909			var bookingPreview = $('<div class="amrp-booking-preview"></div>').appendTo(bookingOptions);
910			var markers = $('<div class="amrp-roomlist-markers"></div>').appendTo(bookingPreview);
911			var w = markers.width();
912			for (var i = 3; i < 24; i += 3) {
913				var left = ((w / 24) * i);
914				$('<span class="l"></span>').css('left', left + 'px').appendTo(markers);
915				left -= 18;
916				$('<span class="t">' + moment(i + ':00', ["HH:mm"]).format("h:mma") + '</span>').css('left', left + 'px').appendTo(markers);
917			}
918			var b = room.bookings;
919			var roomRow = $('<div class="amrp-room"></div>').data('bookings', b).data('room', room).attr('data-roomid', room.id).appendTo(bookingPreview);
920			var timeline = $('<div class="amrp-room-timeline"><div class="amrp-booking"><div class="amrp-setup"></div>&nbsp;<div class="amrp-breakdown"></div></div></div>').appendTo(roomRow);
921			$.each(b, function (index, booking) {
922				$('<div class="amrp-ex-booking" data-status="1">&nbsp;</div>').data('booking', booking).attr('data-booking-id', booking.id).appendTo(timeline);
923			});
924			self._updateEl(self.booking, $('.amrp-booking', bookingPreview));
925			self._updateExBookings(bookingPreview);
926			// self._updateButtons();
927
928			if (room.layouts && room.layouts.length > 0) {
929				var layoutSelect = self._createField('layout_id', 'select', 'Layout', false).appendTo(bookingOptions).find('select');
930				$('<option value="">Please choose a layout</option>').appendTo(layoutSelect);
931				$.each(room.layouts, function (index, layout) {
932					$('<option>' + layout.name + '</option>').val(layout.id).data('details', layout).appendTo(layoutSelect);
933				});
934				var layoutDetail = $('<div></div>').appendTo(bookingOptions);
935				layoutSelect.on('change', function (event) {
936					event.preventDefault();
937					layoutDetail.empty();
938					self.booking.layout = $(':selected', this).data('details');
939					self._updateFinalSummary();
940					if (self.booking.layout)layoutDetail.append(self._getLayoutDetailView(self.booking.layout));
941				});
942			}
943
944			var assets = $('<div class="amrp-booking-final-summary-assets"></div>').appendTo(bookingOptions);
945			$.getJSON(self.settings.apiServer + '/v2/' + self.settings.client + '/rooms/assets/' + room.location_id + '/' + room.id+'?start='+self.booking.start_time+'&end='+self.booking.end_time, function (json, textStatus) {
946				self.assets = [];
947				if (json.length > 0) {
948					$('<div class="amrp-booking-final-summary-subheading">Bookable resources</div>').appendTo(assets);
949					var assetList = $('<ul class="amrp-bookable-assets-list"></ul>').appendTo(assets);
950					$.each(json, function (index, val) {
951						if(parseInt(val.quantity)>0){
952							self.assets[val.id] = val;
953							var li = $('<li><label><input type="checkbox">&nbsp;&nbsp;' + val.name + '</label></li>').data('details', val).appendTo(assetList);
954							if (parseInt(val.quantity) < parseInt(val.booking_max))val.booking_max = val.quantity;
955							if (parseInt(val.booking_max) > 1) {
956								$('<span> (' + val.booking_max + ' available)</span>').appendTo($('label', li));
957								var s = $('<select></select>').appendTo(li);
958								for (var i = parseInt(val.booking_min); i <= parseInt(val.booking_max); i++) {
959									$('<option>' + i + '</option>').appendTo(s);
960								}
961							}
962						}else{
963							var li = $('<li class="amnp-none-available"><label><input type="checkbox" disabled>&nbsp;&nbsp;' + val.name + ' (none available at the chosen time)</label></li>').data('details', val).appendTo(assetList);
964						}
965					});
966				}
967			});
968			if (room.tc_link && room.tc_link.length > 0) {
969				$('<label><input class="agreed" type="checkbox"> I have read and agree to the room booking <a target="_blank" href="' + room.tc_link + '">terms and conditions</a></label>').appendTo(bookingOptions);
970			} else {
971				$('<label><input class="agreed" type="checkbox"> I have read and agree to the room booking terms and conditions</label>').appendTo(bookingOptions);
972			}
973		},
974		_getLayoutDetailView: function (layout) {
975			var view = $('<div class="amrp-room-layout-details"></div>');
976			$('<img>').attr('src', 'http://' + self.settings.client + '.libnet.info/images/roomlayouts/' + layout.image).appendTo(view);
977			$('<div class="amrp-room-layout-title"></div>').text(layout.name).appendTo(view);
978			$('<div class="amrp-room-layout-capacity"><span>Capacity:</span> ' + layout.capacity + '</div>').appendTo(view);
979			if (self.settings.useSetupBreakdown) {
980				if (parseInt(layout.setup_time, 10) > 0)$('<div class="amrp-room-layout-capacity"><span>Setup:</span> ' + layout.setup_time + '</div>').appendTo(view);
981				if (parseInt(layout.breakdown_time, 10) > 0)$('<div class="amrp-room-layout-capacity"><span>Breakdown:</span> ' + layout.breakdown_time + '</div>').appendTo(view);
982			}
983			$('<div class="amrp-room-layout-description"></div>').text(layout.description).appendTo(view);
984			return view;
985
986		},
987		_updateBookedAssets: function () {
988			self.booking.assets = [];
989			self._updateFinalSummary();
990			$('.amrp-bookable-assets-list li', self.confirmScreen).each(function (index, el) {
991				if ($(this).find('input').is(':checked')) {
992					var details = $(this).data('details');
993					var qty = parseInt(details.booking_max) > 1 ? $(this).find('select').val() : 1;
994					self.booking.assets.push({
995						id: details.id,
996						qty: parseInt(qty)
997					});
998					self._updateFinalSummary();
999				}
1000			});
1001		},
1002		_updateFinalSummary: function () {
1003			var finalSummary = $('.amrp-booking-final-summary', self.confirmScreen).empty();
1004			$('<div class="amrp-booking-final-summary-heading">Your reservation</div>').appendTo(finalSummary);
1005			$('<div class="amrp-booking-final-summary-subheading">Contact:</div>').appendTo(finalSummary);
1006			finalSummary.append('<div>' + self.booking.contact.first_name + ' ' + self.booking.contact.last_name + '</div>');
1007			if (self.booking.contact.phone) finalSummary.append('<div>' + self.booking.contact.phone + '</div>');
1008			if (self.booking.contact.email) finalSummary.append('<div>' + self.booking.contact.email + '</div>');
1009			if (self.booking.contact.group_name) {
1010				$('<div class="amrp-booking-final-summary-subheading">Group name:</div>').appendTo(finalSummary);
1011				finalSummary.append('<div>' + self.booking.contact.group_name + '</div>');
1012			}
1013			if (self.booking.contact.booking_title) {
1014				$('<div class="amrp-booking-final-summary-subheading">Booking title:</div>').appendTo(finalSummary);
1015				finalSummary.append('<div>' + self.booking.contact.booking_title + '</div>');
1016			}
1017			$('<div class="amrp-booking-final-summary-subheading">Where:</div>').appendTo(finalSummary);
1018			finalSummary.append('<div>' + self.booking.room.title + ', ' + self.locations[self.booking.room.location_id].name + '</div>');
1019			$('<div class="amrp-booking-final-summary-subheading">
1019When:</div>').appendTo(finalSummary);
1020			var start = moment(self.booking.start_time);
1021			var end = moment(self.booking.end_time);
1022			finalSummary.append('<div>' + start.format('MMMM DD, YYYY') + '</div>');
1023			finalSummary.append('<div>' + start.format('h:mma') + ' - ' + end.format('h:mma') + '</div>');
1024
1025			if (self.booking.layout) {
1026				$('<div class="amrp-booking-final-summary-subheading">Layout:</div>').appendTo(finalSummary);
1027				finalSummary.append('<div>' + self.booking.layout.name + '</div>');
1028			}
1029
1030			if (self.booking.assets && self.booking.assets.length > 0) {
1031				$('<div class="amrp-booking-final-summary-subheading">Bookable resources:</div>').appendTo(finalSummary);
1032				$.each(self.booking.assets, function (index, val) {
1033					var asset = self.assets[val.id];
1034					var r = $('<div></div>').text(asset.name).appendTo(finalSummary);
1035					if (parseInt(asset.booking_max) > 1) {
1036						r.append('&nbsp; (' + val.qty + ')');
1037					}
1038				});
1039			}
1040		}
1041	});
1042	$.fn[pluginName] = function (options) {
1043		return this.each(function () {
1044			if (!$.data(this, "plugin_" + pluginName)) {
1045				$.data(this, "plugin_" + pluginName, new Plugin(this, options));
1046			}
1047		});
1048	};
1049})(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.