/** * Bootstro.js Simple way to show your user around, especially first time users * Http://github.com/clu3/bootstro.js * * Credit thanks to * Revealing Module Pattern from * http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/ * * Bootstrap popover variable width * http://stackoverflow.com/questions/10028218/twitter-bootstrap-popovers-multiple-widths-and-other-css-properties * */ $(document).ready(function(){ //Self-Executing Anonymous Func: Part 2 (Public & Private) (function( bootstro, $, undefined ) { var $elements; //jquery elements to be highlighted var count; var popovers = []; //contains array of the popovers data var activeIndex = null; //index of active item var bootstrapVersion = 3; var defaults = { nextButtonText : 'Next »', //will be wrapped with button as below //nextButton : '', prevButtonText : '« Prev', //prevButton : '', finishButtonText : ' Ok I got it, get back to the site', //finishButton : '', stopOnBackdropClick : true, stopOnEsc : true, //onComplete : function(params){} //params = {idx : activeIndex} //onExit : function(params){} //params = {idx : activeIndex} //onStep : function(params){} //params = {idx : activeIndex, direction : [next|prev]} //url : String // ajaxed url to get show data from margin : 100, //if the currently shown element's margin is less than this value // the element should be scrolled so that i can be viewed properly. This is useful // for sites which have fixed top/bottom nav bar }; var settings; //===================PRIVATE METHODS====================== //http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling function is_entirely_visible($elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $elem.offset().top; var elemBottom = elemTop + $elem.height(); return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) && (elemBottom <= docViewBottom) && (elemTop >= docViewTop) ); } //add the nav buttons to the popover content; function add_nav_btn(content, i) { var $el = get_element(i); var nextButton, prevButton, finishButton, defaultBtnClass; if (bootstrapVersion == 2) defaultBtnClass = "btn btn-primary btn-mini"; else defaultBtnClass = "btn btn-primary btn-xs"; //default bootstrap version 3 content = content + "
"; if ($el.attr('data-bootstro-nextButton')) { nextButton = $el.attr('data-bootstro-nextButton'); } else if ( $el.attr('data-bootstro-nextButtonText') ) { nextButton = ''; } else { if (typeof settings.nextButton != 'undefined' /*&& settings.nextButton != ''*/) nextButton = settings.nextButton; else nextButton = ''; } if ($el.attr('data-bootstro-prevButton')) { prevButton = $el.attr('data-bootstro-prevButton'); } else if ( $el.attr('data-bootstro-prevButtonText') ) { prevButton = ''; } else { if (typeof settings.prevButton != 'undefined' /*&& settings.prevButton != ''*/) prevButton = settings.prevButton; else prevButton = ''; } if ($el.attr('data-bootstro-finishButton')) { finishButton = $el.attr('data-bootstro-finishButton'); } else if ( $el.attr('data-bootstro-finishButtonText') ) { finishButton = ''; } else { if (typeof settings.finishButton != 'undefined' /*&& settings.finishButton != ''*/) finishButton = settings.finishButton; else finishButton = ''; } if (count != 1) { if (i == 0) content = content + nextButton; else if (i == count -1 ) content = content + prevButton; else content = content + nextButton + prevButton } content = content + '
'; content = content +'
' + finishButton + '
'; return content; } //prep objects from json and return selector process_items = function(popover) { var selectorArr = []; $.each(popover, function(t,e){ //only deal with the visible element //build the selector $.each(e, function(j, attr){ $(e.selector).attr('data-bootstro-' + j, attr); }); if ($(e.selector).is(":visible")) selectorArr.push(e.selector); }); return selectorArr.join(","); } //get the element to intro at stack i get_element = function(i) { //get the element with data-bootstro-step=i //or otherwise the the natural order of the set if ($elements.filter("[data-bootstro-step=" + i +"]").size() > 0) return $elements.filter("[data-bootstro-step=" + i +"]"); else { return $elements.eq(i); /* nrOfElementsWithStep = 0; $elements.filter("[data-bootstro-step!='']").each(function(j,e){ nrOfElementsWithStep ++; if (j > i) return $elements.filter(":not([data-bootstro-step])").eq(i - nrOfElementsWithStep); }) */ } } get_popup = function(i) { var p = {}; var $el = get_element(i); //p.selector = selector; var t = ''; if (count > 1) { t = "" + (i +1) + "/" + count + ""; } p.title = $el.attr('data-bootstro-title') || ''; if (p.title != '' && t != '') p.title = t + ' - ' + p.title; else if (p.title == '') p.title = t; p.content = $el.attr('data-bootstro-content') || ''; p.content = add_nav_btn(p.content, i); p.placement = $el.attr('data-bootstro-placement') || 'top'; var style = ''; if ($el.attr('data-bootstro-width')) { p.width = $el.attr('data-bootstro-width'); style = style + 'width:' + $el.attr('data-bootstro-width') + ';' } if ($el.attr('data-bootstro-height')) { p.height = $el.attr('data-bootstro-height'); style = style + 'height:' + $el.attr('data-bootstro-height') + ';' } p.trigger = 'manual'; //always set to manual. p.html = $el.attr('data-bootstro-html') || 'top'; if ($el.attr('data-bootstro-container')) { p.container = $el.attr('data-bootstro-container'); } //resize popover if it's explicitly specified //note: this is ugly. Could have been best if popover supports width & height p.template = '

' + '
'; return p; } //===================PUBLIC METHODS====================== //destroy popover at stack index i bootstro.destroy_popover = function(i) { var i = i || 0; if (i != 'all') { var $el = get_element(i);//$elements.eq(i); $el.popover('destroy').removeClass('bootstro-highlight'); } /* else //destroy all { $elements.each(function(e){ $(e).popover('destroy').removeClass('bootstro-highlight'); }); } */ }; //destroy active popover and remove backdrop bootstro.stop = function() { bootstro.destroy_popover(activeIndex); bootstro.unbind(); $("div.bootstro-backdrop").remove(); if (typeof settings.onExit == 'function') settings.onExit.call(this,{idx : activeIndex}); }; //go to the popover number idx starting from 0 bootstro.go_to = function(idx) { //destroy current popover if any bootstro.destroy_popover(activeIndex); if (count != 0) { var p = get_popup(idx); var $el = get_element(idx); $el.popover(p).popover('show'); //scroll if neccessary var docviewTop = $(window).scrollTop(); var top = Math.min($(".popover.in").offset().top, $el.offset().top); //distance between docviewTop & min. var topDistance = top - docviewTop; if (topDistance < settings.margin) //the element too up above $('html,body').animate({ scrollTop: top - settings.margin}, 'slow'); else if(!is_entirely_visible($(".popover.in")) || !is_entirely_visible($el)) //the element is too down below $('html,body').animate({ scrollTop: top - settings.margin}, 'slow'); // html $el.addClass('bootstro-highlight'); activeIndex = idx; } }; bootstro.next = function() { if (activeIndex + 1 == count) { if (typeof settings.onComplete == 'function') settings.onComplete.call(this, {idx : activeIndex});// } else { bootstro.go_to(activeIndex + 1); if (typeof settings.onStep == 'function') settings.onStep.call(this, {idx : activeIndex, direction : 'next'});// } }; bootstro.prev = function() { if (activeIndex == 0) { /* if (typeof settings.onRewind == 'function') settings.onRewind.call(this, {idx : activeIndex, direction : 'prev'});// */ } else { bootstro.go_to(activeIndex -1); if (typeof settings.onStep == 'function') settings.onStep.call(this, {idx : activeIndex, direction : 'prev'});// } }; bootstro._start = function(selector) { selector = selector || '.bootstro'; $elements = $(selector); count = $elements.size(); if (count > 0 && $('div.bootstro-backdrop').length === 0) { // Prevents multiple copies $('
').appendTo('body'); bootstro.bind(); bootstro.go_to(0); } }; bootstro.start = function(selector, options) { settings = $.extend(true, {}, defaults); //deep copy $.extend(settings, options || {}); //if options specifies a URL, get the intro configuration from URL via ajax if (typeof settings.url != 'undefined') { //get config from ajax $.ajax({ url : settings.url, success : function(data){ if (data.success) { //result is an array of {selector:'','title':'','width', ...} var popover = data.result; //console.log(popover); selector = process_items(popover); bootstro._start(selector); } } }); } //if options specifies an items object use it to load the intro configuration //settings.items is an array of {selector:'','title':'','width', ...} else if (typeof settings.items != 'undefined') { bootstro._start(process_items(settings.items)) } else { bootstro._start(selector); } }; bootstro.set_bootstrap_version = function(ver) { bootstrapVersion = ver; } //bind the nav buttons click event bootstro.bind = function() { bootstro.unbind(); $("html").on('click.bootstro', ".bootstro-next-btn", function(e){ bootstro.next(); e.preventDefault(); return false; }); $("html").on('click.bootstro', ".bootstro-prev-btn", function(e){ bootstro.prev(); e.preventDefault(); return false; }); //end of show $("html").on('click.bootstro', ".bootstro-finish-btn", function(e){ e.preventDefault(); bootstro.stop(); }); if (settings.stopOnBackdropClick) { $("html").on('click.bootstro', 'div.bootstro-backdrop', function(e){ if ($(e.target).hasClass('bootstro-backdrop')) bootstro.stop(); }); } //bind the key event $(document).on('keydown.bootstro', function(e){ var code = (e.keyCode ? e.keyCode : e.which); if (code == 39 || code == 40) bootstro.next(); else if (code == 37 || code == 38) bootstro.prev(); else if(code == 27 && settings.stopOnEsc) bootstro.stop(); }) }; bootstro.unbind = function() { $("html").unbind('click.bootstro'); $(document).unbind('keydown.bootstro'); } }( window.bootstro = window.bootstro || {}, jQuery )); });