/*
* Copyright (C) 2013 Panopta, Andrew Moffat
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function ($) {
$.fn.wizard = function(args) {
return new Wizard(this, args);
};
$.fn.wizard.logging = false;
var WizardCard = function(wizard, card, index, prev, next) {
this.wizard = wizard;
this.index = index;
this.prev = prev;
this.next = next;
this.el = card;
this.title = card.find("h3").first().text();
this.name = card.data("cardname") || this.title;
this.nav = this._createNavElement(this.title, index);
this._disabled = false;
this._loaded = false;
this._events = {};
};
WizardCard.prototype = {
select: function() {
this.log("selecting");
if (!this.isSelected()) {
this.nav.addClass("active");
this.el.show();
if (!this._loaded) {
this.trigger("loaded");
this.reload();
}
this.trigger("selected");
}
/*
* this is ugly, but we're handling the changing of the wizard's
* buttons here, in the WizardCard select. so when a card is
* selected, we're figuring out if we're the first card or the
* last card and changing the wizard's buttons via the guts of
* the wizard
*
* ideally this logic should be encapsulated by some wizard methods
* that we can call from here, instead of messing with the guts
*/
var w = this.wizard;
// The back button is only disabled on this first card...
w.backButton.toggleClass("disabled", this.index == 0);
if (this.index >= w._cards.length-1) {
this.log("on last card, changing next button to submit");
w.changeNextButton(w.args.buttons.submitText, "btn-success");
w._readyToSubmit = true;
w.trigger("readySubmit");
}
else {
w._readyToSubmit = false;
w.changeNextButton(w.args.buttons.nextText, "btn-primary");
}
return this;
},
_createNavElement: function(name, i) {
var li = $('
');
var a = $('');
a.data("navindex", i);
li.append(a);
a.append(' ');
a.append(name);
return li;
},
markVisited: function() {
this.log("marking as visited");
this.nav.addClass("already-visited");
this.trigger("markVisited");
return this;
},
unmarkVisited: function() {
this.log("unmarking as visited");
this.nav.removeClass("already-visited");
this.trigger("unmarkVisited");
return this;
},
deselect: function() {
this.nav.removeClass("active");
this.el.hide();
this.trigger("deselect");
return this;
},
enable: function() {
this.log("enabling");
// Issue #38 Hiding navigation link when hide card
// Awaiting approval
//
// this.nav.removeClass('hide');
this.nav.addClass("active");
this._disabled = false;
this.trigger("enabled");
return this;
},
disable: function(hideCard) {
this.log("disabling");
this._disabled = true;
this.nav.removeClass("active already-visited");
if (hideCard) {
this.el.hide();
// Issue #38 Hiding navigation link when hide card
// Awaiting approval
//
// this.nav.addClass('hide');
}
this.trigger("disabled");
return this;
},
isDisabled: function() {
return this._disabled;
},
alreadyVisited: function() {
return this.nav.hasClass("already-visited");
},
isSelected: function() {
return this.nav.hasClass("active");
},
reload: function() {
this._loaded = true;
this.trigger("reload");
return this;
},
on: function() {
return this.wizard.on.apply(this, arguments);
},
trigger: function() {
this.callListener("on"+arguments[0]);
return this.wizard.trigger.apply(this, arguments);
},
/*
* displays an alert box on the current card
*/
toggleAlert: function(msg, toggle) {
this.log("toggling alert to: " + toggle);
toggle = typeof(toggle) == "undefined" ? true : toggle;
if (toggle) {this.trigger("showAlert");}
else {this.trigger("hideAlert");}
var div;
var alert = this.el.children("h3").first().next("div.alert");
if (alert.length == 0) {
/*
* we're hiding anyways, so no need to create anything.
* we'll do that if we ever are actually showing the alert
*/
if (!toggle) {return this;}
this.log("couldn't find existing alert div, creating one");
div = $("");
div.addClass("alert");
div.addClass("hide");
div.insertAfter(this.el.find("h3").first());
}
else {
this.log("found existing alert div");
div = alert.first();
}
if (toggle) {
if (msg != null) {
this.log("setting alert msg to", msg);
div.html(msg);
}
div.show();
}
else {
div.hide();
}
return this;
},
/*
* this looks for event handlers embedded into the html of the
* wizard card itself, in the form of a data- attribute
*/
callListener: function(name) {
// a bug(?) in jquery..can't access data- if name is camelCase
name = name.toLowerCase();
this.log("looking for listener " + name);
var listener = window[this.el.data(name)];
if (listener) {
this.log("calling listener " + name);
var wizard = this.wizard;
try {
var vret = listener(this);
}
catch (e) {
this.log("exception calling listener " + name + ": ", e);
}
}
else {
this.log("didn't find listener " + name);
}
},
problem: function(toggle) {
this.nav.find("a").toggleClass("wizard-step-error", toggle);
},
validate: function() {
var failures = false;
var self = this;
/*
* run all the validators embedded on the inputs themselves
*/
this.el.find("[data-validate]").each(function(i, el) {
self.log("validating individiual inputs");
el = $(el);
var v = el.data("validate");
if (!v) {return;}
var ret = {
status: true,
title: "Error",
msg: ""
};
var vret = window[v](el);
$.extend(ret, vret);
// Add-On
// This allows the use of a INPUT+BTN used as one according to boostrap layout
// for the wizard it is required to add an id with btn-(ID of Input)
// this will make sure the popover is drawn on the correct element
if ( $('#btn-' + el.attr('id')).length === 1 ) {
el = $('#btn-' + el.attr('id'));
}
if (!ret.status) {
failures = true;
// Updated to show error on correct form-group
el.parents("div.form-group").toggleClass("has-error", true);
// This allows the use of a INPUT+BTN used as one according to boostrap layout
// for the wizard it is required to add an id with btn-(ID of Input)
// this will make sure the popover is drawn on the correct element
if ( $('#btn-' + el.attr('id')).length === 1 ) {
el = $('#btn-' + el.attr('id'));
}
self.wizard.errorPopover(el, ret.msg);
} else {
el.parents("div.form-group").toggleClass("has-error", false);
// This allows the use of a INPUT+BTN used as one according to boostrap layout
// for the wizard it is required to add an id with btn-(ID of Input)
// this will make sure the popover is drawn on the correct element
if ( $('#btn-' + el.attr('id')).length === 1 ) {
el = $('#btn-' + el.attr('id'));
}
try {
el.popover("destroy");
}
/*
* older versions of bootstrap don't have a destroy call
* for popovers
*/
catch (e) {
el.popover("hide");
}
}
});
this.log("after validating inputs, failures is", failures);
/*
* run the validator embedded in the card
*/
var cardValidator = window[this.el.data("validate")];
if (cardValidator) {
this.log("running html-embedded card validator");
var cardValidated = cardValidator(this);
if (typeof(cardValidated) == "undefined" || cardValidated == null) {
cardValidated = true;
}
if (!cardValidated) failures = true;
this.log("after running html-embedded card validator, failures is", failures);
}
/*
* run the validate listener
*/
this.log("running listener validator");
var listenerValidated = this.trigger("validate");
if (typeof(listenerValidated) == "undefined" || listenerValidated == null) {
listenerValidated = true;
}
if (!listenerValidated) failures = true;
this.log("after running listener validator, failures is", failures);
var validated = !failures;
if (validated) {
this.log("validated, calling listeners");
this.trigger("validated");
}
else {
this.log("invalid");
this.trigger("invalid");
}
return validated;
},
log: function() {
if (!window.console || !$.fn.wizard.logging) {return;}
var prepend = "card '"+this.name+"': ";
var args = [prepend];
args.push.apply(args, arguments);
console.log.apply(console, args);
},
isActive: function() {
return this.nav.hasClass("active");
}
};
Wizard = function(markup, args) {
/* TEMPLATE */
this.wizard_template = [
''
];
this.args = {
keyboard: true,
backdrop: true,
show: false,
submitUrl: "",
showCancel: false,
showClose: true,
progressBarCurrent: false,
increaseHeight: 0,
contentHeight: 300,
contentWidth: 580,
buttons: {
cancelText: "Cancel",
nextText: "Next",
backText: "Back",
submitText: "Submit",
submittingText: "Submitting...",
},
formClass: "form-horizontal"
};
$.extend(this.args, args || {});
this._create(markup);
};
Wizard.prototype = {
log: function() {
if (!window.console || !$.fn.wizard.logging) {return;}
var prepend = "wizard "+this.el.id+": ";
var args = [prepend];
args.push.apply(args, arguments);
console.log.apply(console, args);
},
_create: function(markup) {
this.markup = $(markup);
this.title = this.markup.data('title');
this.submitCards = this.markup.find(".wizard-error,.wizard-failure,.wizard-success,.wizard-loading");
this.el = $(this.wizard_template.join('\n'));
$('body').append(this.el);
this.modal = this.el.modal({
keyboard: this.args.keyboard,
show: this.args.show,
backdrop: this.args.backdrop
});
this.dimensions = {
contentHeight: this.args.contentHeight,
contentWidth: this.args.contentWidth
};
this.dialog = this.modal.find('.wizard-dialog');
this.content = this.modal.find('.wizard-content');
this.header = this.modal.find('.wizard-header');
this.body = this.modal.find('.wizard-body');
this.wizardSteps = this.modal.find('.wizard-steps');
this.wizardCards = this.modal.find('.wizard-cards');
this.wizardCardContainer = this.modal.find('.wizard-card-container');
this.wizardCardContainer
.append(this.markup.find('.wizard-card'))
.append(this.submitCards);
this.navContainer = this.modal.find('.wizard-nav-container');
this.navList = this.modal.find('.wizard-nav-list');
this.progressContainer = this.modal.find('.wizard-progress-container');
this.progress = this.progressContainer.find('.progress-bar');
this.closeButton = this.modal.find('button.wizard-close.close');
this.cardsContainer = this.modal.find('wizard-cards-container');
this.form = this.modal.find('form');
this.footer = this.modal.find(".wizard-footer");
this.cancelButton = this.footer.find(".wizard-cancel");
this.backButton = this.footer.find(".wizard-back");
this.nextButton = this.footer.find(".wizard-next");
this._cards = [];
this.cards = {};
this._readyToSubmit = false;
this.percentComplete = 0;
this._submitting = false;
this._events = {};
this._firstShow = true;
this._createCards();
this.nextButton.click(this, this._handleNextClick);
this.backButton.click(this, this._handleBackClick);
this.cancelButton.text(this.args.buttons.cancelText);
this.backButton.text(this.args.buttons.backText);
this.nextButton.text(this.args.buttons.nextText);
// Apply Form Class(es)
this.form.addClass(this.args.formClass);
// Register Array Holder for popovers
this.popovers = [];
var self = this;
var _close = function() {
self.reset();
self.close();
self.trigger("closed");
};
// Register Close Button
this.closeButton.click(_close);
this.cancelButton.click(_close);
this.wizardSteps.on("click", "li.already-visited a.wizard-nav-link", this,
function(event) {
var index = parseInt($(event.target).data("navindex"));
event.data.setCard(index);
});
if ( this.title.length != 0 ) {
this.setTitle(this.title);
}
this.on("submit", this._defaultSubmit);
// Set Modal Dimensions
this.autoDimensions();
},
autoDimensions: function() {
// DO NOT REMOVE DISPLAY ; Temporary display is required for calculation
this.modal.css('display', 'block');
this.dimensions.header = this.header.outerHeight(true);
// Navigation Pane is dyanmic build on card content
// Navigation Pane === BASE Inner Content Height
this.dimensions.navigation = this.wizardSteps.outerHeight(true);
if ( this.dimensions.navigation < this.dimensions.contentHeight ) {
this.dimensions.navigation = this.dimensions.contentHeight;
this.navContainer.height( (this.dimensions.contentHeight-30) - this.progressContainer.outerHeight(true));
}
// Dimension Alias ( Body Height === (Navigation Height) )
this.dimensions.body = this.dimensions.navigation;
// Apply OuterHeight of navigation to it's parent wizardSteps
this.wizardSteps.height(this.dimensions.body);
// Modal Height === (Header + Content)
this.dimensions.modal = (this.dimensions.header + this.dimensions.navigation);
this.content.height(this.dimensions.modal + 'px');
this.dialog.width(this.dimensions.contentWidth);
this.body.height(this.dimensions.body + 'px');
this.wizardCards.height(this.dimensions.body + 'px');
// Footer Height
this.dimensions.footer = this.footer.outerHeight(true);
// Card Container === (Body - Footer)
this.dimensions.cardContainer = (this.dimensions.body - this.dimensions.footer);
this.wizardCardContainer.height(this.dimensions.cardContainer);
// Reposition
this.dimensions.offset = ($(window).height() - this.dialog.height()) / 2;
this.dialog.css({
'margin-top': this.dimensions.offset + 'px',
'padding-top': 0
});
// DO NOT REMOVE NEXT LINE
this.modal.css('display', '');
},
setTitle: function(title) {
this.log("setting title to", title);
this.modal.find(".wizard-title").first().text(title);
return this;
},
setSubtitle: function(title) {
this.log("setting subtitle to", title);
this.modal.find(".wizard-subtitle").first().text(title);
return this;
},
errorPopover: function(el, msg, allowHtml) {
this.log("launching popover on", el);
allowHtml = typeof allowHtml !== "undefined" ? allowHtml : false;
var popover = el.popover({
content: msg,
trigger: "manual",
html: allowHtml,
container: el.parents('.form-group')
}).addClass("error-popover").popover("show").next(".popover");
el.parents('.form-group').find('.popover').addClass("error-popover");
this.popovers.push(el);
return popover;
},
destroyPopover: function(pop) {
pop = $(pop);
/*
* this is the element that the popover was created for
*/
try {
pop.popover("destroy");
}
/*
* older versions of bootstrap don't have a destroy call
* for popovers
*/
catch (e) {
pop.popover("hide");
}
},
hidePopovers: function(el) {
this.log("hiding all popovers");
var self = this;
$.each(this.popovers, function(i, p) {
self.destroyPopover(p);
});
this.modal.find('.has-error').removeClass('has-error');
this.popovers = [];
},
eachCard: function(fn) {
$.each(this._cards, fn);
return this;
},
getActiveCard: function() {
this.log("getting active card");
var currentCard = null;
$.each(this._cards, function(i, card) {
if (card.isActive()) {
currentCard = card;
return false;
}
});
if (currentCard) {this.log("found active card", currentCard);}
else {this.log("couldn't find an active card");}
return currentCard;
},
changeNextButton: function(text, cls) {
this.log("changing next button, text: " + text, "class: " + cls);
if (typeof(cls) != "undefined") {
this.nextButton.removeClass("btn-success btn-primary");
}
if (cls) {
this.nextButton.addClass(cls);
}
this.nextButton.text(text);
return this;
},
hide: function() {
this.log("hiding");
this.modal.modal("hide");
return this;
},
close: function() {
this.log("closing");
this.modal.modal("hide");
return this;
},
show: function(modalOptions) {
this.log("showing");
if (this._firstShow) {
this.setCard(0);
this._firstShow = false;
}
if (this.args.showCancel) {
this.cancelButton.show();
} else {
this.cancelButton.hide();
}
if (this.args.showClose) { this.closeButton.show(); }
this.modal.modal('show');
return this;
},
on: function(name, fn) {
this.log("adding listener to event " + name);
this._events[name] = fn;
return this;
},
trigger: function() {
var name = arguments[0];
var args = Array.prototype.slice.call(arguments);
args.shift();
args.unshift(this);
this.log("firing event " + name);
var handler = this._events[name];
if (handler === undefined && this.wizard !== undefined) {
handler = this.wizard._events[name];
}
var ret = null;
if (typeof(handler) == "function") {
this.log("found event handler, calling " + name);
try {
ret = handler.apply(this, args);
}
catch (e) {
this.log("event handler " + name + " had an exception");
}
}
else {
this.log("couldn't find an event handler for " + name);
}
return ret;
},
reset: function() {
this.log("resetting");
this.updateProgressBar(0);
this.hideSubmitCards();
this.setCard(0);
this.lockCards();
this.enableNextButton();
this.showButtons();
this.hidePopovers();
this.trigger("reset");
return this;
},
/*
* this handles switching to the next card or previous card, taking
* care to skip over disabled cards
*/
_abstractIncrementStep: function(direction, getNext) {
var current = this.getActiveCard();
var next;
if (current) {
/*
* loop until we find a card that isn't disabled
*/
this.log("searching for valid next card");
while (true) {
next = getNext(current);
if (next) {
this.log("looking at card", next.index);
if (next.isDisabled()) {
this.log("card " + next.index + " is disabled/locked, continuing");
current = next;
continue;
}
else {
return this.setCard(current.index+direction);
}
}
else {
this.log("next card is not defined, breaking");
break;
}
}
}
else {
this.log("current card is undefined");
}
},
incrementCard: function() {
this.log("incrementing card");
var card = this._abstractIncrementStep(1, function(current){return current.next;});
this.trigger("incrementCard");
return card;
},
decrementCard: function() {
this.log("decrementing card");
var card = this._abstractIncrementStep(-1, function(current){return current.prev;});
this.trigger("decrementCard");
return card;
},
setCard: function(i) {
this.log("setting card to " + i);
this.hideSubmitCards();
var currentCard = this.getActiveCard();
if (this._submitting) {
this.log("we're submitting the wizard already, can't change cards");
return currentCard;
}
var newCard = this._cards[i];
if (newCard) {
if (newCard.isDisabled()) {
this.log("new card is currently disabled, returning");
return currentCard;
}
if (currentCard) {
/*
* here, we're only validating if we're going forward,
* not if we're going backwards in a step
*/
if (i > currentCard.index) {
var cardToValidate = currentCard;
var ok = false;
/*
* we need to loop over every card between our current
* card and the card that we clicked, and re-validate
* them. if there's an error, we need to select the
* first card to have an error
*/
while (cardToValidate.index != newCard.index) {
/*
* unless we're validating the card that we're
* leaving, we need to select the card, so that
* any validators that trigger errorPopovers can
* display correctly
*/
if (cardToValidate.index != currentCard.index) {
cardToValidate.prev.deselect();
cardToValidate.prev.markVisited();
cardToValidate.select();
}
ok = cardToValidate.validate();
if (!ok) {
return cardToValidate;
}
cardToValidate = cardToValidate.next;
}
cardToValidate.prev.deselect();
cardToValidate.prev.markVisited();
}
currentCard.deselect();
currentCard.markVisited();
}
newCard.select();
if (this.args.progressBarCurrent) {
this.percentComplete = i * 100.0 / this._cards.length;
this.updateProgressBar(this.percentComplete);
}
else {
var lastPercent = this.percentComplete;
this.percentComplete = i * 100.0 / this._cards.length;
this.percentComplete = Math.max(lastPercent, this.percentComplete);
this.updateProgressBar(this.percentComplete);
}
return newCard;
}
else {
this.log("couldn't find card " + i);
}
},
updateProgressBar: function(percent) {
this.log("updating progress to " + percent + "%");
this.progress.css({width: percent + "%"});
this.percentComplete = percent;
this.trigger("progressBar", percent);
if (percent == 100) {
this.log("progress is 100, animating progress bar");
this.progressContainer.find('.progress').addClass("active");
}
else if (percent == 0) {
this.log("progress is 0, disabling animation");
this.progressContainer.find('.progress').removeClass("active");
}
},
getNextCard: function() {
var currentCard = this.getActiveCard();
if (currentCard) return currentCard.next;
},
lockCards: function() {
this.log("locking nav cards");
this.eachCard(function(i,card){card.unmarkVisited();});
return this;
},
disableCards: function() {
this.log("disabling all nav cards");
this.eachCard(function(i,card){card.disable();});
return this;
},
enableCards: function() {
this.log("enabling all nav cards");
this.eachCard(function(i,card){card.enable();});
return this;
},
hideCards: function() {
this.log("hiding cards");
this.eachCard(function(i,card){card.deselect();});
this.hideSubmitCards();
return this;
},
hideButtons: function() {
this.log("hiding buttons");
this.cancelButton.hide();
this.closeButton.hide();
this.nextButton.hide();
this.backButton.hide();
return this;
},
showButtons: function() {
this.log("showing buttons");
if (this.args.showCancel) {
this.cancelButton.show();
} else {
this.cancelButton.hide();
}
if (this.args.showClose) { this.closeButton.show(); };
this.nextButton.show();
this.backButton.show();
return this;
},
getCard: function(el) {
var cardDOMEl = $(el).parents(".wizard-card").first()[0];
if (cardDOMEl) {
var foundCard = null;
this.eachCard(function(i, card) {
if (cardDOMEl == card.el[0]) {
foundCard = card;
return false;
}
return true;
});
return foundCard;
}
else {
return null;
}
},
_createCards: function() {
var prev = null;
var next = null;
var currentCard = null;
var wizard = this;
var self = this;
self.log("Creating Cards");
var cards = this.modal.find(".wizard-cards .wizard-card");
$.each(cards, function(i, card) {
card = $(card);
prev = currentCard;
currentCard = new WizardCard(wizard, card, i, prev, next);
self._cards.push(currentCard);
if (currentCard.name) {
self.cards[currentCard.name] = currentCard;
}
if (prev) {prev.next = currentCard;}
self.modal.find(".wizard-steps .wizard-nav-list").append(currentCard.nav);
});
},
showSubmitCard: function(name) {
this.log("showing "+name+" submit card");
var card = this.el.find(".wizard-"+name);
if (card.length) {
this.hideCards();
this.el.find(".wizard-"+name).show();
}
else {
this.log("couldn't find submit card "+name);
}
},
hideSubmitCard: function(name) {
this.log("hiding "+name+" submit card");
this.el.find(".wizard-"+name).hide();
},
hideSubmitCards: function() {
var wizard = this;
$.each(["success", "error", "failure", "loading"], function(i, name) {
wizard.hideSubmitCard(name);
});
},
enableNextButton: function() {
this.log("enabling next button");
this.nextButton.removeAttr("disabled");
return this;
},
disableNextButton: function() {
this.log("disabling next button");
this.nextButton.attr("disabled", "disabled");
return this;
},
serializeArray: function() {
var form = this.form.serializeArray();
this.form.find('input[disabled][data-serialize="1"]').each(function() {
formObj = {
name: $(this).attr('name'),
value: $(this).val()
};
form.push(formObj);
});
return form;
},
serialize: function() {
var form = this.form.serialize();
this.form.find('input[disabled][data-serialize="1"]').each(function() {
form = form + '&' + $(this).attr('name') + '=' + $(this).val();
});
return form;
},
find: function(selector) {
return this.modal.find(selector);
},
/*
* the next 3 functions are to be called by the custom submit event
* handler. the idea is that after you make an ajax call to submit
* your wizard data (or whatever it is you want to do at the end of
* the wizard), you call one of these 3 handlers to display a specific
* card for either success, failure, or error
*/
submitSuccess: function() {
this.log("submit success");
this._submitting = false;
this.showSubmitCard("success");
this.trigger("submitSuccess");
},
submitFailure: function() {
this.log("submit failure");
this._submitting = false;
this.showSubmitCard("failure");
this.trigger("submitFailure");
},
submitError: function() {
this.log("submit error");
this._submitting = false;
this.showSubmitCard("error");
this.trigger("submitError");
},
_submit: function() {
this.log("submitting wizard");
this._submitting = true;
this.lockCards();
this.cancelButton.hide();
this.closeButton.hide();
this.backButton.hide();
this.showSubmitCard("loading");
this.updateProgressBar(100);
this.changeNextButton(this.args.buttons.submittingText, false);
this.disableNextButton();
var ret = this.trigger("submit");
this.trigger("loading");
},
_onNextClick: function() {
this.log("handling 'next' button click");
var currentCard = this.getActiveCard();
if (this._readyToSubmit && currentCard.validate()) {
this._submit();
}
else {
currentCard = this.incrementCard();
}
},
_onBackClick: function() {
this.log("handling 'back' button click");
var currentCard = this.decrementCard();
},
_handleNextClick: function(event) {
var wizard = event.data;
wizard._onNextClick.call(wizard);
},
_handleBackClick: function(event) {
var wizard = event.data;
wizard._onBackClick.call(wizard);
},
/*
* this function is attached by default to the wizard's "submit" event.
* if you choose to implement your own custom submit logic, you should
* copy how this function works
*/
_defaultSubmit: function(wizard) {
$.ajax({
type: "POST",
url: wizard.args.submitUrl,
data: wizard.serialize(),
dataType: "json"
}).done(function(response) {
wizard.submitSuccess();
wizard.hideButtons();
wizard.updateProgressBar(0);
}).fail(function() {
wizard.submitFailure();
wizard.hideButtons();
});
}
};
}(window.jQuery));