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

415 lines
12 KiB
JavaScript

/*!
* jQuery Stepy - A Wizard Plugin
* --------------------------------------------------------------
*
* jQuery Stepy is a plugin that generates a customizable wizard.
*
* Licensed under The MIT License
*
* @version 1.1.0
* @since 2010-07-03
* @author Washington Botelho
* @documentation wbotelhos.com/stepy
*
* --------------------------------------------------------------
*
* <form>
* <fieldset title="Step 1">
* <legend>description one</legend>
* <!-- inputs -->
* </fieldset>
*
* <fieldset title="Step 2">
* <legend>description two</legend>
* <!-- inputs -->
* </fieldset>
*
* <input type="submit" />
* </form>
*
* $('form').stepy();
*
*/
;(function($) {
var methods = {
init: function(settings) {
return this.each(function() {
methods.destroy.call(this);
this.opt = $.extend({}, $.fn.stepy.defaults, settings);
var self = this,
that = $(this),
id = that.attr('id');
if (id === undefined || id === '') {
var id = methods._hash.call(self);
that.attr('id', id);
}
// Remove Validator...
if (self.opt.validate) {
jQuery.validator.setDefaults({ ignore: self.opt.ignore });
that.append('<div class="stepy-errors" />');
}
self.header = methods._header.call(self);
self.steps = that.children('fieldset');
self.steps.each(function(index) {
methods._createHead.call(self, this, index);
methods._createButtons.call(self, this, index);
});
self.heads = self.header.children('li');
self.heads.first().addClass('stepy-active');
if (self.opt.finishButton) {
methods._bindFinish.call(self);
}
// WIP...
if (self.opt.titleClick) {
self.heads.click(function() {
var array = self.heads.filter('.stepy-active').attr('id').split('-'), // TODO: try keep the number in an attribute.
current = parseInt(array[array.length - 1], 10),
clicked = $(this).index();
if (clicked > current) {
if (self.opt.next && !methods._execute.call(that, self.opt.next, clicked)) {
return false;
}
} else if (clicked < current) {
if (self.opt.back && !methods._execute.call(that, self.opt.back, clicked)) {
return false;
}
}
if (clicked != current) {
methods.step.call(self, (clicked) + 1);
}
});
} else {
self.heads.css('cursor', 'default');
}
if (self.opt.enter) {
methods._bindEnter.call(self);
}
self.steps.first().find(':input:visible:enabled').first().select().focus();
that.data({ 'settings': this.opt, 'stepy': true });
});
}, _bindEnter: function() {
var self = this;
self.steps.delegate('input[type="text"], input[type="password"]', 'keypress', function(evt) {
var key = (evt.keyCode ? evt.keyCode : evt.which);
if (key == 13) {
evt.preventDefault();
var buttons = $(this).closest('fieldset').find('.stepy-navigator');
if (buttons.length) {
var next = buttons.children('.button-next');
if (next.length) {
next.click();
} else if (self.finish) {
self.finish.click();
}
}
}
});
}, _bindFinish: function() {
var self = this,
that = $(this),
finish = that.children('input[type="submit"]');
self.finish = (finish.length === 1) ? finish : that.children('.btn .btn-succes .stepy-finish');
if (self.finish.length) {
var isForm = that.is('form'),
onSubmit = undefined;
if (isForm && self.opt.finish) {
onSubmit = that.attr('onsubmit');
that.attr('onsubmit', 'return false;');
}
self.finish.on('click.stepy', function(evt) {
if (self.opt.finish && !methods._execute.call(that, self.opt.finish, self.steps.length - 1)) {
evt.preventDefault();
} else if (isForm) {
if (onSubmit) {
that.attr('onsubmit', onSubmit);
} else {
that.removeAttr('onsubmit');
}
var isSubmit = self.finish.attr('type') === 'submit';
if (!isSubmit && (!self.opt.validate || methods.validate.call(that, self.steps.length - 1))) {
that.submit();
}
}
});
self.steps.last().children('.stepy-navigator').append(self.finish);
} else {
$.error('Submit button or element with class "stepy-finish" missing!');
}
}, _createBackButton: function(nav, index) {
var self = this,
that = $(this),
attributes = { href: '#', 'class': 'btn btn-default', html: self.opt.backLabel };
$('<a />', attributes).on('click.stepy', function(e) {
e.preventDefault();
if (!self.opt.back || methods._execute.call(self, self.opt.back, index - 1)) {
methods.step.call(self, (index - 1) + 1);
}
}).appendTo(nav);
}, _createButtons: function(step, index) {
var nav = methods._navigator.call(this).appendTo(step);
if (index === 0) {
if (this.steps.length > 1) {
methods._createNextButton.call(this, nav, index);
}
} else {
$(step).hide();
methods._createBackButton.call(this, nav, index);
if (index < this.steps.length - 1) {
methods._createNextButton.call(this, nav, index);
}
}
}, _createHead: function(step, index) {
var step = $(step).attr('id', $(this).attr('id') + '-step-' + index).addClass('stepy-step'),
head = methods._head.call(this, index);
head.append(methods._title.call(this, step));
if (this.opt.description) {
head.append(methods._description.call(this, step));
}
this.header.append(head);
}, _createNextButton: function(nav, index) {
var self = this,
that = $(this),
attributes = { href: '#', 'class': 'btn btn-primary', html: self.opt.nextLabel };
$('<a/>', attributes).on('click.stepy', function(e) {
e.preventDefault();
if (!self.opt.next || methods._execute.call(that, self.opt.next, index + 1)) {
methods.step.call(self, (index + 1) + 1);
}
}).appendTo(nav);
}, _description: function(step) {
var legend = step.children('legend');
if (!this.opt.legend) {
legend.hide();
}
if (legend.length) {
return $('<span />', { html: legend.html() });
}
methods._error.call(this, '<legend /> element missing!');
}, _error: function(message) {
$(this).html(message);
$.error(message);
}, _execute: function(callback, index) {
var isValid = callback.call(this, index + 1);
return isValid || isValid === undefined;
}, _hash: function() {
this.hash = 'stepy-' + Math.random().toString().substring(2)
return this.hash;
}, _head: function(index) {
return $('<li />', { id: $(this).attr('id') + '-head-' + index });
}, _header: function() {
var header = $('<ul />', { id: $(this).attr('id') + '-header', 'class': 'stepy-header' });
if (this.opt.titleTarget) {
header.appendTo(this.opt.titleTarget);
} else {
header.insertBefore(this);
}
return header;
}, _navigator: function(index) {
return $('<div class="stepy-navigator panel-footer" />');
}, _title: function(step) {
return $('<div />', { html: step.attr('title') || '--' });
}, destroy: function() {
return $(this).each(function() {
var that = $(this);
if (that.data('stepy')) {
var steps = that.data('stepy', false).children('fieldset').css('display', '');
that.children('.stepy-errors').remove();
this.finish.appendTo(steps.last());
steps.find('.stepy-navigator').remove();
}
});
}, step: function(index) {
var self = this
that = $(this),
opt = that[0].opt;
index--;
var steps = that.children('fieldset');
if (index > steps.length - 1) {
index = steps.length - 1;
}
var max = index;
// Remove Validator...
if (opt.validate) {
var isValid = true;
for (var i = 0; i < index; i++) {
isValid &= methods.validate.call(this, i);
if (opt.block && !isValid) {
max = i;
break;
}
}
}
// WIP...
var stepsCount = steps.length;
if (opt.transition == 'fade') {
steps.fadeOut(opt.duration, function() {
if (--stepsCount > 0) {
return;
}
steps.eq(max).fadeIn(opt.duration);
});
} else if (opt.transition == 'slide') {
steps.slideUp(opt.duration, function() {
if (--stepsCount > 0) {
return;
}
steps.eq(max).slideDown(opt.duration);
});
} else {
steps.hide(opt.duration).eq(max).show(opt.duration);
}
that[0].heads.removeClass('stepy-active').eq(max).addClass('stepy-active');
if (that.is('form')) {
var $fields = undefined;
if (max == index) {
$fields = steps.eq(max).find(':input:enabled:visible');
} else {
$fields = steps.eq(max).find('.error').select().focus();
}
$fields.first().select().focus();
}
if (opt.select) {
opt.select.call(this, max + 1);
}
return that;
}, validate: function(index) { // WIP...
var that = $(this);
if (!that.is('form')) {
return true;
}
var self = this,
step = that.children('fieldset').eq(index),
isValid = true,
$title = $('#' + that.attr('id') + '-header').children().eq(index),
$validate = that.validate();
$(step.find(':input:enabled').get().reverse()).each(function() {
var fieldIsValid = $validate.element($(this));
if (fieldIsValid === undefined) {
fieldIsValid = true;
}
isValid &= fieldIsValid;
if (isValid) {
if (self.opt.errorImage) {
$title.removeClass('stepy-error');
}
} else {
if (self.opt.errorImage) {
$title.addClass('stepy-error');
}
$validate.focusInvalid();
}
});
return isValid;
}
};
$.fn.stepy = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist!');
}
};
$.fn.stepy.defaults = {
back : undefined,
backLabel : '<i class="fa fa-angle-left"></i> Back',
block : false, // WIP...
description : true,
duration : undefined,
enter : true,
errorImage : false, // WIP...
finish : undefined,
finishButton : true,
ignore : '', // WIP...
legend : true,
next : undefined,
nextLabel : 'Next <i class="fa fa-angle-right"></i>',
select : undefined,
titleClick : false,
titleTarget : undefined,
transition : undefined,
validate : false // WIP...
};
})(jQuery);