/**
*
* Библиотека ITForms.js (Версия 1.2) разработана в компании ITSoft (itsoft.ru)
* Авторы: Игорь Тарасов
* Если у вас есть идеи по расширению функциональных возможностей или рефакторингу кода, то пишите на адрес igor@itsoft.ru
*
*/
jQuery(function($){
$.datepicker.regional['ru'] = {
closeText: 'Закрыть',
prevText: '<Пред',
nextText: 'След>',
currentText: 'Сегодня',
monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь',
'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн',
'Июл','Авг','Сен','Окт','Ноя','Дек'],
dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'],
dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
weekHeader: 'Нед',
dateFormat: 'yy-mm-dd',
firstDay: 1,
isRTL: false,
showMonthAfterYear: true,
changeMonth: true,
changeYear: true,
yearSuffix: ''};
$.datepicker.setDefaults($.datepicker.regional['ru']);
$.timepicker.regional['ru'] = {
timeOnlyTitle: 'Выберите время',
timeText: 'Время',
hourText: 'Часы',
minuteText: 'Минуты',
secondText: 'Секунды',
millisecText: 'миллисекунды',
currentText: 'Сейчас',
closeText: 'Закрыть',
showSecond: true,
timeFormat: 'hh:mm:ss',
ampm: false};
$.timepicker.setDefaults($.timepicker.regional['ru']);
});
(function( $ ) {
$.widget( "ui.combobox", {
_create: function() {
var self = this,
select = this.element.hide(),
selected = select.children( ":selected" ),
value = selected.val() ? selected.text() : "";
var input = this.input = $( "<input>" )
.insertAfter( select )
.val( value )
.autocomplete({
delay: 0,
minLength: 0,
source: function( request, response ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
response( select.children( "option" ).map(function() {
var text = $( this ).text();
if ( this.value && ( !request.term || matcher.test(text) ) )
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"
), "<strong>$1</strong>" ),
value: text,
option: this
};
}) );
},
select: function( event, ui ) {
ui.item.option.selected = true;
self._trigger( "selected", event, {
item: ui.item.option
});
},
change: function( event, ui ) {
if ( !ui.item ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
valid = false;
select.children( "option" ).each(function() {
if ( $( this ).text().match( matcher ) ) {
this.selected = valid = true;
return false;
}
});
if ( !valid ) {
// remove invalid value, as it didn't match anything
$( this ).val( "" );
select.val( "" );
input.data( "autocomplete" ).term = "";
return false;
}
}
}
})
.addClass( "ui-widget ui-widget-content ui-corner-left" );
input.data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.label + "</a>" )
.appendTo( ul );
};
this.button = $( "<button type='button'> </button>" )
.attr( "tabIndex", -1 )
.attr( "title", "Show All Items" )
.insertAfter( input )
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )
.addClass( "ui-corner-right ui-button-icon" )
.click(function() {
// close if already visible
if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
input.autocomplete( "close" );
return;
}
// work around a bug (likely same cause as #5265)
$( this ).blur();
// pass empty string as value to search for, displaying all results
input.autocomplete( "search", "" );
input.focus();
});
},
destroy: function() {
this.input.remove();
this.button.remove();
this.element.show();
$.Widget.prototype.destroy.call( this );
}
});
})( jQuery );
function ITForm(thisForm)
{
var thisObject = this, checkboxes=[], mergeablefields=[], radios=[];
//Public methods
this.checkForm = function()
{
var formState = true;
thisForm.find(':input').each(
function()
{
if($(this).attr('type')=='button' || $(this).attr('type')=='submit')
return true;//переходим к следующему
var errorMsg = getFieldErrorMsg($(this));
if(errorMsg)
{
//$(this).focus();
formState = false;
setFieldState($(this), 'itform_error_text', errorMsg);
}
else
setFieldState($(this), 'itform_success_text');
});
return formState;
};
//sendAs: array -- по умолчанию, csv -- checkboxname=v1,v2,v3, sum -- сумма всех отмеченных
this.setCheckboxProperty = function(checkboxname, sendAs, minSelected, maxSelected)
{
checkboxes[checkboxname] = {sendAs : sendAs, minSelected : (minSelected?minSelected:0), maxSelected: (maxSelected?maxSelected:0)};
};
this.setRadioProperty = function(radioname, required)
{
radios[radioname] = {required : required};
};
this.addMergeableField = function(fieldname, separator, inputs)
{
mergeablefields[fieldname] = {separator : separator, inputs : inputs};
};
//После добавления нового поля в форму нужно повесить обработчики событий
//Данный метод вешает bind на поля
this.addEventListeners = function(input)
{
fieldType = input.attr('type');
//Ставим help icon
if(input.attr('data-help'))
if(input.get(0).nodeName.toLowerCase()=='select' || fieldType == 'file' || input.attr('data-datepicker') || input.attr('data-datetimepicker') || input.attr('data-timepicker'))
displayHelpIcon(input);
if(fieldType=='button' || fieldType=='submit')
return true; //переходим к следующему элементу управления, если нас вызвали в цикле jQuery
if( (fieldType == 'text' || fieldType == 'textarea') && input.attr('value') == '')
{
input.attr('value', input.attr('data-placeholder'));
setFieldState(input, 'itform_empty_text');
}
if(input.attr('data-datepicker') || input.attr('data-datetimepicker'))
{
var dt_settings = new Object();
if(input.attr('data-min-value'))
{
dt_settings.minDate = input.attr('data-min-value');
dt_settings.yearRange = (new Date(input.attr('data-min-value'))).getFullYear() + ':';
}
else
dt_settings.yearRange = 'c-10:';
if(input.attr('data-max-value'))
{
dt_settings.maxDate = input.attr('data-max-value');
dt_settings.yearRange += (new Date(input.attr('data-max-value'))).getFullYear();
}
else
dt_settings.yearRange += 'c+10';
}
if(input.attr('data-datepicker'))
input.datepicker(dt_settings);
else if(input.attr('data-datetimepicker'))
input.datetimepicker(dt_settings);
else if(input.attr('data-timepicker'))
input.timepicker(dt_settings);
else if(input.attr('data-combobox'))
input.combobox();
else if(input.attr('data-slider'))
{
var sliderSettings = ( input.attr('data-slider-option') ? eval('('+input.attr('data-slider-option')+')') : new Object() );
sliderSettings.min = parseInt(input.attr('data-min-value'));
sliderSettings.max = parseInt(input.attr('data-max-value'));
var range_to = $('#'+input.attr('data-slider'));
if(range_to.attr('name'))
{
sliderSettings.range = true;
sliderSettings.values = [parseInt(input.attr('value')?input.attr('value'):0), parseInt(range_to.attr('value')?range_to.attr('value'):0)];
sliderSettings.slide = function(event, ui ){input.val(ui.values[0]);input.change();range_to.val(ui.values[1]);range_to.change();};
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);}});
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);}});
}
else
{
sliderSettings.value = parseInt(input.attr('value')?input.attr('value'):0);
sliderSettings.slide = function(event, ui ){input.val(ui.value);input.change();};
input.bind({keyup:function()
{
if(this.value>=sliderSettings.min && this.value<=sliderSettings.max)
$('#slider-'+this.name).slider('value', this.value);
}});
}
$('#slider-'+input.attr('name')).slider(sliderSettings);
}
input.bind({blur : eventBlur, focus : eventFocus, keyup: eventKeyup, change : eventChange});
if(input.attr('data-enable-chars'))
input.bind('keypress', eventKeypress);
};
/*
* Private methods
*/
var
eventSubmit = function()
{
if(thisObject.checkForm())
{
thisForm.find(':input').each(
function()
{
if($(this).attr('value') == $(this).attr('data-placeholder') && !$(this).attr('valuehaschanged'))
$(this).attr('value', '');
});
packCheckboxes();
mergeFields();
deleteEmptyFields();
return true;
}
else
return false;
},
//Метод для проверки поля при keyup
eventKeypress = function(e)
{
var system = [8,9,13,35,36,37,38,39,40,46,116]; //Delete, BackSpace, Enter, Left, Right, Tab, F5
if(system.indexOf(e.keyCode)!=-1 ||
(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
(e.shiftKey && (e.keyCode==45)) //Shift+Insert
)
return true;
if($(this).attr('data-enable-chars'))
{
var c = String.fromCharCode(e.which);
if(c.search(regexpstring2RegExp($(this).attr('data-enable-chars')))==-1)
return false;
}
return true;
},
eventKeyup = function()
{
if($(this).attr('data-async')!='false')
{
var errorMsg = getFieldErrorMsg($(this));
if(!errorMsg)
setFieldState($(this), 'itform_success_text');
else
setFieldState($(this), 'itform_error_text', errorMsg);
}
else
setFieldState($(this), '');
},
//Метод для проверки поля при change
eventChange = function()
{
$(this).attr('valuehaschanged', true);
var errorMsg = getFieldErrorMsg($(this));
if(!errorMsg)
setFieldState($(this), 'itform_success_text');
else
setFieldState($(this), 'itform_error_text', errorMsg);
},
eventBlur = function()
{
if($(this).attr('value') == '')
{
if(!$(this).attr('valuehaschanged'))
$(this).attr('value', $(this).attr('data-placeholder'));
setFieldState($(this), 'itform_empty_text');
}
//Удаляем плашку help
$('#itform_help').remove();
},
eventFocus = function()
{
var input = $(this);
//Если текст в поле равен placeholderValue
if($(this).attr('value') == $(this).attr('data-placeholder') && !$(this).attr('valuehaschanged'))
$(this).attr('value', '');
//Если есть help выводим плашку
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')) )
$('body').append('<div id="itform_help" style="top:' + (getInputYPosition($(this)) + $(this).height() + 14) + 'px; left:' +
getInputXPosition($(this)) + 'px;"><div id="itform_arrow"></div>' + $(this).attr('data-help') + '</div>');
},
//Метод для отображения иконки help
displayHelpIcon = function(input)
{
var left=input.width()+10,
top = (input.height()/2) - 8;
if(input.attr('type') == 'text')
{
top += 2;
left += 3;
}
input.after('<img src="/common/itforms/itforms_help.png" id="field_' + input.attr('name') + '" align="absmiddle" style="cursor:pointer;margin:2px;">');
$('#field_' + input.attr('name')).bind('click', onHelpClick);
},
//Событие для обработки клика по значку help
onHelpClick = function()
{
var help = $(':input[name="' + $(this).attr('id').substr(6) + '"]').attr('data-help');
openHelp($(this), help);
$(document).bind('mouseup', closeHelp);
},
openHelp = function(icon, help)
{
var top = getInputYPosition(icon) + icon.height() + 14,
left = getInputXPosition(icon);
$('body').append('<div id="itform_help" style="top:' + top + 'px; left:' + left + 'px;"><div id="itform_arrow"></div>' + help + '</div>');
$('#itform_help').css('left', (getInputXPosition(icon) - $('#itform_help').width()/2 ) + 'px');
$('#itform_arrow').css({'left': '50%', 'margin-left': '-5.5px'});
},
//Закрыть плашку help
closeHelp = function()
{
$(document).unbind('mouseup');
$('#itform_help').remove();
},
//Метод, который назначает полю нужные классы в зависимости от состояния
setFieldState = function(input, state, errorMsg)
{
removeOurClasses(input);
input.addClass(state);
if(state=='itform_error_text')
{
var top = getInputYPosition(input), left = getInputXPosition(input) + input.width() + 50;
if(!$('#errorMsgdiv_'+input.attr('name'))[0])
$('body').append('<div class="itform_errorMsgDiv" id="errorMsgdiv_'+input.attr('name')+'" style="top:' + top + 'px; left:' + left + 'px;">' + errorMsg + '</div>');
}
else
$('#errorMsgdiv_'+input.attr('name')).remove();
},
//Удаляем служебные классы для данного поля
removeOurClasses = function(input)
{
input.removeClass('itform_error_text');
input.removeClass('itform_empty_text');
input.removeClass('itform_success_text');
},
//Метод для определения позиции x на экране для this элемента
getInputXPosition = function(self)
{
return self.offset().left;
},
//Метод для определения позиции y на экране для this элемента
getInputYPosition = function(self)
{
return self.offset().top;
},
//Метод удаляет все пустые поля формы
deleteEmptyFields = function()
{
if(thisForm.attr('data-dont-send-empty-fields'))
thisForm.find(':input').each(
function()
{
if(!this.value.length || (!$(this).attr('valuehaschanged') && $(this).attr('data-placeholder')==this.value) )
$(this).removeAttr('name');
});
},
//Метод для проверки поля
getFieldErrorMsg = function(input)
{
var fieldType = input.attr('type');
var value = (input.attr('value')==input.attr('data-placeholder') && !input.attr('valuehaschanged')) ? '' : input.attr('value');
if(input.attr('data-min-length') && value.length < parseInt(input.attr('data-min-length')) )
return 'Значение должно содержать минимум '+input.attr('data-min-length')+' символов';
if(input.attr('data-user-func'))
{
var errorMsg = eval(input.attr('data-user-func'));
if(errorMsg)
return errorMsg;
}
if(fieldType == 'text' || fieldType == 'textarea' || fieldType == 'password')
{
if(input.attr('data-regexp') && value && value.search(regexpstring2RegExp(input.attr('data-regexp')))==-1)
return 'Значение «'+value+'» должно удовлетворять условиям регулярного выражения '+input.attr('data-regexp');
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() )
return 'Значение «'+value+'» должно быть не меньше '+input.attr('data-min-value');
else if(input.attr('data-min-value') && parseFloat(value) < parseFloat(input.attr('data-min-value')) )
return 'Значение «'+value+'» должно быть не меньше '+input.attr('data-min-value');
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() )
return 'Значение «'+value+'» должно быть не больше '+input.attr('data-max-value');
else if(input.attr('data-max-value') && parseFloat(value) > parseFloat(input.attr('data-max-value')) )
return 'Значение «'+value+'» должно быть не больше '+input.attr('data-max-value');
if(input.attr('data-slider') && parseInt(input.attr('value'))>parseInt($('#'+input.attr('data-slider')).attr('value')))
return 'Значение ОТ не может быть больше значения ДО';
else if(input.attr('data-slider-from') && parseInt(input.attr('value'))<parseInt($('#'+input.attr('data-slider-from')).attr('value')))
return 'Значение ДО не может быть меньше значения ОТ';
}
else if(fieldType=='radio' && radios[input.attr('name')] && radios[input.attr('name')].required && !thisForm.find('input[name='+input.attr('name')+']:checked').val())
return 'Необходимо выбрать один из вариантов';
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 )
return 'Должно быть выбрано не меньше '+checkboxes[input.attr('name')].minSelected + ' элементов';
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 )
return 'Должно быть выбрано не больше '+checkboxes[input.attr('name')].maxSelected + ' элементов';
else if(input.attr('data-min-selected') && input.children('option:selected').size() < parseInt(input.attr('data-min-selected')) )
return 'Должно быть выбрано не меньше '+input.attr('data-min-selected') + ' элементов';
else if(input.attr('data-max-selected') && input.children('option:selected').size() > parseInt(input.attr('data-max-selected')) )
return 'Должно быть выбрано не больше '+input.attr('data-max-selected') + ' элементов';
else if (fieldType == 'file' && input.attr('data-file-type') && value)
{
var ext = input.val().split('.').pop(),
fileAttrInArray = input.attr('data-file-type').split(',');
if(fileAttrInArray.indexOf(ext)==-1)
return 'Выбран файл с расширением «' + ext + '» Но вы должны выбрать файл с одним из следующих расширений ' + input.attr('data-file-type');
}
return 0;
},
regexpstring2RegExp = function(str)
{
var match = str.match(new RegExp('^/(.*?)/(g?i?m?y?u?)$'));
if(!match)
return /.*/;
var params = match[2].replace(/u/, '');
return new RegExp(match[1], params);
},
packCheckboxes = function()
{
for(var checkboxname in checkboxes)
{
var result, cb = checkboxes[checkboxname];
if (cb.sendAs == 'csv') //Если необходимо собрать результат в виде csv-строки
{
result = '';
thisForm.find(":input[name='" + checkboxname + "']:checked").each(
function()
{
result += (result?',':'') + $(this).val();
$(this).removeAttr('name');
});
thisForm.find(":input[name='" + checkboxname + "']").each(function(){$(this).removeAttr('name');});
thisForm.prepend('<input type="hidden" value="' + result + '" name="' + checkboxname + '" />');
}
else if (cb.sendAs == 'sum') //Если необходимо сложить все результаты checkbox
{
//Перебираем все поля с указанным именем и складываем все значения
result = 0;
thisForm.find(":input[name='" + checkboxname + "']:checked").each(
function()
{
result |= parseInt($(this).val());
$(this).removeAttr('name');
});
thisForm.find(":input[name='" + checkboxname + "']").each(function(){$(this).removeAttr('name');});
thisForm.prepend('<input type="hidden" value="' + result + '" name="' + checkboxname + '" />');
}
}//for
},
//Соединение полей в одно значение
mergeFields = function()
{
for(var mfieldname in mergeablefields)
{
var result = '', mfield = mergeablefields[mfieldname];
for (var key in mfield.inputs)
{
thisForm.find(":input[name='" + mfield.inputs[key] + "']").each(
function()
{
result += (result?mfield.separator:'') + $(this).val();
$(this).removeAttr('name');
});
}//for (var key
//Создаем поле type="hidden"
result = result.split('"').join('"');
result = result.split('<').join('<');
result = result.split('>').join('>');
thisForm.prepend('<input type="hidden" name="' + mfieldname + '" value="' + result + '" />');
}//for(var mfield
};
//Проходимся по всем полям на форме и вешаем на них bind событий
thisForm.find(':input').each(
function()
{
thisObject.addEventListeners($(this));
});
thisForm.bind('submit', eventSubmit);
}