not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

928 lines
30 KiB

  1. /*
  2. * jQuery Foundation Joyride Plugin 2.1
  3. * http://foundation.zurb.com
  4. * Copyright 2013, ZURB
  5. * Free to use under the MIT license.
  6. * http://www.opensource.org/licenses/mit-license.php
  7. */
  8. /*jslint unparam: true, browser: true, indent: 2 */
  9. ;(function ($, window, undefined) {
  10. 'use strict';
  11. var defaults = {
  12. 'version' : '2.1',
  13. 'tipLocation' : 'bottom', // 'top' or 'bottom' in relation to parent
  14. 'nubPosition' : 'auto', // override on a per tooltip bases
  15. 'scroll' : true, // whether to scroll to tips
  16. 'scrollSpeed' : 300, // Page scrolling speed in milliseconds
  17. 'timer' : 0, // 0 = no timer , all other numbers = timer in milliseconds
  18. 'autoStart' : false, // true or false - false tour starts when restart called
  19. 'startTimerOnClick' : true, // true or false - true requires clicking the first button start the timer
  20. 'startOffset' : 0, // the index of the tooltip you want to start on (index of the li)
  21. 'nextButton' : true, // true or false to control whether a next button is used
  22. 'tipAnimation' : 'fade', // 'pop' or 'fade' in each tip
  23. 'pauseAfter' : [], // array of indexes where to pause the tour after
  24. 'tipAnimationFadeSpeed': 300, // when tipAnimation = 'fade' this is speed in milliseconds for the transition
  25. 'cookieMonster' : false, // true or false to control whether cookies are used
  26. 'cookieName' : 'joyride', // Name the cookie you'll use
  27. 'cookieDomain' : false, // Will this cookie be attached to a domain, ie. '.notableapp.com'
  28. 'cookiePath' : false, // Set to '/' if you want the cookie for the whole website
  29. 'localStorage' : false, // true or false to control whether localstorage is used
  30. 'localStorageKey' : 'joyride', // Keyname in localstorage
  31. 'tipContainer' : 'body', // Where will the tip be attached
  32. 'modal' : false, // Whether to cover page with modal during the tour
  33. 'expose' : false, // Whether to expose the elements at each step in the tour (requires modal:true)
  34. 'postExposeCallback' : $.noop, // A method to call after an element has been exposed
  35. 'preRideCallback' : $.noop, // A method to call before the tour starts (passed index, tip, and cloned exposed element)
  36. 'postRideCallback' : $.noop, // A method to call once the tour closes (canceled or complete)
  37. 'preStepCallback' : $.noop, // A method to call before each step
  38. 'postStepCallback' : $.noop, // A method to call after each step
  39. 'template' : { // HTML segments for tip layout
  40. 'link' : '<a href="#close" class="joyride-close-tip">X</a>',
  41. 'timer' : '<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',
  42. 'tip' : '<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',
  43. 'wrapper' : '<div class="joyride-content-wrapper" role="dialog"></div>',
  44. 'button' : '<a href="#" class="joyride-next-tip"></a>',
  45. 'modal' : '<div class="joyride-modal-bg"></div>',
  46. 'expose' : '<div class="joyride-expose-wrapper"></div>',
  47. 'exposeCover': '<div class="joyride-expose-cover"></div>'
  48. }
  49. },
  50. Modernizr = Modernizr || false,
  51. settings = {},
  52. methods = {
  53. init : function (opts) {
  54. return this.each(function () {
  55. if ($.isEmptyObject(settings)) {
  56. settings = $.extend(true, defaults, opts);
  57. // non configurable settings
  58. settings.document = window.document;
  59. settings.$document = $(settings.document);
  60. settings.$window = $(window);
  61. settings.$content_el = $(this);
  62. settings.$body = $(settings.tipContainer);
  63. settings.body_offset = $(settings.tipContainer).position();
  64. settings.$tip_content = $('> li', settings.$content_el);
  65. settings.paused = false;
  66. settings.attempts = 0;
  67. settings.tipLocationPatterns = {
  68. top: ['bottom'],
  69. bottom: [], // bottom should not need to be repositioned
  70. left: ['right', 'top', 'bottom'],
  71. right: ['left', 'top', 'bottom']
  72. };
  73. // are we using jQuery 1.7+
  74. methods.jquery_check();
  75. // can we create cookies?
  76. if (!$.isFunction($.cookie)) {
  77. settings.cookieMonster = false;
  78. }
  79. // generate the tips and insert into dom.
  80. if ( (!settings.cookieMonster || !$.cookie(settings.cookieName) ) &&
  81. (!settings.localStorage || !methods.support_localstorage() || !localStorage.getItem(settings.localStorageKey) ) ) {
  82. settings.$tip_content.each(function (index) {
  83. methods.create({$li : $(this), index : index});
  84. });
  85. // show first tip
  86. if(settings.autoStart)
  87. {
  88. if (!settings.startTimerOnClick && settings.timer > 0) {
  89. methods.show('init');
  90. methods.startTimer();
  91. } else {
  92. methods.show('init');
  93. }
  94. }
  95. }
  96. settings.$document.on('click.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) {
  97. e.preventDefault();
  98. if (settings.$li.next().length < 1) {
  99. methods.end();
  100. } else if (settings.timer > 0) {
  101. clearTimeout(settings.automate);
  102. methods.hide();
  103. methods.show();
  104. methods.startTimer();
  105. } else {
  106. methods.hide();
  107. methods.show();
  108. }
  109. });
  110. settings.$document.on('click.joyride', '.joyride-close-tip', function (e) {
  111. e.preventDefault();
  112. methods.end(true /* isAborted */);
  113. });
  114. settings.$window.bind('resize.joyride', function (e) {
  115. if(settings.$li){
  116. if(settings.exposed && settings.exposed.length>0){
  117. var $els = $(settings.exposed);
  118. $els.each(function(){
  119. var $this = $(this);
  120. methods.un_expose($this);
  121. methods.expose($this);
  122. });
  123. }
  124. if (methods.is_phone()) {
  125. methods.pos_phone();
  126. } else {
  127. methods.pos_default();
  128. }
  129. }
  130. });
  131. } else {
  132. methods.restart();
  133. }
  134. });
  135. },
  136. // call this method when you want to resume the tour
  137. resume : function () {
  138. methods.set_li();
  139. methods.show();
  140. },
  141. nextTip: function(){
  142. if (settings.$li.next().length < 1) {
  143. methods.end();
  144. } else if (settings.timer > 0) {
  145. clearTimeout(settings.automate);
  146. methods.hide();
  147. methods.show();
  148. methods.startTimer();
  149. } else {
  150. methods.hide();
  151. methods.show();
  152. }
  153. },
  154. tip_template : function (opts) {
  155. var $blank, content, $wrapper;
  156. opts.tip_class = opts.tip_class || '';
  157. $blank = $(settings.template.tip).addClass(opts.tip_class);
  158. content = $.trim($(opts.li).html()) +
  159. methods.button_text(opts.button_text) +
  160. settings.template.link +
  161. methods.timer_instance(opts.index);
  162. $wrapper = $(settings.template.wrapper);
  163. if (opts.li.attr('data-aria-labelledby')) {
  164. $wrapper.attr('aria-labelledby', opts.li.attr('data-aria-labelledby'))
  165. }
  166. if (opts.li.attr('data-aria-describedby')) {
  167. $wrapper.attr('aria-describedby', opts.li.attr('data-aria-describedby'))
  168. }
  169. $blank.append($wrapper);
  170. $blank.first().attr('data-index', opts.index);
  171. $('.joyride-content-wrapper', $blank).append(content);
  172. return $blank[0];
  173. },
  174. timer_instance : function (index) {
  175. var txt;
  176. if ((index === 0 && settings.startTimerOnClick && settings.timer > 0) || settings.timer === 0) {
  177. txt = '';
  178. } else {
  179. txt = methods.outerHTML($(settings.template.timer)[0]);
  180. }
  181. return txt;
  182. },
  183. button_text : function (txt) {
  184. if (settings.nextButton) {
  185. txt = $.trim(txt) || 'Next';
  186. txt = methods.outerHTML($(settings.template.button).append(txt)[0]);
  187. } else {
  188. txt = '';
  189. }
  190. return txt;
  191. },
  192. create : function (opts) {
  193. // backwards compatibility with data-text attribute
  194. var buttonText = opts.$li.attr('data-button') || opts.$li.attr('data-text'),
  195. tipClass = opts.$li.attr('class'),
  196. $tip_content = $(methods.tip_template({
  197. tip_class : tipClass,
  198. index : opts.index,
  199. button_text : buttonText,
  200. li : opts.$li
  201. }));
  202. $(settings.tipContainer).append($tip_content);
  203. },
  204. show : function (init) {
  205. var opts = {}, ii, opts_arr = [], opts_len = 0, p,
  206. $timer = null;
  207. // are we paused?
  208. if (settings.$li === undefined || ($.inArray(settings.$li.index(), settings.pauseAfter) === -1)) {
  209. // don't go to the next li if the tour was paused
  210. if (settings.paused) {
  211. settings.paused = false;
  212. } else {
  213. methods.set_li(init);
  214. }
  215. settings.attempts = 0;
  216. if (settings.$li.length && settings.$target.length > 0) {
  217. if(init){ //run when we first start
  218. settings.preRideCallback(settings.$li.index(), settings.$next_tip );
  219. if(settings.modal){
  220. methods.show_modal();
  221. }
  222. }
  223. settings.preStepCallback(settings.$li.index(), settings.$next_tip );
  224. // parse options
  225. opts_arr = (settings.$li.data('options') || ':').split(';');
  226. opts_len = opts_arr.length;
  227. for (ii = opts_len - 1; ii >= 0; ii--) {
  228. p = opts_arr[ii].split(':');
  229. if (p.length === 2) {
  230. opts[$.trim(p[0])] = $.trim(p[1]);
  231. }
  232. }
  233. settings.tipSettings = $.extend({}, settings, opts);
  234. settings.tipSettings.tipLocationPattern = settings.tipLocationPatterns[settings.tipSettings.tipLocation];
  235. if(settings.modal && settings.expose){
  236. methods.expose();
  237. }
  238. // scroll if not modal
  239. if (!/body/i.test(settings.$target.selector) && settings.scroll) {
  240. methods.scroll_to();
  241. }
  242. if (methods.is_phone()) {
  243. methods.pos_phone(true);
  244. } else {
  245. methods.pos_default(true);
  246. }
  247. $timer = $('.joyride-timer-indicator', settings.$next_tip);
  248. if (/pop/i.test(settings.tipAnimation)) {
  249. $timer.outerWidth(0);
  250. if (settings.timer > 0) {
  251. settings.$next_tip.show();
  252. $timer.animate({
  253. width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth()
  254. }, settings.timer);
  255. } else {
  256. settings.$next_tip.show();
  257. }
  258. } else if (/fade/i.test(settings.tipAnimation)) {
  259. $timer.outerWidth(0);
  260. if (settings.timer > 0) {
  261. settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed);
  262. settings.$next_tip.show();
  263. $timer.animate({
  264. width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth()
  265. }, settings.timer);
  266. } else {
  267. settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed);
  268. }
  269. }
  270. settings.$current_tip = settings.$next_tip;
  271. // Focus next button for keyboard users.
  272. $('.joyride-next-tip', settings.$current_tip).focus();
  273. methods.tabbable(settings.$current_tip);
  274. // skip non-existent targets
  275. } else if (settings.$li && settings.$target.length < 1) {
  276. methods.show();
  277. } else {
  278. methods.end();
  279. }
  280. } else {
  281. settings.paused = true;
  282. }
  283. },
  284. // detect phones with media queries if supported.
  285. is_phone : function () {
  286. if (Modernizr) {
  287. return Modernizr.mq('only screen and (max-width: 767px)');
  288. }
  289. return (settings.$window.width() < 767) ? true : false;
  290. },
  291. support_localstorage : function () {
  292. if (Modernizr) {
  293. return Modernizr.localstorage;
  294. } else {
  295. return !!window.localStorage;
  296. }
  297. },
  298. hide : function () {
  299. if(settings.modal && settings.expose){
  300. methods.un_expose();
  301. }
  302. if(!settings.modal){
  303. $('.joyride-modal-bg').hide();
  304. }
  305. settings.$current_tip.hide();
  306. settings.postStepCallback(settings.$li.index(), settings.$current_tip);
  307. },
  308. set_li : function (init) {
  309. if (init) {
  310. settings.$li = settings.$tip_content.eq(settings.startOffset);
  311. methods.set_next_tip();
  312. settings.$current_tip = settings.$next_tip;
  313. } else {
  314. settings.$li = settings.$li.next();
  315. methods.set_next_tip();
  316. }
  317. methods.set_target();
  318. },
  319. set_next_tip : function () {
  320. settings.$next_tip = $('.joyride-tip-guide[data-index=' + settings.$li.index() + ']');
  321. },
  322. set_target : function () {
  323. var cl = settings.$li.attr('data-class'),
  324. id = settings.$li.attr('data-id'),
  325. $sel = function () {
  326. if (id) {
  327. return $(settings.document.getElementById(id));
  328. } else if (cl) {
  329. return $('.' + cl).filter(":visible").first();
  330. } else {
  331. return $('body');
  332. }
  333. };
  334. settings.$target = $sel();
  335. },
  336. scroll_to : function () {
  337. var window_half, tipOffset;
  338. window_half = settings.$window.height() / 2;
  339. tipOffset = Math.ceil(settings.$target.offset().top - window_half + settings.$next_tip.outerHeight());
  340. $("html, body").stop().animate({
  341. scrollTop: tipOffset
  342. }, settings.scrollSpeed);
  343. },
  344. paused : function () {
  345. if (($.inArray((settings.$li.index() + 1), settings.pauseAfter) === -1)) {
  346. return true;
  347. }
  348. return false;
  349. },
  350. destroy : function () {
  351. if(!$.isEmptyObject(settings)){
  352. settings.$document.off('.joyride');
  353. }
  354. $(window).off('.joyride');
  355. $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride');
  356. $('.joyride-tip-guide, .joyride-modal-bg').remove();
  357. clearTimeout(settings.automate);
  358. settings = {};
  359. },
  360. restart : function () {
  361. if(!settings.autoStart)
  362. {
  363. if (!settings.startTimerOnClick && settings.timer > 0) {
  364. methods.show('init');
  365. methods.startTimer();
  366. } else {
  367. methods.show('init');
  368. }
  369. settings.autoStart = true;
  370. }
  371. else
  372. {
  373. methods.hide();
  374. settings.$li = undefined;
  375. methods.show('init');
  376. }
  377. },
  378. pos_default : function (init) {
  379. var half_fold = Math.ceil(settings.$window.height() / 2),
  380. tip_position = settings.$next_tip.offset(),
  381. $nub = $('.joyride-nub', settings.$next_tip),
  382. nub_width = Math.ceil($nub.outerWidth() / 2),
  383. nub_height = Math.ceil($nub.outerHeight() / 2),
  384. toggle = init || false;
  385. // tip must not be "display: none" to calculate position
  386. if (toggle) {
  387. settings.$next_tip.css('visibility', 'hidden');
  388. settings.$next_tip.show();
  389. }
  390. if (!/body/i.test(settings.$target.selector)) {
  391. var
  392. topAdjustment = settings.tipSettings.tipAdjustmentY ? parseInt(settings.tipSettings.tipAdjustmentY) : 0,
  393. leftAdjustment = settings.tipSettings.tipAdjustmentX ? parseInt(settings.tipSettings.tipAdjustmentX) : 0;
  394. if (methods.bottom()) {
  395. settings.$next_tip.css({
  396. top: (settings.$target.offset().top + nub_height + settings.$target.outerHeight() + topAdjustment),
  397. left: settings.$target.offset().left + leftAdjustment});
  398. if (/right/i.test(settings.tipSettings.nubPosition)) {
  399. settings.$next_tip.css('left', settings.$target.offset().left - settings.$next_tip.outerWidth() + settings.$target.outerWidth());
  400. }
  401. methods.nub_position($nub, settings.tipSettings.nubPosition, 'top');
  402. } else if (methods.top()) {
  403. settings.$next_tip.css({
  404. top: (settings.$target.offset().top - settings.$next_tip.outerHeight() - nub_height + topAdjustment),
  405. left: settings.$target.offset().left + leftAdjustment});
  406. methods.nub_position($nub, settings.tipSettings.nubPosition, 'bottom');
  407. } else if (methods.right()) {
  408. settings.$next_tip.css({
  409. top: settings.$target.offset().top + topAdjustment,
  410. left: (settings.$target.outerWidth() + settings.$target.offset().left + nub_width) + leftAdjustment});
  411. methods.nub_position($nub, settings.tipSettings.nubPosition, 'left');
  412. } else if (methods.left()) {
  413. settings.$next_tip.css({
  414. top: settings.$target.offset().top + topAdjustment,
  415. left: (settings.$target.offset().left - settings.$next_tip.outerWidth() - nub_width) + leftAdjustment});
  416. methods.nub_position($nub, settings.tipSettings.nubPosition, 'right');
  417. }
  418. if (!methods.visible(methods.corners(settings.$next_tip)) && settings.attempts < settings.tipSettings.tipLocationPattern.length) {
  419. $nub.removeClass('bottom')
  420. .removeClass('top')
  421. .removeClass('right')
  422. .removeClass('left');
  423. settings.tipSettings.tipLocation = settings.tipSettings.tipLocationPattern[settings.attempts];
  424. settings.attempts++;
  425. methods.pos_default(true);
  426. }
  427. } else if (settings.$li.length) {
  428. methods.pos_modal($nub);
  429. }
  430. if (toggle) {
  431. settings.$next_tip.hide();
  432. settings.$next_tip.css('visibility', 'visible');
  433. }
  434. },
  435. pos_phone : function (init) {
  436. var tip_height = settings.$next_tip.outerHeight(),
  437. tip_offset = settings.$next_tip.offset(),
  438. target_height = settings.$target.outerHeight(),
  439. $nub = $('.joyride-nub', settings.$next_tip),
  440. nub_height = Math.ceil($nub.outerHeight() / 2),
  441. toggle = init || false;
  442. $nub.removeClass('bottom')
  443. .removeClass('top')
  444. .removeClass('right')
  445. .removeClass('left');
  446. if (toggle) {
  447. settings.$next_tip.css('visibility', 'hidden');
  448. settings.$next_tip.show();
  449. }
  450. if (!/body/i.test(settings.$target.selector)) {
  451. if (methods.top()) {
  452. settings.$next_tip.offset({top: settings.$target.offset().top - tip_height - nub_height});
  453. $nub.addClass('bottom');
  454. } else {
  455. settings.$next_tip.offset({top: settings.$target.offset().top + target_height + nub_height});
  456. $nub.addClass('top');
  457. }
  458. } else if (settings.$li.length) {
  459. methods.pos_modal($nub);
  460. }
  461. if (toggle) {
  462. settings.$next_tip.hide();
  463. settings.$next_tip.css('visibility', 'visible');
  464. }
  465. },
  466. pos_modal : function ($nub) {
  467. methods.center();
  468. $nub.hide();
  469. methods.show_modal();
  470. },
  471. show_modal : function() {
  472. if ($('.joyride-modal-bg').length < 1) {
  473. $('body').append(settings.template.modal).show();
  474. }
  475. if (/pop/i.test(settings.tipAnimation)) {
  476. $('.joyride-modal-bg').show();
  477. } else {
  478. $('.joyride-modal-bg').fadeIn(settings.tipAnimationFadeSpeed);
  479. }
  480. },
  481. expose: function(){
  482. var expose,
  483. exposeCover,
  484. el,
  485. origCSS,
  486. randId = 'expose-'+Math.floor(Math.random()*10000);
  487. if (arguments.length>0 && arguments[0] instanceof $){
  488. el = arguments[0];
  489. } else if(settings.$target && !/body/i.test(settings.$target.selector)){
  490. el = settings.$target;
  491. } else {
  492. return false;
  493. }
  494. if(el.length < 1){
  495. if(window.console){
  496. console.error('element not valid', el);
  497. }
  498. return false;
  499. }
  500. expose = $(settings.template.expose);
  501. settings.$body.append(expose);
  502. expose.css({
  503. top: el.offset().top,
  504. left: el.offset().left,
  505. width: el.outerWidth(true),
  506. height: el.outerHeight(true)
  507. });
  508. exposeCover = $(settings.template.exposeCover);
  509. origCSS = {
  510. zIndex: el.css('z-index'),
  511. position: el.css('position')
  512. };
  513. el.css('z-index',expose.css('z-index')*1+1);
  514. if(origCSS.position == 'static'){
  515. el.css('position','relative');
  516. }
  517. el.data('expose-css',origCSS);
  518. exposeCover.css({
  519. top: el.offset().top,
  520. left: el.offset().left,
  521. width: el.outerWidth(true),
  522. height: el.outerHeight(true)
  523. });
  524. settings.$body.append(exposeCover);
  525. expose.addClass(randId);
  526. exposeCover.addClass(randId);
  527. if(settings.tipSettings['exposeClass']){
  528. expose.addClass(settings.tipSettings['exposeClass']);
  529. exposeCover.addClass(settings.tipSettings['exposeClass']);
  530. }
  531. el.data('expose', randId);
  532. settings.postExposeCallback(settings.$li.index(), settings.$next_tip, el);
  533. methods.add_exposed(el);
  534. },
  535. un_expose: function(){
  536. var exposeId,
  537. el,
  538. expose ,
  539. origCSS,
  540. clearAll = false;
  541. if (arguments.length>0 && arguments[0] instanceof $){
  542. el = arguments[0];
  543. } else if(settings.$target && !/body/i.test(settings.$target.selector)){
  544. el = settings.$target;
  545. } else {
  546. return false;
  547. }
  548. if(el.length < 1){
  549. if(window.console){
  550. console.error('element not valid', el);
  551. }
  552. return false;
  553. }
  554. exposeId = el.data('expose');
  555. expose = $('.'+exposeId);
  556. if(arguments.length>1){
  557. clearAll = arguments[1];
  558. }
  559. if(clearAll === true){
  560. $('.joyride-expose-wrapper,.joyride-expose-cover').remove();
  561. } else {
  562. expose.remove();
  563. }
  564. origCSS = el.data('expose-css');
  565. if(origCSS.zIndex == 'auto'){
  566. el.css('z-index', '');
  567. } else {
  568. el.css('z-index',origCSS.zIndex);
  569. }
  570. if(origCSS.position != el.css('position')){
  571. if(origCSS.position == 'static'){// this is default, no need to set it.
  572. el.css('position', '');
  573. } else {
  574. el.css('position',origCSS.position);
  575. }
  576. }
  577. el.removeData('expose');
  578. el.removeData('expose-z-index');
  579. methods.remove_exposed(el);
  580. },
  581. add_exposed: function(el){
  582. settings.exposed = settings.exposed || [];
  583. if(el instanceof $){
  584. settings.exposed.push(el[0]);
  585. } else if(typeof el == 'string'){
  586. settings.exposed.push(el);
  587. }
  588. },
  589. remove_exposed: function(el){
  590. var search;
  591. if(el instanceof $){
  592. search = el[0]
  593. } else if (typeof el == 'string'){
  594. search = el;
  595. }
  596. settings.exposed = settings.exposed || [];
  597. for(var i=0; i<settings.exposed.length; i++){
  598. if(settings.exposed[i] == search){
  599. settings.exposed.splice(i,1);
  600. return;
  601. }
  602. }
  603. },
  604. center : function () {
  605. var $w = settings.$window;
  606. settings.$next_tip.css({
  607. top : ((($w.height() - settings.$next_tip.outerHeight()) / 2) + $w.scrollTop()),
  608. left : ((($w.width() - settings.$next_tip.outerWidth()) / 2) + $w.scrollLeft())
  609. });
  610. return true;
  611. },
  612. bottom : function () {
  613. return /bottom/i.test(settings.tipSettings.tipLocation);
  614. },
  615. top : function () {
  616. return /top/i.test(settings.tipSettings.tipLocation);
  617. },
  618. right : function () {
  619. return /right/i.test(settings.tipSettings.tipLocation);
  620. },
  621. left : function () {
  622. return /left/i.test(settings.tipSettings.tipLocation);
  623. },
  624. corners : function (el) {
  625. var w = settings.$window,
  626. window_half = w.height() / 2,
  627. tipOffset = Math.ceil(settings.$target.offset().top - window_half + settings.$next_tip.outerHeight()),//using this to calculate since scroll may not have finished yet.
  628. right = w.width() + w.scrollLeft(),
  629. offsetBottom = w.height() + tipOffset,
  630. bottom = w.height() + w.scrollTop(),
  631. top = w.scrollTop();
  632. if(tipOffset < top){
  633. if (tipOffset <0 ){
  634. top = 0;
  635. } else {
  636. top = tipOffset;
  637. }
  638. }
  639. if(offsetBottom > bottom){
  640. bottom = offsetBottom;
  641. }
  642. return [
  643. el.offset().top < top,
  644. right < el.offset().left + el.outerWidth(),
  645. bottom < el.offset().top + el.outerHeight(),
  646. w.scrollLeft() > el.offset().left
  647. ];
  648. },
  649. visible : function (hidden_corners) {
  650. var i = hidden_corners.length;
  651. while (i--) {
  652. if (hidden_corners[i]) return false;
  653. }
  654. return true;
  655. },
  656. nub_position : function (nub, pos, def) {
  657. if (pos === 'auto') {
  658. nub.addClass(def);
  659. } else {
  660. nub.addClass(pos);
  661. }
  662. },
  663. startTimer : function () {
  664. if (settings.$li.length) {
  665. settings.automate = setTimeout(function () {
  666. methods.hide();
  667. methods.show();
  668. methods.startTimer();
  669. }, settings.timer);
  670. } else {
  671. clearTimeout(settings.automate);
  672. }
  673. },
  674. end : function (isAborted) {
  675. isAborted = isAborted || false;
  676. // Unbind resize events.
  677. if (isAborted) {
  678. settings.$window.unbind('resize.joyride');
  679. }
  680. if (settings.cookieMonster) {
  681. $.cookie(settings.cookieName, 'ridden', { expires: 365, domain: settings.cookieDomain, path: settings.cookiePath });
  682. }
  683. if (settings.localStorage) {
  684. localStorage.setItem(settings.localStorageKey, true);
  685. }
  686. if (settings.timer > 0) {
  687. clearTimeout(settings.automate);
  688. }
  689. if(settings.modal && settings.expose){
  690. methods.un_expose();
  691. }
  692. if (settings.$current_tip) {
  693. settings.$current_tip.hide();
  694. }
  695. if (settings.$li) {
  696. settings.postStepCallback(settings.$li.index(), settings.$current_tip, isAborted);
  697. settings.postRideCallback(settings.$li.index(), settings.$current_tip, isAborted);
  698. }
  699. $('.joyride-modal-bg').hide();
  700. },
  701. jquery_check : function () {
  702. // define on() and off() for older jQuery
  703. if (!$.isFunction($.fn.on)) {
  704. $.fn.on = function (types, sel, fn) {
  705. return this.delegate(sel, types, fn);
  706. };
  707. $.fn.off = function (types, sel, fn) {
  708. return this.undelegate(sel, types, fn);
  709. };
  710. return false;
  711. }
  712. return true;
  713. },
  714. outerHTML : function (el) {
  715. // support FireFox < 11
  716. return el.outerHTML || new XMLSerializer().serializeToString(el);
  717. },
  718. version : function () {
  719. return settings.version;
  720. },
  721. tabbable : function (el) {
  722. $(el).on('keydown', function( event ) {
  723. if (!event.isDefaultPrevented() && event.keyCode &&
  724. // Escape key.
  725. event.keyCode === 27 ) {
  726. event.preventDefault();
  727. methods.end(true /* isAborted */);
  728. return;
  729. }
  730. // Prevent tabbing out of tour items.
  731. if ( event.keyCode !== 9 ) {
  732. return;
  733. }
  734. var tabbables = $(el).find(":tabbable"),
  735. first = tabbables.filter(":first"),
  736. last = tabbables.filter(":last");
  737. if ( event.target === last[0] && !event.shiftKey ) {
  738. first.focus( 1 );
  739. event.preventDefault();
  740. } else if ( event.target === first[0] && event.shiftKey ) {
  741. last.focus( 1 );
  742. event.preventDefault();
  743. }
  744. });
  745. }
  746. };
  747. $.fn.joyride = function (method) {
  748. if (methods[method]) {
  749. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  750. } else if (typeof method === 'object' || !method) {
  751. return methods.init.apply(this, arguments);
  752. } else {
  753. $.error('Method ' + method + ' does not exist on jQuery.joyride');
  754. }
  755. };
  756. }(jQuery, this));