├── lemongrab.png ├── README.md ├── composer.json └── jquery.lemongrab.js /lemongrab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pozitronik/jquery.lemongrab/HEAD/lemongrab.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jquery.lemongrab 2 | ================ 3 | 4 | jquery.lemongrab.js - простой и минималистичный jQuery-плагин для валидации форм. С его помощью можно задавать условия валидации полей, описывать реакцию на изменение состояния, и связывать поля по условиям. 5 | 6 | Документация и примеры доступны здесь: http://pozitronik.me/lemongrab 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pozitronik/jquery.lemongrab", 3 | "description": "jQuery web-form validation plugin", 4 | "type": "library", 5 | "license": [ 6 | "GPL-3.0", 7 | ], 8 | "homepage": "http://pozitronik.me/lemongrab", 9 | "authors": [ 10 | { 11 | "name": "Pavel Dubrovsky", 12 | "homepage": "http://pozitronik.me", 13 | "role": "Developer" 14 | }, 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /jquery.lemongrab.js: -------------------------------------------------------------------------------- 1 | /*! Lemongrab v 11.03.15 | (c) 2013-2015 Pavel Dubrovsky*/ 2 | 3 | (function( $ ) { 4 | var 5 | ValidState=false, 6 | RequiredState=false, 7 | VisibleState=false, 8 | FORM,//Глобальный (для плагина) объект формы, на которую он натравлен 9 | ACCEPTABLE; 10 | 11 | 12 | jQuery.fn.lemongrab = function(options,RULES) { 13 | ACCEPTABLE=$.extend({ "subselector":"*", 14 | "classValid":"ACCEPTABLE", 15 | "classInvalid":"UNACCEPTABLE", 16 | "classRequired":"REQUIRED", 17 | "classNorequired":"NOREQUIRED", 18 | "classEnabled":"ENABLED", 19 | "classDisabled":"DISABLED", 20 | "classVisible":"VISIBLE", 21 | "classHidden":"HIDDEN", 22 | "classChanged":"CHANGED", 23 | "classUnchanged":"UNCHANGED", 24 | "action":"input", 25 | "actions":{}, 26 | "onAction":false, 27 | "allowInvalidSubmit":true, 28 | "useRequiredAttr":true, 29 | "nativeEnabled":true, 30 | "nativeVisible":true, 31 | "autograb":true, 32 | "ignore_autograb_change":true,//Игнорировать смену состояний Changed/Unchanged при autograb=true 33 | "ignore_autograb_action":false//Игнорировать вызов события onAction при autograb=true 34 | },options); 35 | FORM=this; 36 | 37 | var rule,conditions,selector_count,conditions_count,rule_options={},lemons,lemon,rules; 38 | 39 | //если в RULES что-то задано - считаем, что там JSON, парсим его. 40 | if (typeof(RULES)!=='undefined' && RULES!=='') { 41 | var for_selector; 42 | for (selector_count=0;selector_count 214 | Входное значение - jQuery-объект с селектом 215 | */ 216 | function get_selected_options(select){ 217 | var options=select.find("option:selected"), 218 | option_count, 219 | ret=[]; 220 | for (option_count=0;option_countblock.find(selector).length || cl===0) return (false); 241 | } 242 | } 243 | return (true); 244 | } 245 | 246 | /* 247 | * По заданному набору параметров генерирует JS-код условия 248 | */ 249 | function get_js_condition (condition,field){ 250 | var ret={ 251 | handler:[], 252 | condition:"" 253 | },_condition,summary_selector,index; 254 | 255 | switch (condition.key.toLowerCase()){ 256 | case 'regexp': 257 | if (condition.selector!=='') {//Задан селектор внешнего поля, дополнительно вешаем обработчик на него 258 | ret.condition = "RegExp(/"+condition.value+"/).test($('"+condition.selector+"').val())"; 259 | ret.handler.push("$('"+condition.selector+"')"); 260 | } else {//Селектор не задан, проверяем собственное поле 261 | ret.condition = "RegExp(/"+condition.value+"/).test(field.val())";//field дальше будет обрабатываться eval 262 | } 263 | break; 264 | case 'checked': 265 | if (condition.selector!=='') {//Задан селектор внешнего поля, дополнительно вешаем обработчик на него 266 | ret.condition = "$('"+condition.selector+"').is(':checked')"; 267 | ret.handler.push("$('"+condition.selector+"')"); 268 | } else {//Селектор не задан, проверяем собственное поле 269 | ret.condition = "field.is(':checked')"; 270 | } 271 | break; 272 | case 'select': 273 | _condition=''; 274 | //В condition.value должен быть массив переведённый в строку - об этом заботимся в complete_condition 275 | if (condition.selector!=='') {//Задан селектор внешнего поля, дополнительно вешаем обработчик на него 276 | if (condition.min) {//Генерируем условие для min, если оно задано 277 | _condition+=" && (get_selected_options($('"+condition.selector+"')).length >= "+condition.min+")"; 278 | } 279 | if (condition.max) {//Генерируем условие для max, если оно задано 280 | _condition+=" && (get_selected_options($('"+condition.selector+"')).length <= "+condition.max+")"; 281 | } 282 | ret.condition = condition.value+".equals (get_selected_options($('"+condition.selector+"')),"+condition.strict+")"+_condition; 283 | ret.handler.push("$('"+condition.selector+"')"); 284 | } else {//Селектор не задан, проверяем собственное поле 285 | 286 | if (condition.min) {//Генерируем условие для min, если оно задано 287 | _condition+=" && (get_selected_options(field).length >= "+condition.min+")"; 288 | } 289 | if (condition.max) {//Генерируем условие для max, если оно задано 290 | _condition+=" && (get_selected_options(field).length <= "+condition.max+")"; 291 | } 292 | ret.condition = condition.value+".equals (get_selected_options(field),"+condition.strict+")"+_condition; 293 | } 294 | break; 295 | case 'set': 296 | _condition=''; 297 | summary_selector=''; 298 | //Поскольку в condition.value должен быть массив селекторов, нужно разобрать его, сделав один большой селектор 299 | for (index=0;index= "+condition.min+")"; 307 | } 308 | if (condition.max) {//Генерируем условие для max, если оно задано 309 | _condition+=" && ($('"+condition.selector+summary_selector+"').length <= "+condition.max+")"; 310 | } 311 | ret.condition = "is_fields_checked($('"+condition.selector+"'),"+ JSON.stringify(condition.value)+","+condition.strict+")"+_condition; 312 | /*Для блоков требуется эмулировать onchange, поэтому передаём вот такой селектор*/ 313 | ret.handler.push("$('"+condition.selector+"').find('input[type=checkbox], input[type=radio]')"); 314 | } else {//Селектор не задан, проверяем собственное поле 315 | if (condition.min) {//Генерируем условие для min, если оно задано 316 | _condition+=" && (field.find('"+summary_selector+"').length >= "+condition.min+")"; 317 | } 318 | if (condition.max) {//Генерируем условие для max, если оно задано 319 | _condition+=" && (field.find('"+summary_selector+"').length <= "+condition.max+")"; 320 | } 321 | ret.condition = "is_fields_checked(field,"+ JSON.stringify(condition.value)+","+condition.strict+")"+_condition; 322 | /*Для блоков требуется эмулировать onchange, поэтому передаём вот такой селектор*/ 323 | ret.handler.push("field.find('input[type=checkbox], input[type=radio]')"); 324 | } 325 | break; 326 | case 'native': 327 | ret.condition=condition.value; 328 | if (condition.selector!=='') { //Задан селектор внешнего поля, дополнительно вешаем обработчик на него 329 | ret.handler.push("$('"+condition.selector+"')"); 330 | } 331 | break; 332 | } 333 | ret.handler.push("field"); 334 | if (condition.invert) { 335 | ret.condition=condition.logic+" !("+ret.condition+")"; 336 | } else { 337 | ret.condition=condition.logic+" ("+ret.condition+")"; 338 | } 339 | return (ret); 340 | 341 | } 342 | 343 | /* 344 | * field - jQuery-объект поля, к которому применяется правило 345 | * rule - применяемое правило 346 | * conditions - набор условий, при которых правило применяется 347 | */ 348 | function apply_rule(field,rule,conditions){ 349 | var summary_conditions={ 350 | condition:"", 351 | handler:[] 352 | }, 353 | js_condition,i,h,condition, action; 354 | for (condition in conditions){ 355 | if ($.isNumeric(condition)) js_condition=get_js_condition(conditions[condition],field);//В conditions может быть нецифровой параметр, дополняющий набор правил 356 | summary_conditions.condition+=" "+js_condition.condition; 357 | for (i=0;iarray.length)) return (false); 475 | 476 | var i; 477 | if (!Array.prototype.indexOf) {/*честно скопипащено из интернетов и не проверялось, т.к. все современные браузеры должны поддерживать indexOf*/ 478 | Array.prototype.indexOf = function(elt /*, from*/) { 479 | var len = this.length >>> 0; 480 | var from = Number(arguments[1]) || 0; 481 | from = (from < 0) ? Math.ceil(from) : Math.floor(from); 482 | if (from < 0) from += len; 483 | for (; from < len; from++){ 484 | if (from in this && this[from] === elt) return from; 485 | } 486 | return -1; 487 | }; 488 | } 489 | 490 | for (i=0;i