Skip to content

Instantly share code, notes, and snippets.

@MartyIX
Forked from redhead/live-form-validation.js
Created October 6, 2011 16:29
Show Gist options
  • Select an option

  • Save MartyIX/1267868 to your computer and use it in GitHub Desktop.

Select an option

Save MartyIX/1267868 to your computer and use it in GitHub Desktop.
Live Form Validation for Nette 2.0
/**
* Live Form Validation for Nette 2.0
*
* @author Radek Ježdík, David Grudl
* @license New BSD License
*/
var LiveForm = {
options: {
controlErrorClass: 'form-control-error',
errorMessageClass: 'form-error-message',
validMessageClass: 'form-valid-message',
showValid: false,
messageTag: 'span',
messageIdPostfix: '_message',
wait: 300
},
forms: { }
};
LiveForm.addError = function(el, message) {
if (!this.forms[el.form.id].hasError) {
this.forms[el.form.id].hasError = true;
}
this.addClass(el, this.options.controlErrorClass);
if (!message) {
message = ' ';
}
var error = this.getMessageElement(el);
error.innerHTML = message;
}
LiveForm.removeError = function(el) {
this.removeClass(el, this.options.controlErrorClass);
var err_el = document.getElementById(el.id + this.options.messageIdPostfix);
if (this.options.showValid && this.showValid(el)) {
err_el = this.getMessageElement(el);
err_el.className = this.options.validMessageClass;
return;
}
if (err_el) {
err_el.parentNode.removeChild(err_el);
}
}
LiveForm.showValid = function(el) {
var showValid = true;
if(el.type) {
var type = el.type.toLowerCase();
if(type == 'checkbox' || type == 'radio')
showValid = false;
}
var rules = rules || eval('[' + (el.getAttribute('data-Nette-rules') || '') + ']');
if(rules.length == 0) {
showValid = false;
}
if (this.hasClass(el, 'dont-show-when-valid')) {
return false;
}
return showValid;
}
// if needed CHANGE these handlers to use jQuery events instead
LiveForm.setUpHandlers = function(el) {
if (!this.hasClass(el, 'no-js-validation')) {
var handler = function(event) {
event = event || window.event;
Nette.validateControl(event.target ? event.target : event.srcElement);
};
var self = this;
el.onchange = handler;
el.onblur = handler;
el.onkeydown = function (event) {
if (self.options.wait >= 200) {
// Hide validation span tag.
self.removeClass(this, self.options.controlErrorClass);
self.removeClass(this, self.options.validMessageClass);
var error = self.getMessageElement(this);
error.innerHTML = '';
error.className = '';
// Cancel timeout to run validation handler
if (self.timeout) {
clearTimeout(self.timeout);
}
}
}
el.onkeyup = function(event) {
event = event || window.event;
if (event.keyCode !== 9) {
if (self.timeout) clearTimeout(self.timeout);
self.timeout = setTimeout(function() {
handler(event);
}, self.options.wait);
}
};
}
}
LiveForm.getMessageElement = function(el) {
id = el.id + this.options.messageIdPostfix;
var error = document.getElementById(id)
if (!error) {
error = document.createElement(this.options.messageTag);
error.id = id;
el.parentNode.appendChild(error);
}
if (el.style.display == 'none')
error.style.display = 'none';
error.className = this.options.errorMessageClass;
error.innerHTML = '';
return error;
}
LiveForm.addClass = function(el, className) {
if (!el.className) {
el.className = className;
} else if (!this.hasClass(el, className)) {
el.className += ' ' + className;
}
}
LiveForm.hasClass = function(el, className) {
if (el.className)
return el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
return false;
}
LiveForm.removeClass = function(el, className) {
if (this.hasClass(el, className)) {
var reg = new RegExp('(\\s|^)'+ className + '(\\s|$)');
var m = el.className.match(reg);
el.className = el.className.replace(reg, (m[1] == ' ' && m[2] == ' ') ? ' ' : '');
}
}
///////////////////////////////////////////////////////////////////////////////////////////
var Nette = Nette || { };
Nette.addEvent = function (element, on, callback) {
var original = element['on' + on];
element['on' + on] = function () {
if (typeof original === 'function' && original.apply(element, arguments) === false) {
return false;
}
return callback.apply(element, arguments);
};
};
Nette.getValue = function(elem) {
var i, len;
if (!elem) {
return null;
} else if (!elem.nodeName) { // radio
for (i = 0, len = elem.length; i < len; i++) {
if (elem[i].checked) {
return elem[i].value;
}
}
return null;
} else if (elem.nodeName.toLowerCase() === 'select') {
var index = elem.selectedIndex, options = elem.options;
if (index < 0) {
return null;
} else if (elem.type === 'select-one') {
return options[index].value;
}
for (i = 0, values = [], len = options.length; i < len; i++) {
if (options[i].selected) {
values.push(options[i].value);
}
}
return values;
} else if (elem.type === 'checkbox') {
return elem.checked;
} else if (elem.type === 'radio') {
return Nette.getValue(elem.form.elements[elem.name]);
} else {
return elem.value.replace(/^\s+|\s+$/g, '');
}
};
Nette.validateControl = function(elem, rules, onlyCheck) {
rules = rules || eval('[' + (elem.getAttribute('data-nette-rules') || '') + ']');
for (var id in rules) {
var rule = rules[id], op = rule.op.match(/(~)?([^?]+)/);
rule.neg = op[1];
rule.op = op[2];
rule.condition = !!rule.rules;
var el = rule.control ? elem.form.elements[rule.control] : elem;
var success = Nette.validateRule(el, rule.op, rule.arg);
if (success === null) continue;
if (rule.neg) success = !success;
if (rule.condition && success) {
if (!Nette.validateControl(elem, rule.rules, onlyCheck)) {
return false;
}
} else if (!rule.condition && !success) {
if (el.disabled) continue;
if (!onlyCheck) {
Nette.addError(el, rule.msg.replace('%value', Nette.getValue(el)));
}
return false;
}
}
if (!onlyCheck) {
LiveForm.removeError(elem);
}
return true;
};
Nette.validateForm = function(sender) {
var form = sender.form || sender;
LiveForm.forms[form.id].hasError = false;
if (form['nette-submittedBy'] && form['nette-submittedBy'].getAttribute('formnovalidate') !== null) {
return true;
}
var ok = true;
for (var i = 0; i < form.elements.length; i++) {
var elem = form.elements[i];
if (!(elem.nodeName.toLowerCase() in {
input:1,
select:1,
textarea:1
}) || (elem.type in {
hidden:1,
submit:1,
image:1,
reset: 1
}) || elem.disabled || elem.readonly) {
continue;
}
if (!Nette.validateControl(elem)) {
ok = false;
}
}
return ok;
};
Nette.addError = function(elem, message) {
if (elem.focus && !LiveForm.forms[elem.form.id].hasError) {
elem.focus();
}
LiveForm.addError(elem, message);
};
Nette.validateRule = function(elem, op, arg) {
var val = Nette.getValue(elem);
if (elem.getAttribute) {
if (val === elem.getAttribute('data-nette-empty-value')) {
val = '';
}
}
if (op.charAt(0) === ':') {
op = op.substr(1);
}
op = op.replace('::', '_');
op = op.replace('\\', '');
return Nette.validators[op] ? Nette.validators[op](elem, arg, val) : null;
};
Nette.validators = {
filled: function(elem, arg, val) {
return val !== '' && val !== false && val !== null;
},
valid: function(elem, arg, val) {
return Nette.validateControl(elem, null, true);
},
equal: function(elem, arg, val) {
if (arg === undefined) {
return null;
}
arg = Nette.isArray(arg) ? arg : [arg];
for (var i = 0, len = arg.length; i < len; i++) {
if (val == (arg[i].control ? Nette.getValue(elem.form.elements[arg[i].control]) : arg[i])) {
return true;
}
}
return false;
},
minLength: function(elem, arg, val) {
return val.length >= arg;
},
maxLength: function(elem, arg, val) {
return val.length <= arg;
},
length: function(elem, arg, val) {
arg = Nette.isArray(arg) ? arg : [arg, arg];
return (arg[0] === null || val.length >= arg[0]) && (arg[1] === null || val.length <= arg[1]);
},
email: function(elem, arg, val) {
return (/^[^@\s]+@[^@\s]+\.[a-z]{2,10}$/i).test(val);
},
url: function(elem, arg, val) {
return (/^.+\.[a-z]{2,6}(\/.*)?$/i).test(val);
},
regexp: function(elem, arg, val) {
var parts = typeof arg === 'string' ? arg.match(/^\/(.*)\/([imu]*)$/) : false;
if (parts) {
try {
return (new RegExp(parts[1], parts[2].replace('u', ''))).test(val);
} catch (e) {}
}
},
pattern: function(elem, arg, val) {
try {
return typeof arg === 'string' ? (new RegExp('^(' + arg + ')$')).test(val) : null;
} catch (e) {}
},
integer: function(elem, arg, val) {
return (/^-?[0-9]+$/).test(val);
},
'float': function(elem, arg, val) {
return (/^-?[0-9]*[.,]?[0-9]+$/).test(val);
},
range: function(elem, arg, val) {
return Nette.isArray(arg) ? ((arg[0] === null || parseFloat(val) >= arg[0]) && (arg[1] === null || parseFloat(val) <= arg[1])) : null;
},
submitted: function(elem, arg, val) {
return elem.form['nette-submittedBy'] === elem;
}
};
Nette.toggleForm = function(form) {
for (var i = 0; i < form.elements.length; i++) {
if (form.elements[i].nodeName.toLowerCase() in {
input:1,
select:1,
textarea:1,
button:1
}) {
Nette.toggleControl(form.elements[i]);
}
}
};
Nette.toggleControl = function(elem, rules, firsttime) {
rules = rules || eval('[' + (elem.getAttribute('data-nette-rules') || '') + ']');
var has = false, __hasProp = Object.prototype.hasOwnProperty, handler = function() {
Nette.toggleForm(elem.form);
};
for (var id in rules) {
var rule = rules[id], op = rule.op.match(/(~)?([^?]+)/);
rule.neg = op[1];
rule.op = op[2];
rule.condition = !!rule.rules;
if (!rule.condition) continue;
var el = rule.control ? elem.form.elements[rule.control] : elem;
var success = Nette.validateRule(el, rule.op, rule.arg);
if (success === null) continue;
if (rule.neg) success = !success;
if (Nette.toggleControl(elem, rule.rules, firsttime) || rule.toggle) {
has = true;
if (firsttime) {
if (!el.nodeName) { // radio
for (var i = 0; i < el.length; i++) {
Nette.addEvent(el[i], 'click', handler);
}
} else if (el.nodeName.toLowerCase() === 'select') {
Nette.addEvent(el, 'change', handler);
} else {
Nette.addEvent(el, 'click', handler);
}
}
for (var id2 in rule.toggle || []) {
if (__hasProp.call(rule.toggle, id2)) {
Nette.toggle(id2, success ? rule.toggle[id2] : !rule.toggle[id2]);
}
}
}
}
return has;
};
Nette.toggle = function(id, visible) {
var elem = document.getElementById(id);
if (elem) {
elem.style.display = visible ? "" : "none";
}
};
Nette.initForm = function(form) {
form.noValidate = true;
LiveForm.forms[form.id] = {
hasError: false
};
Nette.addEvent(form, 'submit', function() {
return Nette.validateForm(form);
});
Nette.addEvent(form, 'click', function(e) {
e = e || event;
var target = e.target || e.srcElement;
form['nette-submittedBy'] = (target.type in {
submit:1,
image:1
}) ? target : null;
});
for (var i = 0; i < form.elements.length; i++) {
Nette.toggleControl(form.elements[i], null, true);
LiveForm.setUpHandlers(form.elements[i]);
}
if (/MSIE/.exec(navigator.userAgent)) {
var labels = {},
wheelHandler = function() {
return false;
},
clickHandler = function() {
document.getElementById(this.htmlFor).focus();
return false;
};
for (i = 0, elms = form.getElementsByTagName('label'); i < elms.length; i++) {
labels[elms[i].htmlFor] = elms[i];
}
for (i = 0, elms = form.getElementsByTagName('select'); i < elms.length; i++) {
Nette.addEvent(elms[i], 'mousewheel', wheelHandler); // prevents accidental change in IE
if (labels[elms[i].htmlId]) {
Nette.addEvent(labels[elms[i].htmlId], 'click', clickHandler); // prevents deselect in IE 5 - 6
}
}
}
};
Nette.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
Nette.addEvent(window, 'load', function () {
for (var i = 0; i < document.forms.length; i++) {
Nette.initForm(document.forms[i]);
}
});
@MartyIX

MartyIX commented Oct 9, 2011

Copy link
Copy Markdown
Author

Přidána možnost vypnutí validace pro konkrétní prvky pomocí třídy "no-js-validation".

Příklad formuláře v Nette:

       $form->addUpload('file')
              ->addRule(Form::MAX_FILE_SIZE, 'Maximum file size is 1 MB', 1024 * 1024)
              ->getControlPrototype()->addClass('no-js-validation');

@MartyIX

MartyIX commented Dec 6, 2011

Copy link
Copy Markdown
Author

Chyba z http://addons.nette.org/cs/live-form-validation-for-nette-2-0:

hlásím chybu při nastavení:
controlErrorClass: ‚form-control-error‘,
errorMessageClass: ‚form-error-message‘,
validMessageClass: ‚form-valid-message‘,
showValid: false,
messageTag: ‚div‘,
messageIdPostfix: ‚_message‘

při opravě zmizí input místo zprávy

Resenim by melo byt:

err_el.parentNode.removeChild(el);

->

err_el.parentNode.removeChild(err_el);

@redhead

redhead commented May 29, 2012

Copy link
Copy Markdown

MartyIX, koukám, že jsi to docela vyšperkoval! Můžu to přehodit na hlavní gist, kam vede odkaz z addonů? Já jen jestli je to psáno obecně, jestli tam nemáš nějaké vlastní specifické vychytávky?

@MartyIX

MartyIX commented May 29, 2012

Copy link
Copy Markdown
Author

Ahoj, urcite muzes. Nic specifickeho tam neni. Bohuzel uz nevim, jak moc je to otestovane, protoze ted pouzivam upravenou verzi tohoto s par efekty v jQuery.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment