Задача: Валидация, динамическая проверка заполнения html форм
Исходник: ITForms - валидация HTML-форм и расширение функциональности, язык: javascript [code #632, hits: 14761]
аноним: Игорь Тарасов [добавлен: 04.11.2011]
  1. /**
  2. *
  3. * Библиотека ITForms.js (Версия 1.2) разработана в компании ITSoft (itsoft.ru)
  4. * Авторы: Игорь Тарасов
  5. * Если у вас есть идеи по расширению функциональных возможностей или рефакторингу кода, то пишите на адрес igor@itsoft.ru
  6. *
  7. */
  8. jQuery(function($){
  9. $.datepicker.regional['ru'] = {
  10. closeText: 'Закрыть',
  11. prevText: '<Пред',
  12. nextText: 'След>',
  13. currentText: 'Сегодня',
  14. monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
  15. 'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
  16. monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
  17. 'Июл','Авг','Сен','Окт','Ноя','Дек'],
  18. dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
  19. dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
  20. dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
  21. weekHeader: 'Нед',
  22. dateFormat: 'yy-mm-dd',
  23. firstDay: 1,
  24. isRTL: false,
  25. showMonthAfterYear: true,
  26. changeMonth: true,
  27. changeYear: true,
  28. yearSuffix: ''};
  29. $.datepicker.setDefaults($.datepicker.regional['ru']);
  30.  
  31. $.timepicker.regional['ru'] = {
  32. timeOnlyTitle: 'Выберите время',
  33. timeText: 'Время',
  34. hourText: 'Часы',
  35. minuteText: 'Минуты',
  36. secondText: 'Секунды',
  37. millisecText: 'миллисекунды',
  38. currentText: 'Сейчас',
  39. closeText: 'Закрыть',
  40. showSecond: true,
  41. timeFormat: 'hh:mm:ss',
  42. ampm: false};
  43.  
  44. $.timepicker.setDefaults($.timepicker.regional['ru']);
  45. });
  46.  
  47. (function( $ ) {
  48. $.widget( "ui.combobox", {
  49. _create: function() {
  50. var self = this,
  51. select = this.element.hide(),
  52. selected = select.children( ":selected" ),
  53. value = selected.val() ? selected.text() : "";
  54. var input = this.input = $( "<input>" )
  55. .insertAfter( select )
  56. .val( value )
  57. .autocomplete({
  58. delay: 0,
  59. minLength: 0,
  60. source: function( request, response ) {
  61. var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
  62. response( select.children( "option" ).map(function() {
  63. var text = $( this ).text();
  64. if ( this.value && ( !request.term || matcher.test(text) ) )
  65. return {
  66. label: text.replace(
  67. new RegExp(
  68. "(?![^&;]+;)(?!<[^<>]*)(" +
  69. $.ui.autocomplete.escapeRegex(request.term) +
  70. ")(?![^<>]*>)(?![^&;]+;)", "gi"
  71. ), "<strong>$1</strong>" ),
  72. value: text,
  73. option: this
  74. };
  75. }) );
  76. },
  77. select: function( event, ui ) {
  78. ui.item.option.selected = true;
  79. self._trigger( "selected", event, {
  80. item: ui.item.option
  81. });
  82. },
  83. change: function( event, ui ) {
  84. if ( !ui.item ) {
  85. var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
  86. valid = false;
  87. select.children( "option" ).each(function() {
  88. if ( $( this ).text().match( matcher ) ) {
  89. this.selected = valid = true;
  90. return false;
  91. }
  92. });
  93. if ( !valid ) {
  94. // remove invalid value, as it didn't match anything
  95. $( this ).val( "" );
  96. select.val( "" );
  97. input.data( "autocomplete" ).term = "";
  98. return false;
  99. }
  100. }
  101. }
  102. })
  103. .addClass( "ui-widget ui-widget-content ui-corner-left" );
  104.  
  105. input.data( "autocomplete" )._renderItem = function( ul, item ) {
  106. return $( "<li></li>" )
  107. .data( "item.autocomplete", item )
  108. .append( "<a>" + item.label + "</a>" )
  109. .appendTo( ul );
  110. };
  111.  
  112. this.button = $( "<button type='button'>&nbsp;</button>" )
  113. .attr( "tabIndex", -1 )
  114. .attr( "title", "Show All Items" )
  115. .insertAfter( input )
  116. .button({
  117. icons: {
  118. primary: "ui-icon-triangle-1-s"
  119. },
  120. text: false
  121. })
  122. .removeClass( "ui-corner-all" )
  123. .addClass( "ui-corner-right ui-button-icon" )
  124. .click(function() {
  125. // close if already visible
  126. if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
  127. input.autocomplete( "close" );
  128. return;
  129. }
  130.  
  131. // work around a bug (likely same cause as #5265)
  132. $( this ).blur();
  133.  
  134. // pass empty string as value to search for, displaying all results
  135. input.autocomplete( "search", "" );
  136. input.focus();
  137. });
  138. },
  139.  
  140. destroy: function() {
  141. this.input.remove();
  142. this.button.remove();
  143. this.element.show();
  144. $.Widget.prototype.destroy.call( this );
  145. }
  146. });
  147. })( jQuery );
  148.  
  149. function ITForm(thisForm)
  150. {
  151. var thisObject = this, checkboxes=[], mergeablefields=[], radios=[];
  152.  
  153. //Public methods
  154.  
  155. this.checkForm = function()
  156. {
  157. var formState = true;
  158. thisForm.find(':input').each(
  159. function()
  160. {
  161. if($(this).attr('type')=='button' || $(this).attr('type')=='submit')
  162. return true;//переходим к следующему
  163.  
  164. var errorMsg = getFieldErrorMsg($(this));
  165. if(errorMsg)
  166. {
  167. //$(this).focus();
  168. formState = false;
  169. setFieldState($(this), 'itform_error_text', errorMsg);
  170. }
  171. else
  172. setFieldState($(this), 'itform_success_text');
  173. });
  174.  
  175. return formState;
  176. };
  177.  
  178. //sendAs: array -- по умолчанию, csv -- checkboxname=v1,v2,v3, sum -- сумма всех отмеченных
  179. this.setCheckboxProperty = function(checkboxname, sendAs, minSelected, maxSelected)
  180. {
  181. checkboxes[checkboxname] = {sendAs : sendAs, minSelected : (minSelected?minSelected:0), maxSelected: (maxSelected?maxSelected:0)};
  182. };
  183.  
  184. this.setRadioProperty = function(radioname, required)
  185. {
  186. radios[radioname] = {required : required};
  187. };
  188.  
  189. this.addMergeableField = function(fieldname, separator, inputs)
  190. {
  191. mergeablefields[fieldname] = {separator : separator, inputs : inputs};
  192. };
  193.  
  194.  
  195. //После добавления нового поля в форму нужно повесить обработчики событий
  196. //Данный метод вешает bind на поля
  197. this.addEventListeners = function(input)
  198. {
  199. fieldType = input.attr('type');
  200.  
  201. //Ставим help icon
  202. if(input.attr('data-help'))
  203. if(input.get(0).nodeName.toLowerCase()=='select' || fieldType == 'file' || input.attr('data-datepicker') || input.attr('data-datetimepicker') || input.attr('data-timepicker'))
  204. displayHelpIcon(input);
  205.  
  206. if(fieldType=='button' || fieldType=='submit')
  207. return true; //переходим к следующему элементу управления, если нас вызвали в цикле jQuery
  208.  
  209. if( (fieldType == 'text' || fieldType == 'textarea') && input.attr('value') == '')
  210. {
  211. input.attr('value', input.attr('data-placeholder'));
  212. setFieldState(input, 'itform_empty_text');
  213. }
  214.  
  215. if(input.attr('data-datepicker') || input.attr('data-datetimepicker'))
  216. {
  217. var dt_settings = new Object();
  218.  
  219. if(input.attr('data-min-value'))
  220. {
  221. dt_settings.minDate = input.attr('data-min-value');
  222. dt_settings.yearRange = (new Date(input.attr('data-min-value'))).getFullYear() + ':';
  223. }
  224. else
  225. dt_settings.yearRange = 'c-10:';
  226.  
  227. if(input.attr('data-max-value'))
  228. {
  229. dt_settings.maxDate = input.attr('data-max-value');
  230. dt_settings.yearRange += (new Date(input.attr('data-max-value'))).getFullYear();
  231. }
  232. else
  233. dt_settings.yearRange += 'c+10';
  234. }
  235.  
  236.  
  237. if(input.attr('data-datepicker'))
  238. input.datepicker(dt_settings);
  239. else if(input.attr('data-datetimepicker'))
  240. input.datetimepicker(dt_settings);
  241. else if(input.attr('data-timepicker'))
  242. input.timepicker(dt_settings);
  243. else if(input.attr('data-combobox'))
  244. input.combobox();
  245. else if(input.attr('data-slider'))
  246. {
  247. var sliderSettings = ( input.attr('data-slider-option') ? eval('('+input.attr('data-slider-option')+')') : new Object() );
  248. sliderSettings.min = parseInt(input.attr('data-min-value'));
  249. sliderSettings.max = parseInt(input.attr('data-max-value'));
  250.  
  251. var range_to = $('#'+input.attr('data-slider'));
  252. if(range_to.attr('name'))
  253. {
  254. sliderSettings.range = true;
  255. sliderSettings.values = [parseInt(input.attr('value')?input.attr('value'):0), parseInt(range_to.attr('value')?range_to.attr('value'):0)];
  256. sliderSettings.slide = function(event, ui ){input.val(ui.values[0]);input.change();range_to.val(ui.values[1]);range_to.change();};
  257. input.bind({keyup:function(){if(this.value>=sliderSettings.min && this.value<=sliderSettings.max && this.value<=$('#slider-'+this.name).slider('values',1))$('#slider-'+this.name).slider('values', 0, this.value);}});
  258. range_to.bind({keyup:function(){if(this.value>=sliderSettings.min && this.value<=sliderSettings.max && this.value>=$('#slider-'+input.attr('name')).slider('values', 0))$('#slider-'+input.attr('name')).slider('values', 1, this.value);}});
  259. }
  260. else
  261. {
  262. sliderSettings.value = parseInt(input.attr('value')?input.attr('value'):0);
  263. sliderSettings.slide = function(event, ui ){input.val(ui.value);input.change();};
  264. input.bind({keyup:function()
  265. {
  266. if(this.value>=sliderSettings.min && this.value<=sliderSettings.max)
  267. $('#slider-'+this.name).slider('value', this.value);
  268. }});
  269. }
  270. $('#slider-'+input.attr('name')).slider(sliderSettings);
  271. }
  272.  
  273. input.bind({blur : eventBlur, focus : eventFocus, keyup: eventKeyup, change : eventChange});
  274.  
  275. if(input.attr('data-enable-chars'))
  276. input.bind('keypress', eventKeypress);
  277. };
  278.  
  279. /*
  280. * Private methods
  281. */
  282. var
  283.  
  284. eventSubmit = function()
  285. {
  286.  
  287. if(thisObject.checkForm())
  288. {
  289. thisForm.find(':input').each(
  290. function()
  291. {
  292. if($(this).attr('value') == $(this).attr('data-placeholder') && !$(this).attr('valuehaschanged'))
  293. $(this).attr('value', '');
  294. });
  295.  
  296. packCheckboxes();
  297. mergeFields();
  298. deleteEmptyFields();
  299. return true;
  300. }
  301. else
  302. return false;
  303. },
  304.  
  305.  
  306. //Метод для проверки поля при keyup
  307. eventKeypress = function(e)
  308. {
  309. var system = [8,9,13,35,36,37,38,39,40,46,116]; //Delete, BackSpace, Enter, Left, Right, Tab, F5
  310. if(system.indexOf(e.keyCode)!=-1 ||
  311. (e.metaKey||e.ctrlKey) || //Command || Ctrl + any key && (e.which==120||e.which==99||e.which==118||e.which==97||e.which==88||e.which==67||e.which==86||e.which==65)) //A X C V
  312. (e.shiftKey && (e.keyCode==45)) //Shift+Insert
  313. )
  314. return true;
  315.  
  316. if($(this).attr('data-enable-chars'))
  317. {
  318. var c = String.fromCharCode(e.which);
  319. if(c.search(regexpstring2RegExp($(this).attr('data-enable-chars')))==-1)
  320. return false;
  321. }
  322.  
  323. return true;
  324. },
  325.  
  326.  
  327. eventKeyup = function()
  328. {
  329. if($(this).attr('data-async')!='false')
  330. {
  331. var errorMsg = getFieldErrorMsg($(this));
  332. if(!errorMsg)
  333. setFieldState($(this), 'itform_success_text');
  334. else
  335. setFieldState($(this), 'itform_error_text', errorMsg);
  336. }
  337. else
  338. setFieldState($(this), '');
  339. },
  340.  
  341. //Метод для проверки поля при change
  342. eventChange = function()
  343. {
  344. $(this).attr('valuehaschanged', true);
  345.  
  346. var errorMsg = getFieldErrorMsg($(this));
  347. if(!errorMsg)
  348. setFieldState($(this), 'itform_success_text');
  349. else
  350. setFieldState($(this), 'itform_error_text', errorMsg);
  351. },
  352.  
  353.  
  354. eventBlur = function()
  355. {
  356. if($(this).attr('value') == '')
  357. {
  358. if(!$(this).attr('valuehaschanged'))
  359. $(this).attr('value', $(this).attr('data-placeholder'));
  360.  
  361. setFieldState($(this), 'itform_empty_text');
  362. }
  363.  
  364. //Удаляем плашку help
  365. $('#itform_help').remove();
  366. },
  367.  
  368.  
  369. eventFocus = function()
  370. {
  371. var input = $(this);
  372. //Если текст в поле равен placeholderValue
  373. if($(this).attr('value') == $(this).attr('data-placeholder') && !$(this).attr('valuehaschanged'))
  374. $(this).attr('value', '');
  375.  
  376. //Если есть help выводим плашку
  377. if($(this).attr('data-help') && !(input.get(0).nodeName.toLowerCase()=='select' || input.attr('type') == 'file' || input.attr('data-datepicker') || input.attr('data-datetimepicker') || input.attr('data-timepicker')) )
  378. $('body').append('<div id="itform_help" style="top:' + (getInputYPosition($(this)) + $(this).height() + 14) + 'px; left:' +
  379. getInputXPosition($(this)) + 'px;"><div id="itform_arrow"></div>' + $(this).attr('data-help') + '</div>');
  380. },
  381.  
  382.  
  383. //Метод для отображения иконки help
  384. displayHelpIcon = function(input)
  385. {
  386. var left=input.width()+10,
  387. top = (input.height()/2) - 8;
  388.  
  389. if(input.attr('type') == 'text')
  390. {
  391. top += 2;
  392. left += 3;
  393. }
  394.  
  395. input.after('<img src="/common/itforms/itforms_help.png" id="field_' + input.attr('name') + '" align="absmiddle" style="cursor:pointer;margin:2px;">');
  396. $('#field_' + input.attr('name')).bind('click', onHelpClick);
  397. },
  398.  
  399. //Событие для обработки клика по значку help
  400. onHelpClick = function()
  401. {
  402. var help = $(':input[name="' + $(this).attr('id').substr(6) + '"]').attr('data-help');
  403. openHelp($(this), help);
  404. $(document).bind('mouseup', closeHelp);
  405. },
  406.  
  407.  
  408. openHelp = function(icon, help)
  409. {
  410. var top = getInputYPosition(icon) + icon.height() + 14,
  411. left = getInputXPosition(icon);
  412.  
  413. $('body').append('<div id="itform_help" style="top:' + top + 'px; left:' + left + 'px;"><div id="itform_arrow"></div>' + help + '</div>');
  414. $('#itform_help').css('left', (getInputXPosition(icon) - $('#itform_help').width()/2 ) + 'px');
  415. $('#itform_arrow').css({'left': '50%', 'margin-left': '-5.5px'});
  416. },
  417.  
  418.  
  419. //Закрыть плашку help
  420. closeHelp = function()
  421. {
  422. $(document).unbind('mouseup');
  423. $('#itform_help').remove();
  424. },
  425.  
  426.  
  427. //Метод, который назначает полю нужные классы в зависимости от состояния
  428. setFieldState = function(input, state, errorMsg)
  429. {
  430. removeOurClasses(input);
  431. input.addClass(state);
  432.  
  433. if(state=='itform_error_text')
  434. {
  435. var top = getInputYPosition(input), left = getInputXPosition(input) + input.width() + 50;
  436. if(!$('#errorMsgdiv_'+input.attr('name'))[0])
  437. $('body').append('<div class="itform_errorMsgDiv" id="errorMsgdiv_'+input.attr('name')+'" style="top:' + top + 'px; left:' + left + 'px;">' + errorMsg + '</div>');
  438. }
  439. else
  440. $('#errorMsgdiv_'+input.attr('name')).remove();
  441. },
  442.  
  443.  
  444. //Удаляем служебные классы для данного поля
  445. removeOurClasses = function(input)
  446. {
  447. input.removeClass('itform_error_text');
  448. input.removeClass('itform_empty_text');
  449. input.removeClass('itform_success_text');
  450. },
  451.  
  452.  
  453. //Метод для определения позиции x на экране для this элемента
  454. getInputXPosition = function(self)
  455. {
  456. return self.offset().left;
  457. },
  458.  
  459.  
  460. //Метод для определения позиции y на экране для this элемента
  461. getInputYPosition = function(self)
  462. {
  463. return self.offset().top;
  464. },
  465.  
  466.  
  467. //Метод удаляет все пустые поля формы
  468. deleteEmptyFields = function()
  469. {
  470. if(thisForm.attr('data-dont-send-empty-fields'))
  471. thisForm.find(':input').each(
  472. function()
  473. {
  474. if(!this.value.length || (!$(this).attr('valuehaschanged') && $(this).attr('data-placeholder')==this.value) )
  475. $(this).removeAttr('name');
  476. });
  477. },
  478.  
  479.  
  480. //Метод для проверки поля
  481. getFieldErrorMsg = function(input)
  482. {
  483. var fieldType = input.attr('type');
  484. var value = (input.attr('value')==input.attr('data-placeholder') && !input.attr('valuehaschanged')) ? '' : input.attr('value');
  485.  
  486. if(input.attr('data-min-length') && value.length < parseInt(input.attr('data-min-length')) )
  487. return 'Значение должно содержать минимум '+input.attr('data-min-length')+' символов';
  488.  
  489.  
  490.  
  491. if(input.attr('data-user-func'))
  492. {
  493. var errorMsg = eval(input.attr('data-user-func'));
  494. if(errorMsg)
  495. return errorMsg;
  496. }
  497.  
  498. if(fieldType == 'text' || fieldType == 'textarea' || fieldType == 'password')
  499. {
  500. if(input.attr('data-regexp') && value && value.search(regexpstring2RegExp(input.attr('data-regexp')))==-1)
  501. return 'Значение «'+value+'» должно удовлетворять условиям регулярного выражения '+input.attr('data-regexp');
  502.  
  503. if(input.attr('data-min-value') && (input.attr('data-datepicker')||input.attr('data-datetimepicker')) && (new Date(value)).getTime() < (new Date(input.attr('data-min-value'))).getTime() )
  504. return 'Значение «'+value+'» должно быть не меньше '+input.attr('data-min-value');
  505. else if(input.attr('data-min-value') && parseFloat(value) < parseFloat(input.attr('data-min-value')) )
  506. return 'Значение «'+value+'» должно быть не меньше '+input.attr('data-min-value');
  507.  
  508. if(input.attr('data-max-value') && (input.attr('data-datepicker')||input.attr('data-datetimepicker')) && (new Date(value)).getTime() > (new Date(input.attr('data-max-value'))).getTime() )
  509. return 'Значение «'+value+'» должно быть не больше '+input.attr('data-max-value');
  510. else if(input.attr('data-max-value') && parseFloat(value) > parseFloat(input.attr('data-max-value')) )
  511. return 'Значение «'+value+'» должно быть не больше '+input.attr('data-max-value');
  512.  
  513. if(input.attr('data-slider') && parseInt(input.attr('value'))>parseInt($('#'+input.attr('data-slider')).attr('value')))
  514. return 'Значение ОТ не может быть больше значения ДО';
  515. else if(input.attr('data-slider-from') && parseInt(input.attr('value'))<parseInt($('#'+input.attr('data-slider-from')).attr('value')))
  516. return 'Значение ДО не может быть меньше значения ОТ';
  517. }
  518. else if(fieldType=='radio' && radios[input.attr('name')] && radios[input.attr('name')].required && !thisForm.find('input[name='+input.attr('name')+']:checked').val())
  519. return 'Необходимо выбрать один из вариантов';
  520.  
  521. else if(fieldType=='checkbox' && checkboxes[input.attr('name')] && checkboxes[input.attr('name')].minSelected && thisForm.find('input[name='+input.attr('name')+']:checked').size() < checkboxes[input.attr('name')].minSelected )
  522. return 'Должно быть выбрано не меньше '+checkboxes[input.attr('name')].minSelected + ' элементов';
  523.  
  524. else if(fieldType=='checkbox' && checkboxes[input.attr('name')] && checkboxes[input.attr('name')].maxSelected && thisForm.find('input[name='+input.attr('name')+']:checked').size() > checkboxes[input.attr('name')].maxSelected )
  525. return 'Должно быть выбрано не больше '+checkboxes[input.attr('name')].maxSelected + ' элементов';
  526.  
  527. else if(input.attr('data-min-selected') && input.children('option:selected').size() < parseInt(input.attr('data-min-selected')) )
  528. return 'Должно быть выбрано не меньше '+input.attr('data-min-selected') + ' элементов';
  529.  
  530. else if(input.attr('data-max-selected') && input.children('option:selected').size() > parseInt(input.attr('data-max-selected')) )
  531. return 'Должно быть выбрано не больше '+input.attr('data-max-selected') + ' элементов';
  532.  
  533. else if (fieldType == 'file' && input.attr('data-file-type') && value)
  534. {
  535. var ext = input.val().split('.').pop(),
  536. fileAttrInArray = input.attr('data-file-type').split(',');
  537. if(fileAttrInArray.indexOf(ext)==-1)
  538. return 'Выбран файл с расширением «' + ext + '» Но вы должны выбрать файл с одним из следующих расширений ' + input.attr('data-file-type');
  539. }
  540.  
  541. return 0;
  542. },
  543.  
  544.  
  545. regexpstring2RegExp = function(str)
  546. {
  547. var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?u?)$'));
  548. if(!match)
  549. return /.*/;
  550. var params = match[2].replace(/u/, '');
  551. return new RegExp(match[1], params);
  552. },
  553.  
  554.  
  555. packCheckboxes = function()
  556. {
  557. for(var checkboxname in checkboxes)
  558. {
  559. var result, cb = checkboxes[checkboxname];
  560. if (cb.sendAs == 'csv') //Если необходимо собрать результат в виде csv-строки
  561. {
  562. result = '';
  563. thisForm.find(":input[name='" + checkboxname + "']:checked").each(
  564. function()
  565. {
  566. result += (result?',':'') + $(this).val();
  567. $(this).removeAttr('name');
  568. });
  569. thisForm.find(":input[name='" + checkboxname + "']").each(function(){$(this).removeAttr('name');});
  570. thisForm.prepend('<input type="hidden" value="' + result + '" name="' + checkboxname + '" />');
  571. }
  572. else if (cb.sendAs == 'sum') //Если необходимо сложить все результаты checkbox
  573. {
  574. //Перебираем все поля с указанным именем и складываем все значения
  575. result = 0;
  576. thisForm.find(":input[name='" + checkboxname + "']:checked").each(
  577. function()
  578. {
  579. result |= parseInt($(this).val());
  580. $(this).removeAttr('name');
  581. });
  582. thisForm.find(":input[name='" + checkboxname + "']").each(function(){$(this).removeAttr('name');});
  583. thisForm.prepend('<input type="hidden" value="' + result + '" name="' + checkboxname + '" />');
  584. }
  585. }//for
  586. },
  587.  
  588.  
  589. //Соединение полей в одно значение
  590. mergeFields = function()
  591. {
  592. for(var mfieldname in mergeablefields)
  593. {
  594. var result = '', mfield = mergeablefields[mfieldname];
  595. for (var key in mfield.inputs)
  596. {
  597. thisForm.find(":input[name='" + mfield.inputs[key] + "']").each(
  598. function()
  599. {
  600. result += (result?mfield.separator:'') + $(this).val();
  601. $(this).removeAttr('name');
  602. });
  603. }//for (var key
  604. //Создаем поле type="hidden"
  605. result = result.split('"').join('&quot;');
  606. result = result.split('<').join('&lt;');
  607. result = result.split('>').join('&gt;');
  608. thisForm.prepend('<input type="hidden" name="' + mfieldname + '" value="' + result + '" />');
  609. }//for(var mfield
  610. };
  611.  
  612.  
  613. //Проходимся по всем полям на форме и вешаем на них bind событий
  614. thisForm.find(':input').each(
  615. function()
  616. {
  617. thisObject.addEventListeners($(this));
  618. });
  619.  
  620. thisForm.bind('submit', eventSubmit);
  621. }
Позволяет задать ряд ограничений целостности на вводимые данные. Большинство настроек задаются в виде атрибутов HTML-тегов input и select. http://itsoft.ru

+добавить реализацию