Skip to content

Instantly share code, notes, and snippets.

@chestozo
Created June 24, 2015 17:06
Show Gist options
  • Save chestozo/661179bcacefa0be0686 to your computer and use it in GitHub Desktop.
Save chestozo/661179bcacefa0be0686 to your computer and use it in GitHub Desktop.
/*
* -----------------------------------------------------------------------------------------------------------------------
* Sirvoy Booking Widget, (c) Sirvoy Ltd 2014, All rights reserved.
* -----------------------------------------------------------------------------------------------------------------------
*
* Usage:
*
* To include the booking widget on your page, just add the following html to your page:
* <script async type="text/javascript" src="https://secured.sirvoy.com/widget/sirvoy.js" data-form-id="your-token-here"></script>
*
* The widget will be loaded via javascript and inserted at the position of the script-tag. You can have multiple booking
* widgets at the same page if you want to. If you want to customize the widget you can also include some of the optional
* attributes in the script-tag.
*
* Mandatory attributes:
* -----------------------------------------------------------------------------------------------------------------------
* data-form-id The id for the booking form, you get this after logging in to your Sirvoy-account and choose
* Settings > Your website > Booking forms.
* -----------------------------------------------------------------------------------------------------------------------
*
* Optional attributes:
* -----------------------------------------------------------------------------------------------------------------------
* data-container-id Customize the widget containers id-attribute. If not specified this will be automatically generated.
* data-widget Specifies the widget type to render. Can be one of "small" or "normal". Defaults to "normal".
* data-lang Sets the initial language of the widget. If not specified it will be assigned automatically.
* data-category Preselected category. See settings and description under Settings > Your website > Booking forms.
* data-callback A javascript function in the globale scope that will be called for events that occur.
* -----------------------------------------------------------------------------------------------------------------------
*
*/
(function (global) {
// add array index of for old browsers (IE8)
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(obj, start) {
var i, j;
i = start || 0;
j = this.length;
while (i < j) {
if (this[i] === obj) {
return i;
}
i++;
}
return -1;
};
}
// Init global variable for our widgets
if (!global.SirvoyBookingWidget) {
// This code will be executed once per page
global.SirvoyBookingWidget = {}; // init global variable so we don't run this again.
// Our local jQuery variable
var jQuery;
// Load jQuery if not present on page (or not acceptable version)
if (window.jQuery === undefined || !checkJqueryVersion(window.jQuery.fn.jquery)) {
var jq_script_tag = document.createElement('script');
jq_script_tag.setAttribute("type","text/javascript");
jq_script_tag.setAttribute("src", "//code.jquery.com/jquery-1.11.1.min.js");
if (jq_script_tag.readyState) {
jq_script_tag.onreadystatechange = function () { // For old versions of IE
if (this.readyState == 'complete' || this.readyState == 'loaded') {
jqueryLoadHandler();
}
};
} else { // Other browsers
jq_script_tag.onload = jqueryLoadHandler;
}
// Try to find the head, otherwise default to the documentElement
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(jq_script_tag);
// Set jQuery strategy so we know later on that we have loaded our own jQuery.
global.SirvoyBookingWidget.jQueryLoadStrategy = 'load';
} else {
// The jQuery version on the window is the one we want to use
jQuery = window.jQuery;
// Set jQuery strategy so we know later on that we use the hosts jQuery version.
global.SirvoyBookingWidget.jQueryLoadStrategy = 'host';
}
}
var SirvoyBookingWidget = global.SirvoyBookingWidget;
// If we use the hosts jQuery we need to run the main function here.
// If we load our own jQuery version this will be done in the callback after jQuery has loaded.
if (SirvoyBookingWidget.jQueryLoadStrategy == 'host') main(window.jQuery);
// Check that jquery on host page has acceptable version - we will use our own version.
function checkJqueryVersion(version) {
var parts = version.split('.');
// sanity check: It should be at least a major and a minor version
if (parts.length < 2) return false;
// all above 1.9 is OK
if (parts[0] == '1' && (parts[1] > 8))
return true;
// 2.x has same api, but dropped support for <IE9, when we no longer support IE8 we can use this.
// if (parts[0] == '2')
// return true;
// Else, return 'false' to load our own jQuery.
return false;
}
// Called once jQuery has loaded
function jqueryLoadHandler() {
// Restore $ and window.jQuery to their previous values and store the
// new jQuery in our local jQuery variable
jQuery = window.jQuery.noConflict(true);
// Call our main function, pass in jQuery as $ so we can use $ as we are used to.
main(jQuery);
}
// Used to extract parameters from host page
function getUrlParam(name) {
var params = window.location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var item = params[i].split('=');
if (item[0] == name) {
return item[1];
}
}
return false;
}
// Our main function
function main($) {
// Register event listeners for events from iframe
if (window.addEventListener) { window.addEventListener('message', onMessage, false); } //W3C
else if (window.attachEvent) { window.attachEvent('onmessage', onMessage); } //IE
// To keep track of which embeds we have already processed
if (!SirvoyBookingWidget.processedScripts) { SirvoyBookingWidget.processedScripts = []; }
var processedScripts = SirvoyBookingWidget.processedScripts;
// Style tags inserted by us
if (!SirvoyBookingWidget.styleTags) { SirvoyBookingWidget.styleTags = []; }
var styleTags = SirvoyBookingWidget.styleTags;
// Keep track of registered callbacks
if (!SirvoyBookingWidget.userCallbacks) { SirvoyBookingWidget.userCallbacks = []; }
var userCallbacks = SirvoyBookingWidget.userCallbacks;
var scriptTags = document.getElementsByTagName('script');
var thisRequestUri = 'https\x3A\x2F\x2Fsecured.sirvoy.com\x2Fwidget\x2Fsirvoy.js';
var widgetId = 0; // index of widget on page, counting from 1 and upwards
for(var i = 0; i < scriptTags.length; i++) {
var scriptTag = scriptTags[i];
// src matches the uri of this request, and not processed it yet.
if (scriptTag.src == thisRequestUri && processedScripts.indexOf(scriptTag) < 0) {
processedScripts.push(scriptTag);
widgetId++;
// add the style tag into the head (once only)
if(styleTags.length == 0) {
// add a style tag to the head
var styleTag = document.createElement("link");
styleTag.rel = "stylesheet";
styleTag.type = "text/css";
styleTag.href = "https\x3A\x2F\x2Fsirvoy.s3.amazonaws.com\x2Fcss\x2Fwidget\x2Dbook.css";
styleTag.media = "all";
document.getElementsByTagName('head')[0].appendChild(styleTag);
styleTags.push(styleTag);
}
var widget_data = {}; // settings object
var data_widget = scriptTag.getAttribute('data-widget');
var data_category_id = scriptTag.getAttribute('data-category');
var data_lang = scriptTag.getAttribute('data-lang');
var data_container_id = scriptTag.getAttribute('data-container-id');
var data_from_backend = scriptTag.getAttribute('data-from-backend');
var data_callback = scriptTag.getAttribute('data-callback');
widget_data.container_id = data_container_id || 'sbw_widget_' + widgetId; //auto id if not provided
widget_data.widget = data_widget || 'normal'; //'normal' widget if not provided
// Mandatory attributes
widget_data.form_id = scriptTag.getAttribute('data-form-id');
if (!widget_data.form_id)
throw "Sirvoy Booking Widget: The attribute 'data-form-id' is mandatory, aborting.";
// Optional attributes
if (data_category_id) widget_data.category_id = data_category_id;
if (data_lang) widget_data.lang = data_lang;
// For widget 'normal':
// If host page parameters 'check_in', 'check_out' are present, we pass them along. The parameter
// 'target' (one of 'results' or 'search') determines which view. If 'target' is not present, the 'results'
// view is the default. If 'adults' is present, we that along as well.
// Additional parameters are: 'source'.
var search_params = {
adults: getUrlParam('adults'),
checkin: getUrlParam('check_in'),
checkout: getUrlParam('check_out'),
target: getUrlParam('target'),
source: getUrlParam('source')
};
if (widget_data.widget == 'normal' && search_params.checkin && search_params.checkout) {
widget_data.search_params = search_params;
}
// if host page parameter 'language' is present this will have precedence over data-lang.
if (getUrlParam('language')) widget_data.lang = getUrlParam('language');
// booking from backend
if (data_from_backend)
widget_data.from_backend = true;
// callbacks - save for later
userCallbacks[widget_data.container_id] = window[data_callback];
// Create a div container
var div = document.createElement('div');
div.id = widget_data.container_id;
div.className = 'sbw yui3-cssreset sbw_form_id_' + widget_data.form_id + ' sbw_id_' + widget_data.container_id;
scriptTag.parentNode.insertBefore(div, scriptTag);
// Show loader
div.innerHTML = '<img style="display: block; margin: 0 auto;" class="sbw-loader" alt="" src="data:image/gif;base64,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" />';
(function(container) {
$.ajax({
url: "https\x3A\x2F\x2Fsecured.sirvoy.com\x2Fwidget\x2Fbook_widget_data.js",
// send settings from client to server
contentType: "application/json; charset=utf-8",
data: {data: widget_data},
// data retrival from server to client
dataType: "jsonp",
jsonp: "callback",
success: function(response){
container.innerHTML = container.innerHTML + response.html;
},
error: function(response){
container.innerHTML = '<div class="sbw-error"><p>' +
'<strong>An error occured.</strong>' +
' Sirvoy booking widget could not load. Check the widget markup. ' +
'These are the details of the error:</p>' +
'<p class="pre">' + JSON.stringify(response) +
'</p></div>';
},
})})(div); // run the above code in a closure so we can access the container in the callback
}
}
// To handle resize we have this function
function onMessage(event) {
// check origin
if (!event.origin.match(/^(http|https):\/\/(localhost|secured\.sirvoy\.com)$/g))
return;
// event.data is a string with json (IE can't send object via postMessage()), we will now convert this to an object.
var msg = JSON.parse(event.data);
// page resize?
if (msg.event == 'page_resize' && msg.height && msg.container_id) {
resizeWidget(msg.container_id, msg.height, msg.scroll);
return;
}
// else: this is a custom callback for a booking event, is callback defined?
if (msg.event && msg.container_id2 && userCallbacks[msg.container_id]) {
// call user function
userCallbacks[msg.container_id](msg);
} else {
console.log('Sirvoy Booking Widget: Bad data in message: ' + JSON.stringify(msg));
}
}
// Called from onMessage, resizes widget
function resizeWidget(container_id, height, scroll) {
// adjust height
$('#' + container_id + '> iframe').height(height);
// hide loader
$('#' + container_id + '> .sbw-loader').hide();
if (scroll) {
// scroll to widget top
var offset_top = $('#' + container_id).offset().top;
$(window).scrollTop(offset_top);
}
}
} //main
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment