1
0

jquery.smartWizard.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * SmartWizard 3.3.1 plugin
  3. * jQuery Wizard control Plugin
  4. * by Dipu
  5. *
  6. * Refactored and extended:
  7. * https://github.com/mstratman/jQuery-Smart-Wizard
  8. *
  9. * Original URLs:
  10. * http://www.techlaboratory.net
  11. * http://tech-laboratory.blogspot.com
  12. */
  13. function SmartWizard(target, options) {
  14. this.target = target;
  15. this.options = options;
  16. this.curStepIdx = options.selected;
  17. this.steps = $(target).children("ul").children("li").children("a"); // Get all anchors
  18. this.contentWidth = 0;
  19. this.msgBox = '';//$('<div class="msgBox"><div class="content"></div><a href="#" class="close">X</a></div>');
  20. this.elmStepContainer = $('<div></div>').addClass("stepContainer");
  21. this.loader = $('<div>Loading</div>').addClass("loader");
  22. this.buttons = {
  23. next : $('<a>'+options.labelNext+'</a>').attr("href","#").addClass("buttonNext btn btn-default pull-right"),
  24. previous : $('<a>'+options.labelPrevious+'</a>').attr("href","#").addClass("buttonPrevious btn btn-default pull-left"),
  25. finish : $('<a>'+options.labelFinish+'</a>').attr("href","#").addClass("buttonFinish btn btn-primary pull-right")
  26. };
  27. /*
  28. * Private functions
  29. */
  30. var _init = function($this) {
  31. var elmActionBar = $('<div></div>').addClass("actionBar");
  32. elmActionBar.append($this.msgBox);
  33. $('.close',$this.msgBox).click(function() {
  34. $this.msgBox.fadeOut("normal");
  35. return false;
  36. });
  37. var allDivs = $this.target.children('div');
  38. // CHeck if ul with steps has been added by user, if not add them
  39. if($this.target.children('ul').length == 0 ){
  40. var ul = $("<ul/>");
  41. target.prepend(ul)
  42. // for each div create a li
  43. allDivs.each(function(i,e){
  44. var title = $(e).first().children(".StepTitle").text();
  45. var s = $(e).attr("id")
  46. // if referenced div has no id, add one.
  47. if (s==undefined){
  48. s = "step-"+(i+1)
  49. $(e).attr("id",s);
  50. }
  51. var span = $("<span/>").addClass("stepDesc").text(title);
  52. var li = $("<li></li>").append($("<a></a>").attr("href", "#" + s).append($("<label></label>").addClass("stepNumber").text(i + 1)).append(span));
  53. ul.append(li);
  54. });
  55. // (re)initialise the steps property
  56. $this.steps = $(target).children("ul").children("li").children("a"); // Get all anchors
  57. }
  58. $this.target.children('ul').addClass("anchor");
  59. allDivs.addClass("content");
  60. // highlight steps with errors
  61. if($this.options.errorSteps && $this.options.errorSteps.length>0){
  62. $.each($this.options.errorSteps, function(i, n){
  63. $this.setError({ stepnum: n, iserror:true });
  64. });
  65. }
  66. $this.elmStepContainer.append(allDivs);
  67. elmActionBar.append($this.loader);
  68. $this.target.append($this.elmStepContainer);
  69. if ($this.options.includeFinishButton){
  70. elmActionBar.append($this.buttons.finish)
  71. }
  72. elmActionBar.append($this.buttons.next)
  73. .append($this.buttons.previous);
  74. $this.target.append(elmActionBar);
  75. this.contentWidth = $this.elmStepContainer.width();
  76. $($this.buttons.next).click(function() {
  77. $this.goForward();
  78. return false;
  79. });
  80. $($this.buttons.previous).click(function() {
  81. $this.goBackward();
  82. return false;
  83. });
  84. $($this.buttons.finish).click(function() {
  85. if(!$(this).hasClass('buttonDisabled')){
  86. if($.isFunction($this.options.onFinish)) {
  87. var context = { fromStep: $this.curStepIdx + 1 };
  88. if(!$this.options.onFinish.call(this,$($this.steps), context)){
  89. return false;
  90. }
  91. }else{
  92. var frm = $this.target.parents('form');
  93. if(frm && frm.length){
  94. var st = $($this.target).find("ul > li").length;
  95. $($this.target).find("a[rel="+(st-1)+"]").addClass("active");
  96. $($this.target).find("a[rel="+st+"]").addClass("active");
  97. frm.submit();
  98. }
  99. }
  100. }
  101. return false;
  102. });
  103. $($this.steps).bind("click", function(e){
  104. if($this.steps.index(this) == $this.curStepIdx){
  105. return false;
  106. }
  107. var nextStepIdx = $this.steps.index(this);
  108. var isDone = $this.steps.eq(nextStepIdx).attr("isDone") - 0;
  109. if(isDone == 1){
  110. _loadContent($this, nextStepIdx);
  111. }
  112. return false;
  113. });
  114. // Enable keyboard navigation
  115. if($this.options.keyNavigation){
  116. $(document).keyup(function(e){
  117. if(e.which==39){ // Right Arrow
  118. $this.goForward();
  119. }else if(e.which==37){ // Left Arrow
  120. $this.goBackward();
  121. }
  122. });
  123. }
  124. // Prepare the steps
  125. _prepareSteps($this);
  126. // Show the first slected step
  127. _loadContent($this, $this.curStepIdx);
  128. };
  129. var _prepareSteps = function($this) {
  130. if(! $this.options.enableAllSteps){
  131. $($this.steps, $this.target).removeClass("selected").removeClass("done").addClass("disabled");
  132. $($this.steps, $this.target).attr("isDone",0);
  133. }else{
  134. $($this.steps, $this.target).removeClass("selected").removeClass("disabled").addClass("done");
  135. $($this.steps, $this.target).attr("isDone",1);
  136. }
  137. $($this.steps, $this.target).each(function(i){
  138. $($(this).attr("href").replace(/^.+#/, '#'), $this.target).hide();
  139. $(this).attr("rel",i+1);
  140. });
  141. };
  142. var _step = function ($this, selStep) {
  143. return $(
  144. $(selStep, $this.target).attr("href").replace(/^.+#/, '#'),
  145. $this.target
  146. );
  147. };
  148. var _loadContent = function($this, stepIdx) {
  149. var selStep = $this.steps.eq(stepIdx);
  150. var ajaxurl = $this.options.contentURL;
  151. var ajaxurl_data = $this.options.contentURLData;
  152. var hasContent = selStep.data('hasContent');
  153. var stepNum = stepIdx+1;
  154. if (ajaxurl && ajaxurl.length>0) {
  155. if ($this.options.contentCache && hasContent) {
  156. _showStep($this, stepIdx);
  157. } else {
  158. var ajax_args = {
  159. url: ajaxurl,
  160. type: $this.options.ajaxType,
  161. data: ({step_number : stepNum}),
  162. dataType: "text",
  163. beforeSend: function(){
  164. $this.loader.show();
  165. },
  166. error: function(){
  167. $this.loader.hide();
  168. },
  169. success: function(res){
  170. $this.loader.hide();
  171. if(res && res.length>0){
  172. selStep.data('hasContent',true);
  173. _step($this, selStep).html(res);
  174. _showStep($this, stepIdx);
  175. }
  176. }
  177. };
  178. if (ajaxurl_data) {
  179. ajax_args = $.extend(ajax_args, ajaxurl_data(stepNum));
  180. }
  181. $.ajax(ajax_args);
  182. }
  183. }else{
  184. _showStep($this,stepIdx);
  185. }
  186. };
  187. var _showStep = function($this, stepIdx) {
  188. var selStep = $this.steps.eq(stepIdx);
  189. var curStep = $this.steps.eq($this.curStepIdx);
  190. if(stepIdx != $this.curStepIdx){
  191. if($.isFunction($this.options.onLeaveStep)) {
  192. var context = { fromStep: $this.curStepIdx+1, toStep: stepIdx+1 };
  193. if (! $this.options.onLeaveStep.call($this,$(curStep), context)){
  194. return false;
  195. }
  196. }
  197. }
  198. //$this.elmStepContainer.height(_step($this, selStep).outerHeight());
  199. var prevCurStepIdx = $this.curStepIdx;
  200. $this.curStepIdx = stepIdx;
  201. if ($this.options.transitionEffect == 'slide'){
  202. _step($this, curStep).slideUp("fast",function(e){
  203. _step($this, selStep).slideDown("fast");
  204. _setupStep($this,curStep,selStep);
  205. });
  206. } else if ($this.options.transitionEffect == 'fade'){
  207. _step($this, curStep).fadeOut("fast",function(e){
  208. _step($this, selStep).fadeIn("fast");
  209. _setupStep($this,curStep,selStep);
  210. });
  211. } else if ($this.options.transitionEffect == 'slideleft'){
  212. var nextElmLeft = 0;
  213. var nextElmLeft1 = null;
  214. var nextElmLeft = null;
  215. var curElementLeft = 0;
  216. if(stepIdx > prevCurStepIdx){
  217. nextElmLeft1 = $this.elmStepContainer.width() + 10;
  218. nextElmLeft2 = 0;
  219. curElementLeft = 0 - _step($this, curStep).outerWidth();
  220. } else {
  221. nextElmLeft1 = 0 - _step($this, selStep).outerWidth() + 20;
  222. nextElmLeft2 = 0;
  223. curElementLeft = 10 + _step($this, curStep).outerWidth();
  224. }
  225. if (stepIdx == prevCurStepIdx) {
  226. nextElmLeft1 = $($(selStep, $this.target).attr("href"), $this.target).outerWidth() + 20;
  227. nextElmLeft2 = 0;
  228. curElementLeft = 0 - $($(curStep, $this.target).attr("href"), $this.target).outerWidth();
  229. } else {
  230. $($(curStep, $this.target).attr("href"), $this.target).animate({left:curElementLeft},"fast",function(e){
  231. $($(curStep, $this.target).attr("href"), $this.target).hide();
  232. });
  233. }
  234. _step($this, selStep).css("left",nextElmLeft1).show().animate({left:nextElmLeft2},"fast",function(e){
  235. _setupStep($this,curStep,selStep);
  236. });
  237. } else {
  238. _step($this, curStep).hide();
  239. _step($this, selStep).show();
  240. _setupStep($this,curStep,selStep);
  241. }
  242. return true;
  243. };
  244. var _setupStep = function($this, curStep, selStep) {
  245. if($(curStep).attr("rel") > 1){
  246. var stepn = parseInt($(curStep).attr("rel"));
  247. $($this.target).find("a[rel="+(stepn-1)+"]").addClass("active");
  248. if($(selStep).attr("rel") == $($this.target).find("ul > li").length){
  249. $(selStep).parent("li").prev("li").find("a").addClass("active");
  250. $(selStep).addClass("active");
  251. }
  252. }
  253. $(curStep, $this.target).removeClass("selected");
  254. $(curStep, $this.target).addClass("done");
  255. $(selStep, $this.target).removeClass("disabled");
  256. $(selStep, $this.target).removeClass("done");
  257. $(selStep, $this.target).addClass("selected");
  258. $(selStep, $this.target).attr("isDone",1);
  259. _adjustButton($this);
  260. if($.isFunction($this.options.onShowStep)) {
  261. var context = { fromStep: parseInt($(curStep).attr('rel')), toStep: parseInt($(selStep).attr('rel')) };
  262. if(! $this.options.onShowStep.call(this,$(selStep),context)){
  263. return false;
  264. }
  265. }
  266. if ($this.options.noForwardJumping) {
  267. // +2 == +1 (for index to step num) +1 (for next step)
  268. for (var i = $this.curStepIdx + 2; i <= $this.steps.length; i++) {
  269. $this.disableStep(i);
  270. }
  271. }
  272. };
  273. var _adjustButton = function($this) {
  274. if (! $this.options.cycleSteps){
  275. if (0 >= $this.curStepIdx) {
  276. $($this.buttons.previous).addClass("buttonDisabled disabled");
  277. if ($this.options.hideButtonsOnDisabled) {
  278. $($this.buttons.previous).hide();
  279. }
  280. }else{
  281. $($this.buttons.previous).removeClass("buttonDisabled disabled");
  282. if ($this.options.hideButtonsOnDisabled) {
  283. $($this.buttons.previous).show();
  284. }
  285. }
  286. if (($this.steps.length-1) <= $this.curStepIdx){
  287. $($this.buttons.next).addClass("buttonDisabled disabled");
  288. if ($this.options.hideButtonsOnDisabled) {
  289. $($this.buttons.next).hide();
  290. }
  291. }else{
  292. $($this.buttons.next).removeClass("buttonDisabled disabled");
  293. if ($this.options.hideButtonsOnDisabled) {
  294. $($this.buttons.next).show();
  295. }
  296. }
  297. }
  298. // Finish Button
  299. $this.enableFinish($this.options.enableFinishButton);
  300. };
  301. /*
  302. * Public methods
  303. */
  304. SmartWizard.prototype.goForward = function(){
  305. var nextStepIdx = this.curStepIdx + 1;
  306. if (this.steps.length <= nextStepIdx){
  307. if (! this.options.cycleSteps){
  308. return false;
  309. }
  310. nextStepIdx = 0;
  311. }
  312. _loadContent(this, nextStepIdx);
  313. };
  314. SmartWizard.prototype.goBackward = function(){
  315. var nextStepIdx = this.curStepIdx-1;
  316. if (0 > nextStepIdx){
  317. if (! this.options.cycleSteps){
  318. return false;
  319. }
  320. nextStepIdx = this.steps.length - 1;
  321. }
  322. _loadContent(this, nextStepIdx);
  323. };
  324. SmartWizard.prototype.goToStep = function(stepNum){
  325. var stepIdx = stepNum - 1;
  326. if (stepIdx >= 0 && stepIdx < this.steps.length) {
  327. _loadContent(this, stepIdx);
  328. }
  329. };
  330. SmartWizard.prototype.enableStep = function(stepNum) {
  331. var stepIdx = stepNum - 1;
  332. if (stepIdx == this.curStepIdx || stepIdx < 0 || stepIdx >= this.steps.length) {
  333. return false;
  334. }
  335. var step = this.steps.eq(stepIdx);
  336. $(step, this.target).attr("isDone",1);
  337. $(step, this.target).removeClass("disabled").removeClass("selected").addClass("done");
  338. }
  339. SmartWizard.prototype.disableStep = function(stepNum) {
  340. var stepIdx = stepNum - 1;
  341. if (stepIdx == this.curStepIdx || stepIdx < 0 || stepIdx >= this.steps.length) {
  342. return false;
  343. }
  344. var step = this.steps.eq(stepIdx);
  345. $(step, this.target).attr("isDone",0);
  346. $(step, this.target).removeClass("done").removeClass("selected").addClass("disabled");
  347. }
  348. SmartWizard.prototype.currentStep = function() {
  349. return this.curStepIdx + 1;
  350. }
  351. SmartWizard.prototype.showMessage = function (msg) {
  352. $('.content', this.msgBox).html(msg);
  353. this.msgBox.show();
  354. }
  355. SmartWizard.prototype.enableFinish = function (enable) {
  356. // Controll status of finish button dynamically
  357. // just call this with status you want
  358. this.options.enableFinishButton = enable;
  359. if (this.options.includeFinishButton){
  360. if (!this.steps.hasClass('disabled') || this.options.enableFinishButton){
  361. $(this.buttons.finish).removeClass("buttonDisabled disabled");
  362. if (this.options.hideButtonsOnDisabled) {
  363. $(this.buttons.finish).show();
  364. }
  365. }else{
  366. $(this.buttons.finish).addClass("buttonDisabled disabled");
  367. if (this.options.hideButtonsOnDisabled) {
  368. $(this.buttons.finish).hide();
  369. }
  370. }
  371. }
  372. return this.options.enableFinishButton;
  373. }
  374. SmartWizard.prototype.hideMessage = function () {
  375. this.msgBox.fadeOut("normal");
  376. }
  377. SmartWizard.prototype.showError = function(stepnum) {
  378. this.setError(stepnum, true);
  379. }
  380. SmartWizard.prototype.hideError = function(stepnum) {
  381. this.setError(stepnum, false);
  382. }
  383. SmartWizard.prototype.setError = function(stepnum,iserror) {
  384. if (typeof stepnum == "object") {
  385. iserror = stepnum.iserror;
  386. stepnum = stepnum.stepnum;
  387. }
  388. if (iserror){
  389. $(this.steps.eq(stepnum-1), this.target).addClass('error')
  390. }else{
  391. $(this.steps.eq(stepnum-1), this.target).removeClass("error");
  392. }
  393. }
  394. SmartWizard.prototype.fixHeight = function(){
  395. var height = 0;
  396. var selStep = this.steps.eq(this.curStepIdx);
  397. var stepContainer = _step(this, selStep);
  398. stepContainer.children().each(function() {
  399. if($(this).is(':visible')) {
  400. height += $(this).outerHeight(true);
  401. }
  402. });
  403. // These values (5 and 20) are experimentally chosen.
  404. stepContainer.height(height + 5);
  405. this.elmStepContainer.height(height + 20);
  406. }
  407. _init(this);
  408. };
  409. (function($){
  410. $.fn.smartWizard = function(method) {
  411. var args = arguments;
  412. var rv = undefined;
  413. var allObjs = this.each(function() {
  414. var wiz = $(this).data('smartWizard');
  415. if (typeof method == 'object' || ! method || ! wiz) {
  416. var options = $.extend({}, $.fn.smartWizard.defaults, method || {});
  417. if (! wiz) {
  418. wiz = new SmartWizard($(this), options);
  419. $(this).data('smartWizard', wiz);
  420. }
  421. } else {
  422. if (typeof SmartWizard.prototype[method] == "function") {
  423. rv = SmartWizard.prototype[method].apply(wiz, Array.prototype.slice.call(args, 1));
  424. return rv;
  425. } else {
  426. $.error('Method ' + method + ' does not exist on jQuery.smartWizard');
  427. }
  428. }
  429. });
  430. if (rv === undefined) {
  431. return allObjs;
  432. } else {
  433. return rv;
  434. }
  435. };
  436. // Default Properties and Events
  437. $.fn.smartWizard.defaults = {
  438. selected: 0, // Selected Step, 0 = first step
  439. keyNavigation: true, // Enable/Disable key navigation(left and right keys are used if enabled)
  440. enableAllSteps: false,
  441. transitionEffect: 'fade', // Effect on navigation, none/fade/slide/slideleft
  442. contentURL:null, // content url, Enables Ajax content loading
  443. contentCache:true, // cache step contents, if false content is fetched always from ajax url
  444. cycleSteps: false, // cycle step navigation
  445. enableFinishButton: false, // make finish button enabled always
  446. hideButtonsOnDisabled: false, // when the previous/next/finish buttons are disabled, hide them instead?
  447. errorSteps:[], // Array Steps with errors
  448. labelNext:'Next',
  449. labelPrevious:'Previous',
  450. labelFinish:'Finish',
  451. noForwardJumping: false,
  452. ajaxType: "POST",
  453. onLeaveStep: null, // triggers when leaving a step
  454. onShowStep: null, // triggers when showing a step
  455. onFinish: null, // triggers when Finish button is clicked
  456. includeFinishButton : true // Add the finish button
  457. };
  458. })(jQuery);