(function (Q, $) {
/**
* @module Q-tools
*/
/**
* Place this tool inside a <form> tag to create nice AJAX forms
* @class Q form
* @constructor
* @param {Object} [options] This is an object of parameters for this function
* @param {Q.Event} [options.onSubmit] This event triggers On form submit
* @default Q.Event()
* @param {Q.Event} [options.onResponse] This event triggers after getting some response from from url request
* @default Q.Event()
* @param {Q.Event} [options.onSuccess] This event triggers if response returned with 200 success code , and if there are no HTTP errors in response headers
* @default Q.Event()
* @param {String} [options.slotsToRequest] Slot names for Q.request
* @default 'form'
* @param {Object} [options.contentElements] An Object of content Elements
* @default {}
* @param {Function} [options.loader] Main request function which calls on form submit
* @default <code>function (url, method, params, slots, callback) { Q.request(url+"?"+params, slots, callback, {method: method}); }</code>
* @param {String} [options.loader.url] Url for request
* @param {String} [options.loader.method] Form Method / Request Method
* @param {String} [options.loader.params] Url Encoded /Serialised form data as URL parameters
* @param {String} [options.loader.slots] Slot Names
* @param {Function} [options.loader.callback] Callback function after request
*
*/
Q.Tool.define('Q/form', function(options) {
Q.addStylesheet('{{Q}}/css/form.css');
this.refresh(options);
},
{
onSubmit: new Q.Event(),
onResponse: new Q.Event(),
onSuccess: new Q.Event(),
slotsToRequest: 'form',
contentElements: {},
loader: function (url, method, params, slots, callback) {
Q.request(url+"?"+params, slots, callback, {method: method});
}
},
{
Q: {
beforeRemove: {"Q/form": function () {
var form = $(this.element).closest('form');
if (form.data('Q/form tool') === this) {
form.removeData('Q/form tool');
form.off('submit.Q_form');
}
}},
onRetain: {"Q/form": function (options) {
this.refresh(options);
}}
},
/**
* @method refresh
*/
refresh: function () {
// constructor & private declarations
var tool = this;
var $te = $(tool.element);
var form = $te.closest('form');
if (!form.length) return;
if (form.data('Q/form tool')) return;
form.on('submit.Q_form', function(event) {
function onResponse(err, data) {
var msg;
if (msg = Q.firstErrorMessage(err)) {
return alert(msg);
}
$('button', $te).closest('td').removeClass('Q_throb');
Q.handle(tool.state.onResponse, tool, arguments);
$('div.Q_form_undermessagebubble', $te).empty();
$('tr.Q_error', $te).removeClass('Q_error');
if ('errors' in data) {
tool.applyErrors(data.errors);
$('tr.Q_error').eq(0).prev().find(':input:visible').eq(0).focus();
if (data.scriptLines && data.scriptLines.form) {
eval(data.scriptLines.form);
}
} else {
var slots = Object.keys(data.slots);
var pipe = new Q.pipe(slots, function () {
Q.handle(tool.state.onSuccess, tool, arguments);
});
for (var slot in data.slots) {
var e;
switch (typeof tool.state.contentElements[slot]) {
case 'HTMLElement':
case 'jQuery':
e = $(tool.state.contentElements[slot]); break;
case 'string':
e = $(tool.state.contentElements[slot], form); break;
default:
e = $(tool.element);
}
var replaced = Q.replace(e[0], data.slots[slot]);
Q.activate(replaced, pipe.fill(slot));
if (data.scriptLines && data.scriptLines[slot]) {
eval(data.scriptLines[slot]);
}
}
}
};
event.preventDefault();
$('button', $te).closest('td').addClass('Q_throb');
var result = {};
var action = form.attr('action');
if (!action) {
action = window.location.href.split('?')[0];
}
Q.handle(tool.state.onSubmit, tool, [form, result]);
if (result.cancel) {
return false;
}
var input = $('input[name="Q.method"]', form);
method = (input.val() || form.attr('method') || 'post').toUpperCase();
if (tool.state.ignoreCache
&& typeof tool.state.loader.forget === "function") {
tool.state.ignoreCache = false;
tool.state.loader.forget(action, method, form.serialize(), tool.state.slotsToRequest);
}
tool.state.loader(action, method, form.serialize(), tool.state.slotsToRequest, onResponse);
});
$('input', form).add('select', form).on('input', function () {
if (form.state('Q/validator')) {
form.plugin('Q/validator', 'reset', $(this));
}
});
form.data('Q/form tool', tool);
},
/**
* @method applyErrors
* @param {Object} errors
*/
applyErrors: function(errors) {
var err = null;
for (var i=0; i<errors.length; ++i) {
if (!('fields' in errors[i])
|| Q.typeOf(errors[i].fields) !== 'array'
|| !errors[i].fields.length) {
err = errors[i];
continue;
}
for (var j=0; j<errors[i].fields.length; ++j) {
var k = errors[i].fields[j];
var td = $("td[data-fieldname='"+k+"']", this.element);
if (!td.length) {
err = errors[i];
}
var tr = td.closest('tr').next();
tr.addClass('Q_error');
$('div.Q_form_undermessagebubble', tr)
.html(errors[i].message);
}
}
if (err) {
alert(err.message);
}
}
}
);
})(Q, jQuery);