jquery.pagewalkthrough.js 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. /***
  2. * Author: Erwin Yusrizal
  3. * UX by: Tai Nguyen
  4. * Contributors: James West <jwwest@gmail.com>
  5. * JP Adams <jpadamsonline@gmail.com>
  6. * James Warwood <james.duncan.1991@googlemail.com>
  7. * Craig Roberts <craig0990@googlemail.com>
  8. * Created On: 27/02/2013
  9. * Version: 2.6.7
  10. * Features & Bugs: https://github.com/warby-/jquery-pagewalkthrough/issues
  11. ***/
  12. ;(function($, window, document, undefined) {
  13. 'use strict';
  14. /**
  15. * GLOBAL VAR
  16. */
  17. var _globalWalkthrough = {},
  18. _elements = [],
  19. _activeWalkthrough,
  20. _activeId,
  21. _hasDefault = true,
  22. _counter = 0,
  23. _isCookieLoad,
  24. _firstTimeLoad = true,
  25. _onLoad = true,
  26. _index = 0,
  27. _isWalkthroughActive = false,
  28. $jpwOverlay = $('<div id="jpwOverlay"></div>'),
  29. $jpWalkthrough = $('<div id="jpWalkthrough"></div>'),
  30. $jpwTooltip = $('<div id="jpwTooltip"></div>');
  31. /**
  32. * PUBLIC METHOD
  33. */
  34. var methods = {
  35. isActive: function() {
  36. return !!_isWalkthroughActive;
  37. },
  38. index: function(value) {
  39. if (typeof value !== 'undefined') {
  40. _index = value;
  41. }
  42. return _index;
  43. },
  44. //init method
  45. init: function(options) {
  46. options = $.extend(true, {}, $.fn.pagewalkthrough.defaults, options);
  47. var that = this;
  48. if (!options.name) {
  49. throw new Error('Must provide a unique name for a tour');
  50. }
  51. // @todo what happens with multiple walkthroughs on the same element?
  52. this.first().data('jpw', options);
  53. options._element = this;
  54. return this.each(function(i) {
  55. options = options || {};
  56. options.elementID = options.name;
  57. _globalWalkthrough[options.name] = options;
  58. _elements.push(options.name);
  59. //check if onLoad and this is first time load
  60. if (options.onLoad) {
  61. _counter++;
  62. }
  63. //get first onload = true
  64. if (_counter === 1 && _onLoad) {
  65. _activeId = options.name;
  66. _activeWalkthrough = _globalWalkthrough[_activeId];
  67. _onLoad = false;
  68. }
  69. // set the activeWalkthrough if onLoad is false for all walkthroughs
  70. if ((i + 1 === that.length && _counter === 0)) {
  71. _activeId = options.name;
  72. _activeWalkthrough = _globalWalkthrough[_elements[0]];
  73. _hasDefault = false;
  74. }
  75. });
  76. },
  77. renderOverlay: function() {
  78. // if each walkthrough has onLoad: true, log warning message
  79. if (_counter > 1) {
  80. debug('Warning: Only 1st walkthrough will be shown onLoad as default');
  81. }
  82. //get cookie load
  83. _isCookieLoad = getCookie('_walkthrough-' + _activeId);
  84. //check if first time walkthrough
  85. if (typeof _isCookieLoad === 'undefined') {
  86. _isWalkthroughActive = true;
  87. if (!(onEnter())) return;
  88. showStep();
  89. showButton('jpwClose', 'body');
  90. setTimeout(function() {
  91. //call onAfterShow callback
  92. if (isFirstStep() && _firstTimeLoad) {
  93. if (!onAfterShow()) return;
  94. }
  95. }, 100);
  96. } else {
  97. onCookieLoad(_globalWalkthrough);
  98. }
  99. },
  100. restart: function(e) {
  101. if (isFirstStep()) return;
  102. _index = 0;
  103. if (!(onRestart(e))) return;
  104. if (!(onEnter(e))) return;
  105. showStep();
  106. },
  107. close: function() {
  108. var options = _activeWalkthrough;
  109. onLeave();
  110. if (typeof options.onClose === 'function') {
  111. options.onClose.call(this);
  112. }
  113. _index = 0;
  114. _firstTimeLoad = true;
  115. _isWalkthroughActive = false;
  116. //set cookie to false
  117. setCookie('_walkthrough-' + _activeId, 0, 365);
  118. _isCookieLoad = getCookie('_walkthrough-' + _activeId);
  119. $jpwOverlay.fadeOut('slow', function() {
  120. $(this).remove();
  121. });
  122. $jpWalkthrough.fadeOut('slow', function() {
  123. $(this).html('').remove();
  124. });
  125. $('#jpwClose').fadeOut('slow', function() {
  126. $(this).remove();
  127. });
  128. },
  129. show: function(name, e) {
  130. // If no name, then first argument is event
  131. e = name == null ? name : e;
  132. name = name || this.first().data('jpw').name;
  133. _activeWalkthrough = _globalWalkthrough[this.first().data('jpw').name];
  134. if ((name === _activeId && _isWalkthroughActive) || !(onEnter(e))) return;
  135. _isWalkthroughActive = true;
  136. _firstTimeLoad = true;
  137. if (!(onBeforeShow())) return;
  138. showStep();
  139. showButton('jpwClose', 'body');
  140. //call onAfterShow callback
  141. if (isFirstStep() && _firstTimeLoad) {
  142. if (!onAfterShow()) return;
  143. }
  144. },
  145. next: function(e) {
  146. _firstTimeLoad = false;
  147. if (isLastStep()) return;
  148. if (!onLeave(e)) return;
  149. _index = parseInt(_index, 10) + 1;
  150. if (!onEnter(e)) {
  151. methods.next();
  152. }
  153. showStep('next');
  154. },
  155. prev: function(e) {
  156. if (isFirstStep()) return;
  157. if (!onLeave(e)) return;
  158. _index = parseInt(_index, 10) - 1;
  159. if (!onEnter(e)) {
  160. methods.prev();
  161. }
  162. showStep('prev');
  163. },
  164. getOptions: function(activeWalkthrough) {
  165. var _wtObj;
  166. //get only current active walkthrough
  167. if (activeWalkthrough) {
  168. _wtObj = {};
  169. _wtObj = _activeWalkthrough;
  170. //get all walkthrough
  171. } else {
  172. _wtObj = [];
  173. for (var wt in _globalWalkthrough) {
  174. _wtObj.push(_globalWalkthrough[wt]);
  175. }
  176. }
  177. return _wtObj;
  178. },
  179. refresh: function() {
  180. // Stricly speaking, a skipDirection should never
  181. // be needed, but I'd rather provide one at this point
  182. // than watch it explode...
  183. showStep('next');
  184. }
  185. }; //end public method
  186. /* Pre-build walkthrough step function. Handles the scrolling to the target
  187. * element.
  188. */
  189. function showStep(skipDirection) {
  190. var options = _activeWalkthrough,
  191. step = options.steps[_index],
  192. targetElement = options._element.find(step.wrapper),
  193. scrollTarget = getScrollParent(targetElement),
  194. maxScroll, scrollTo;
  195. if (step.popup.type !== 'modal' && !targetElement.length) {
  196. if (step.popup.fallback === 'skip' ||
  197. typeof step.popup.fallback === 'undefined') {
  198. methods[skipDirection]();
  199. return;
  200. }
  201. step.popup.type = step.popup.fallback;
  202. }
  203. // For modals, scroll to the top. For tooltips, try and center the target
  204. // (wrapper) element in the screen
  205. maxScroll = scrollTarget[0].scrollHeight - scrollTarget.outerHeight();
  206. scrollTo = step.popup.type === 'modal' ? 0 :
  207. Math.floor(
  208. targetElement.offset().top - ($(window).height() / 2) +
  209. scrollTarget.scrollTop()
  210. );
  211. // @TODO: simplify this logic
  212. //
  213. // Conditions for scrolling:
  214. // 1. new scroll value is not equal to current scroll value
  215. // AND
  216. // a. new scroll value is less than the max scroll, and we are
  217. // currently at the max scroll value
  218. // OR
  219. // b. new scroll value is less than or equal to 0, and the current
  220. // scroll is greater than 0
  221. // OR
  222. // c. new scroll value is greater than 0
  223. if (scrollTarget.scrollTop() !== scrollTo &&
  224. (
  225. (scrollTarget.scrollTop() === maxScroll && scrollTo < maxScroll) ||
  226. (scrollTo <= 0 && scrollTarget.scrollTop() > 0) ||
  227. (scrollTo > 0)
  228. )) {
  229. // Stylistic concerns - fill overlay hole and hide tooltip whilst
  230. // scrolling
  231. $jpWalkthrough.addClass('jpw-scrolling');
  232. $jpwTooltip.fadeOut('fast');
  233. scrollTarget.animate({
  234. scrollTop: scrollTo
  235. }, options.steps[_index].scrollSpeed, buildWalkthrough);
  236. } else {
  237. // Not scrolling, so jump directly to building the walkthrough
  238. buildWalkthrough();
  239. }
  240. }
  241. function buildWalkthrough() {
  242. $jpWalkthrough.removeClass('jpw-scrolling');
  243. var options = _activeWalkthrough,
  244. step = options.steps[_index],
  245. targetElement,
  246. scrollParent,
  247. maxHeight;
  248. // Extend step options with defaults
  249. options.steps[_index] = $.extend(
  250. true, {}, $.fn.pagewalkthrough.defaults.steps[0], step
  251. );
  252. targetElement = options._element.find(step.wrapper);
  253. scrollParent = getScrollParent(targetElement);
  254. $jpwOverlay.show();
  255. if (step.popup.type !== 'modal' && step.popup.type !== 'nohighlight') {
  256. $jpWalkthrough.html('');
  257. //check if wrapper is not empty or undefined
  258. if (step.wrapper === '' || typeof step.wrapper === 'undefined') {
  259. // @TODO should we skip here?
  260. debug('Your walkthrough position is: "' + step.popup.type +
  261. '" but wrapper is empty or undefined. Please check your "' +
  262. _activeId + '" wrapper parameter.'
  263. );
  264. return;
  265. }
  266. maxHeight = scrollParent.outerHeight() - targetElement.offset().top +
  267. scrollParent.offset().top + scrollParent.scrollTop();
  268. // If max height is negative, means we have plenty room so targetElement
  269. // height
  270. maxHeight = maxHeight <= 0 ? targetElement.outerHeight() : maxHeight;
  271. // @todo make it so we don't have to destroy and recreate this element for
  272. // each step
  273. $jpwOverlay.appendTo($jpWalkthrough);
  274. // Overlay hole
  275. $('<div>')
  276. .addClass('overlay-hole')
  277. .height(Math.min(maxHeight, targetElement.outerHeight()))
  278. .width(targetElement.outerWidth())
  279. .css({
  280. // Recommended to be at least twice the inset box-shadow spread
  281. padding: '20px',
  282. position: 'absolute',
  283. top: targetElement.offset().top - 20, // top/left minus padding
  284. left: targetElement.offset().left - 20,
  285. 'z-index': 999998,
  286. 'box-shadow': '0 0 1px 10000px rgba(0, 0, 0, 0.6)'
  287. })
  288. .append(
  289. $('<div>')
  290. .css({
  291. position: 'absolute',
  292. top: 0,
  293. bottom: 0,
  294. left: 0,
  295. right: 0
  296. })
  297. )
  298. .appendTo($jpWalkthrough);
  299. if ($('#jpWalkthrough').length) {
  300. $('#jpWalkthrough').remove();
  301. }
  302. $jpWalkthrough.appendTo('body').show();
  303. $jpwTooltip.show();
  304. showTooltip();
  305. } else if (step.popup.type === 'modal') {
  306. if ($('#jpWalkthrough').length) {
  307. $('#jpWalkthrough').remove();
  308. }
  309. showModal();
  310. } else {
  311. if ($('#jpWalkthrough').length) {
  312. $('#jpWalkthrough').remove();
  313. }
  314. }
  315. showButton('jpwPrevious');
  316. showButton('jpwNext');
  317. showButton('jpwFinish');
  318. }
  319. /*
  320. * SHOW MODAL
  321. */
  322. function showModal() {
  323. var options = _activeWalkthrough,
  324. step = options.steps[_index];
  325. $jpwOverlay.appendTo('body').show().removeClass('transparent');
  326. var textRotation = setRotation(parseInt(step.popup.contentRotation, 10));
  327. $jpwTooltip.css({
  328. 'position': 'absolute',
  329. 'left': '50%',
  330. 'top': '25%',
  331. 'margin-left': -(parseInt(step.popup.width, 10) + 60) / 2 + 'px',
  332. 'z-index': '999999'
  333. });
  334. var tooltipSlide = $('<div id="tooltipTop">' +
  335. '<div id="topLeft"></div>' +
  336. '<div id="topRight"></div>' +
  337. '</div>' +
  338. '<div id="tooltipInner">' +
  339. '</div>' +
  340. '<div id="tooltipBottom">' +
  341. '<div id="bottomLeft"></div>' +
  342. '<div id="bottomRight"></div>' +
  343. '</div>');
  344. $jpWalkthrough.html('');
  345. $jpwTooltip.html('').append(tooltipSlide)
  346. .wrapInner($('<div />', {
  347. id: 'tooltipWrapper',
  348. style: 'width:' + cleanValue(parseInt(step.popup.width, 10) + 30)
  349. }))
  350. .append('<div id="bottom-scratch"></div>')
  351. .appendTo($jpWalkthrough);
  352. $jpWalkthrough.appendTo('body');
  353. $jpwTooltip.show();
  354. $('#tooltipWrapper').css(textRotation);
  355. $('#tooltipInner').append(getContent(step)).show();
  356. $jpWalkthrough.show();
  357. }
  358. /*
  359. * SHOW TOOLTIP
  360. */
  361. function showTooltip() {
  362. var opt = _activeWalkthrough,
  363. step = opt.steps[_index];
  364. var top, left, arrowTop, arrowLeft,
  365. overlayHoleWidth = $('#jpWalkthrough .overlay-hole').outerWidth(),
  366. overlayHoleHeight = $('#jpWalkthrough .overlay-hole').outerHeight(),
  367. overlayHoleTop = $('#jpWalkthrough .overlay-hole').offset().top,
  368. overlayHoleLeft = $('#jpWalkthrough .overlay-hole').offset().left,
  369. arrow = 30;
  370. var textRotation = (typeof step.popup.contentRotation === 'undefined' ||
  371. parseInt(step.popup.contentRotation, 10) === 0) ? clearRotation() :
  372. setRotation(parseInt(step.popup.contentRotation, 10));
  373. // Remove overlay background to prevent double-transparency
  374. if ($('#jpwOverlay').length) {
  375. $('#jpwOverlay').addClass('transparent');
  376. }
  377. var tooltipSlide = $('<div id="tooltipTop">' +
  378. '<div id="topLeft"></div>' +
  379. '<div id="topRight"></div>' +
  380. '</div>' +
  381. '<div id="tooltipInner">' +
  382. '</div>' +
  383. '<div id="tooltipBottom">' +
  384. '<div id="bottomLeft"></div>' +
  385. '<div id="bottomRight"></div>' +
  386. '</div>');
  387. $jpwTooltip.html('').css({
  388. 'margin-left': '0',
  389. 'margin-top': '0',
  390. 'position': 'absolute',
  391. 'z-index': '999999'
  392. })
  393. .append(tooltipSlide)
  394. .wrapInner($('<div />', {
  395. id: 'tooltipWrapper',
  396. style: 'width:' + cleanValue(parseInt(step.popup.width, 10) + 30)
  397. }))
  398. .appendTo($jpWalkthrough);
  399. $jpWalkthrough.appendTo('body').show();
  400. $('#tooltipWrapper').css(textRotation);
  401. $('#tooltipInner').append(getContent(step)).show();
  402. $jpwTooltip.append(
  403. '<span class="' + step.popup.position + '">&nbsp;</span>'
  404. );
  405. switch (step.popup.position) {
  406. case 'top':
  407. top = overlayHoleTop - ($jpwTooltip.height() + (arrow / 2)) +
  408. parseInt(step.popup.offsetVertical, 10) - 86;
  409. left = (overlayHoleLeft + (overlayHoleWidth / 2)) -
  410. ($jpwTooltip.width() / 2) - 5 +
  411. parseInt(step.popup.offsetHorizontal, 10);
  412. arrowLeft = ($jpwTooltip.width() / 2) - arrow +
  413. parseInt(step.popup.offsetArrowHorizontal, 10);
  414. arrowTop = (step.popup.offsetArrowVertical) ?
  415. parseInt(step.popup.offsetArrowVertical, 10) :
  416. '';
  417. break;
  418. case 'right':
  419. top = overlayHoleTop - (arrow / 2) +
  420. parseInt(step.popup.offsetVertical, 10);
  421. left = overlayHoleLeft + overlayHoleWidth + (arrow / 2) +
  422. parseInt(step.popup.offsetHorizontal, 10) + 105;
  423. arrowTop = arrow + parseInt(step.popup.offsetArrowVertical, 10);
  424. arrowLeft = (step.popup.offsetArrowHorizontal) ?
  425. parseInt(step.popup.offsetArrowHorizontal, 10) :
  426. '';
  427. break;
  428. case 'bottom':
  429. top = overlayHoleTop + overlayHoleHeight +
  430. parseInt(step.popup.offsetVertical, 10) + 86;
  431. left = (overlayHoleLeft + (overlayHoleWidth / 2)) -
  432. ($jpwTooltip.width() / 2) - 5 +
  433. parseInt(step.popup.offsetHorizontal, 10);
  434. arrowLeft = (($jpwTooltip.width() / 2) - arrow) +
  435. parseInt(step.popup.offsetArrowHorizontal, 10);
  436. arrowTop = (step.popup.offsetArrowVertical) ?
  437. parseInt(step.popup.offsetArrowVertical, 10) :
  438. '';
  439. break;
  440. case 'left':
  441. top = overlayHoleTop - (arrow / 2) +
  442. parseInt(step.popup.offsetVertical, 10);
  443. left = overlayHoleLeft - $jpwTooltip.width() - (arrow) +
  444. parseInt(step.popup.offsetHorizontal, 10) - 105;
  445. arrowTop = arrow + parseInt(step.popup.offsetArrowVertical, 10);
  446. arrowLeft = (step.popup.offsetArrowVertical) ?
  447. parseInt(step.popup.offsetArrowHorizontal, 10) :
  448. '';
  449. break;
  450. }
  451. $('#jpwTooltip span.' + step.popup.position).css({
  452. 'top': cleanValue(arrowTop),
  453. 'left': cleanValue(arrowLeft)
  454. });
  455. $jpwTooltip.css({
  456. 'top': cleanValue(top),
  457. 'left': cleanValue(left)
  458. });
  459. $jpWalkthrough.show();
  460. }
  461. /* Get the content for a step. First attempts to treat step.popup.content
  462. * as a selector. If this fails, or returns an empty result set, it falls
  463. * back to return the value of step.popup.content.
  464. *
  465. * This allows both selectors and literal content to be provided in the
  466. * content option.
  467. *
  468. * @param {Object} step The step data to return the content for
  469. */
  470. function getContent(step) {
  471. var option = step.popup.content,
  472. content;
  473. try {
  474. content = $('body').find(option).html();
  475. } catch(e) {
  476. }
  477. return content || option;
  478. }
  479. /* Render a control button outside the #tooltipInner element.
  480. *
  481. * @param {String} id The button identifier within the
  482. * options.buttons hash (e.g. 'jpwNext')
  483. * @param {jQuery|String} appendTo (Optional) The element or selector to
  484. * append the button to. Defaults to
  485. * #tooltipWrapper
  486. */
  487. function showButton(id, appendTo) {
  488. if ($('#' + id).length) return;
  489. var btn = _activeWalkthrough.buttons[id];
  490. appendTo = appendTo || '#tooltipWrapper';
  491. // Check that button is defined
  492. if (!btn) return;
  493. // Check that button should be shown
  494. if ((typeof btn.show === 'function' && !btn.show()) || !btn.show) {
  495. return;
  496. }
  497. // Append button
  498. $(appendTo).append($('<a />', {
  499. id: id,
  500. html: btn.i18n
  501. }));
  502. }
  503. /**
  504. /* CALLBACK
  505. /*/
  506. //callback for onLoadHidden cookie
  507. function onCookieLoad(options) {
  508. /*jshint validthis: true */
  509. for (var i = 0; i < _elements.length; i++) {
  510. if (typeof(options[_elements[i]].onCookieLoad) === 'function') {
  511. options[_elements[i]].onCookieLoad.call(this);
  512. }
  513. }
  514. return false;
  515. }
  516. function onLeave(e) {
  517. /*jshint validthis: true */
  518. var options = _activeWalkthrough;
  519. if (typeof options.steps[_index].onLeave === 'function') {
  520. if (!options.steps[_index].onLeave.call(this, e, _index)) {
  521. return false;
  522. }
  523. }
  524. return true;
  525. }
  526. //callback for onEnter step
  527. function onEnter(e) {
  528. /*jshint validthis: true */
  529. var options = _activeWalkthrough;
  530. if (typeof options.steps[_index].onEnter === 'function') {
  531. return options.steps[_index].onEnter.call(this, e, _index);
  532. }
  533. return true;
  534. }
  535. //callback for onRestart help
  536. function onRestart(e) {
  537. /*jshint validthis: true */
  538. var options = _activeWalkthrough;
  539. //set help mode to true
  540. _isWalkthroughActive = true;
  541. methods.restart(e);
  542. if (typeof options.onRestart === 'function') {
  543. if (!options.onRestart.call(this)) {
  544. return false;
  545. }
  546. }
  547. return true;
  548. }
  549. //callback for before all first walkthrough element loaded
  550. function onBeforeShow() {
  551. /*jshint validthis: true */
  552. var options = _activeWalkthrough || {};
  553. _index = 0;
  554. if (typeof(options.onBeforeShow) === 'function') {
  555. if (!options.onBeforeShow.call(this)) {
  556. return false;
  557. }
  558. }
  559. return true;
  560. }
  561. //callback for after all first walkthrough element loaded
  562. function onAfterShow() {
  563. /*jshint validthis: true */
  564. var options = _activeWalkthrough;
  565. _index = 0;
  566. if (typeof(options.onAfterShow) === 'function') {
  567. if (!options.onAfterShow.call(this)) {
  568. return false;
  569. }
  570. }
  571. return true;
  572. }
  573. /**
  574. * HELPERS
  575. */
  576. function debug(message) {
  577. if (window.console && window.console.log)
  578. window.console.log(message);
  579. }
  580. function clearRotation() {
  581. var rotationStyle = {
  582. '-webkit-transform': 'none', //safari
  583. '-moz-transform': 'none', //firefox
  584. '-o-transform': 'none', //opera
  585. 'filter': 'none', //IE7
  586. '-ms-transform': 'none' //IE8+
  587. };
  588. return rotationStyle;
  589. }
  590. function setRotation(angle) {
  591. //for IE7 & IE8
  592. var M11, M12, M21, M22, deg2rad, rad;
  593. //degree to radian
  594. deg2rad = Math.PI * 2 / 360;
  595. rad = angle * deg2rad;
  596. M11 = Math.cos(rad);
  597. M12 = Math.sin(rad);
  598. M21 = Math.sin(rad);
  599. M22 = Math.cos(rad);
  600. var rotationStyle = {
  601. '-webkit-transform': 'rotate(' + parseInt(angle, 10) + 'deg)', //safari
  602. '-moz-transform': 'rotate(' + parseInt(angle, 10) + 'deg)', //firefox
  603. '-o-transform': 'rotate(' + parseInt(angle, 10) + 'deg)', //opera
  604. '-ms-transform': 'rotate(' + parseInt(angle, 10) + 'deg)' //IE9+
  605. };
  606. return rotationStyle;
  607. }
  608. function cleanValue(value) {
  609. if (typeof value === 'string') {
  610. if (value.toLowerCase().indexOf('px') === -1) {
  611. return value + 'px';
  612. } else {
  613. return value;
  614. }
  615. } else {
  616. return value + 'px';
  617. }
  618. }
  619. function setCookie(cName, value, exdays) {
  620. var exdate = new Date();
  621. exdate.setDate(exdate.getDate() + exdays);
  622. var cValue = encodeURIComponent(value) + ((exdays == null) ? '' :
  623. '; expires=' + exdate.toUTCString());
  624. document.cookie = [cName, '=', cValue].join('');
  625. }
  626. function getCookie(cName) {
  627. var i, x, y, ARRcookies = document.cookie.split(';');
  628. for (i = 0; i < ARRcookies.length; i++) {
  629. x = ARRcookies[i].substr(0, ARRcookies[i].indexOf('='));
  630. y = ARRcookies[i].substr(ARRcookies[i].indexOf('=') + 1);
  631. x = x.replace(/^\s+|\s+$/g, '');
  632. if (x === cName) {
  633. return decodeURIComponent(y);
  634. }
  635. }
  636. }
  637. /* Returns true if the current step is the last step in the walkthrough.
  638. *
  639. * @return {Boolean} true if user is currently viewing last step; false
  640. * otherwise
  641. */
  642. function isLastStep() {
  643. return _index === (_activeWalkthrough.steps.length - 1);
  644. }
  645. /* Returns true if the current step is the first step in the walkthrough.
  646. *
  647. * @return {Boolean} true if user is currently viewing first step; false
  648. * otherwise
  649. */
  650. function isFirstStep() {
  651. return _index === 0;
  652. }
  653. /* Get the first scrollable parent of the specified element.
  654. * Adapted from jQueryUI's [scrollParent](api.jqueryui.com/scrollParent/).
  655. *
  656. * @param {jQuery} element The element to find the scrollable parent of
  657. *
  658. * @return {jQuery} The first scrollable parent, or an empty jQuery object if
  659. * either of the following is true:
  660. * 1. `element`'s position is 'fixed'
  661. * 2. `element`'s position is 'absolute', and the parent's
  662. * is 'static'
  663. */
  664. function getScrollParent(element) {
  665. if (!(element instanceof $)) {
  666. element = $(element);
  667. }
  668. element = element.first();
  669. var position = element.css('position'),
  670. excludeStaticParent = position === 'absolute',
  671. scrollParent = element.parents().filter(function() {
  672. var parent = $(this);
  673. if (excludeStaticParent && parent.css('position') === 'static') {
  674. return false;
  675. }
  676. return (/(auto|scroll)/).test(
  677. parent.css('overflow') + parent.css('overflow-y') +
  678. parent.css('overflow-x')
  679. );
  680. }).eq(0);
  681. return position === 'fixed' ? $() : !scrollParent.length ?
  682. $('body') : scrollParent;
  683. }
  684. /**
  685. * BUTTON CLOSE CLICK
  686. */
  687. /* Close and finish tour buttons clicks */
  688. $(document).on('click', '#jpwClose, #jpwFinish', methods.close);
  689. /* Next button clicks
  690. */
  691. $(document).on('click', '#jpwNext', function() {
  692. $.pagewalkthrough('next');
  693. });
  694. /* Previous button clicks
  695. */
  696. $(document).on('click', '#jpwPrevious', function() {
  697. $.pagewalkthrough('prev');
  698. });
  699. $(document).on(
  700. 'click',
  701. '#jpwOverlay, #jpwTooltip',
  702. function(ev) {
  703. ev.stopPropagation();
  704. ev.stopImmediatePropagation();
  705. }
  706. );
  707. /**
  708. * DRAG & DROP
  709. */
  710. /**
  711. * MAIN PLUGIN
  712. */
  713. $.pagewalkthrough = $.fn.pagewalkthrough = function(method) {
  714. if (methods[method]) {
  715. return methods[method].apply(this, [].slice.call(arguments, 1));
  716. } else if (typeof method === 'object' || !method) {
  717. methods.init.apply(this, arguments);
  718. // render the overlay on it has a default walkthrough set to show onload
  719. if (_hasDefault && _counter < 2) {
  720. setTimeout(function() {
  721. methods.renderOverlay();
  722. }, 500);
  723. }
  724. } else {
  725. $.error('Method ' + method + ' does not exist on jQuery.pagewalkthrough');
  726. }
  727. };
  728. /* #### <a name="default-options">Options</a>
  729. *
  730. * Default options for each walkthrough.
  731. * User options extend these defaults.
  732. */
  733. $.fn.pagewalkthrough.defaults = {
  734. /* Array of steps to show
  735. */
  736. steps: [
  737. {
  738. // jQuery selector for the element to highlight for this step
  739. wrapper: '',
  740. // ##### <a name="popup-options">Popup options</a>
  741. popup: {
  742. // Selector for the element which contains the content, or the literal
  743. // content
  744. content: '',
  745. // Popup type - either modal, tooltip or nohighlight.
  746. // See [Popup Types](/pages/popup-types.html)
  747. type: 'modal',
  748. // Position for tooltip and nohighlight style popups - either top,
  749. // left, right or bottom
  750. position: 'top',
  751. // Horizontal offset for the walkthrough
  752. offsetHorizontal: 0,
  753. // Vertical offset for the walkthrough
  754. offsetVertical: 0,
  755. // Horizontal offset for the arrow
  756. offsetArrowHorizontal: 0,
  757. // Vertical offset for the arrow
  758. offsetArrowVertical: 0,
  759. // Default width for each popup
  760. width: '320',
  761. // Amount in degrees to rotate the content by
  762. contentRotation: 0
  763. },
  764. // Automatically scroll to the content for the step
  765. autoScroll: true,
  766. // Speed to use when scrolling to elements
  767. scrollSpeed: 1000,
  768. // Prevent the user from scrolling away from the content
  769. lockScrolling: false,
  770. // Callback when entering the step
  771. onEnter: null,
  772. // Callback when leaving the step
  773. onLeave: null
  774. }
  775. ],
  776. // **(Required)** Walkthrough name. Should be a unique name to identify the
  777. // walkthrough, as it will
  778. // be used in the cookie name
  779. name: null,
  780. // Automatically show the walkthrough when the page is loaded. If multiple
  781. // walkthroughs set this to true, only the first walkthrough is shown
  782. // automatically
  783. onLoad: true,
  784. // Callback to be executed before the walkthrough is shown
  785. onBeforeShow: null,
  786. // Callback executed after the walkthrough is shown
  787. onAfterShow: null,
  788. // Callback executed in the event that 'restart' is triggered
  789. onRestart: null,
  790. // Callback executed when the walkthrough is closed. The walkthrough can be
  791. // closed by the user clicking the close button in the top right, or
  792. // clicking the finish button on the last step
  793. onClose: null,
  794. // Callback executed when cookie has been set after a walkthrough has been
  795. // closed
  796. onCookieLoad: null,
  797. /* ##### <a name="controls-options">Walkthrough controls</a>
  798. *
  799. * Hash of buttons to show. Object keys are used as the button element's ID
  800. */
  801. buttons: {
  802. // ID of the button
  803. jpwClose: {
  804. // Translation string for the button
  805. i18n: 'Click here to close',
  806. // Whether or not to show the button. Can be a boolean value, or a
  807. // function which returns a boolean value
  808. show: true
  809. },
  810. jpwNext: {
  811. i18n: 'Next &rarr;',
  812. // Function which resolves to a boolean
  813. show: function() {
  814. return !isLastStep();
  815. }
  816. },
  817. jpwPrevious: {
  818. i18n: '&larr; Previous',
  819. show: function() {
  820. return !isFirstStep();
  821. }
  822. },
  823. jpwFinish: {
  824. i18n: 'Finish &#10004;',
  825. show: function() {
  826. return isLastStep();
  827. }
  828. }
  829. }
  830. };
  831. }(jQuery, window, document));