/*
* jQuery Form Styler v1.5.2
* https://github.com/Dimox/jQueryFormStyler
*
* Copyright 2012-2014 Dimox (http://dimox.name/)
* Released under the MIT license.
*
* Date: 2014.05.28
*
*/
(function($) {
$.fn.styler = function(opt) {
var opt = $.extend({
wrapper: 'form',
idSuffix: '-styler',
filePlaceholder: 'Файл не выбран',
fileBrowse: 'Обзор...',
selectSearch: true,
selectSearchLimit: 10,
selectSearchNotFound: 'Совпадений не найдено',
selectSearchPlaceholder: 'Поиск...',
selectVisibleOptions: 0,
singleSelectzIndex: '100',
selectSmartPositioning: true,
onSelectOpened: function() {},
onSelectClosed: function() {},
onFormStyled: function() {}
}, opt);
return this.each(function() {
var el = $(this);
function attributes() {
var id = '',
title = '',
classes = '',
dataList = '';
if (el.attr('id') !== undefined && el.attr('id') != '') id = ' id="' + el.attr('id') + opt.idSuffix + '"';
if (el.attr('title') !== undefined && el.attr('title') != '') title = ' title="' + el.attr('title') + '"';
if (el.attr('class') !== undefined && el.attr('class') != '') classes = ' ' + el.attr('class');
var data = el.data();
for (var i in data) {
if (data[i] != '') dataList += ' data-' + i + '="' + data[i] + '"';
}
id += dataList;
this.id = id;
this.title = title;
this.classes = classes;
}
// checkbox
if (el.is(':checkbox')) {
el.each(function() {
if (el.parent('div.jq-checkbox').length < 1) {
function checkbox() {
var att = new attributes();
var checkbox = $('
');
// прячем оригинальный чекбокс
el.css({
position: 'absolute',
zIndex: '-1',
opacity: 0,
margin: 0,
padding: 0
}).after(checkbox).prependTo(checkbox);
checkbox.attr('unselectable', 'on').css({
'-webkit-user-select': 'none',
'-moz-user-select': 'none',
'-ms-user-select': 'none',
'-o-user-select': 'none',
'user-select': 'none',
display: 'inline-block',
position: 'relative',
overflow: 'hidden'
});
if (el.is(':checked')) checkbox.addClass('checked');
if (el.is(':disabled')) checkbox.addClass('disabled');
// клик на псевдочекбокс
checkbox.click(function() {
if (!checkbox.is('.disabled')) {
if (el.is(':checked')) {
el.prop('checked', false);
checkbox.removeClass('checked');
} else {
el.prop('checked', true);
checkbox.addClass('checked');
}
el.change();
return false;
} else {
return false;
}
});
// клик на label
el.closest('label').add('label[for="' + el.attr('id') + '"]').click(function(e) {
checkbox.click();
e.preventDefault();
});
// переключение по Space или Enter
el.change(function() {
if (el.is(':checked')) checkbox.addClass('checked');
else checkbox.removeClass('checked');
})
// чтобы переключался чекбокс, который находится в теге label
.keydown(function(e) {
if (e.which == 32) checkbox.click();
})
.focus(function() {
if (!checkbox.is('.disabled')) checkbox.addClass('focused');
})
.blur(function() {
checkbox.removeClass('focused');
})
} // end checkbox()
checkbox();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off().parent().before(el).remove();
checkbox();
});
}
});
// end checkbox
// radio
} else if (el.is(':radio')) {
el.each(function() {
if (el.parent('div.jq-radio').length < 1) {
function radio() {
var att = new attributes();
var radio = $('
');
// прячем оригинальную радиокнопку
el.css({
position: 'absolute',
zIndex: '-1',
opacity: 0,
margin: 0,
padding: 0
}).after(radio).prependTo(radio);
radio.attr('unselectable', 'on').css({
'-webkit-user-select': 'none',
'-moz-user-select': 'none',
'-ms-user-select': 'none',
'-o-user-select': 'none',
'user-select': 'none',
display: 'inline-block',
position: 'relative'
});
if (el.is(':checked')) radio.addClass('checked');
if (el.is(':disabled')) radio.addClass('disabled');
// клик на псевдорадиокнопке
radio.click(function() {
if (!radio.is('.disabled')) {
radio.closest(opt.wrapper).find('input[name="' + el.attr('name') + '"]').prop('checked', false).parent().removeClass('checked');
el.prop('checked', true).parent().addClass('checked');
el.change();
return false;
} else {
return false;
}
});
// клик на label
el.closest('label').add('label[for="' + el.attr('id') + '"]').click(function(e) {
radio.click();
e.preventDefault();
});
// переключение стрелками
el.change(function() {
el.parent().addClass('checked');
})
.focus(function() {
if (!radio.is('.disabled')) radio.addClass('focused');
})
.blur(function() {
radio.removeClass('focused');
})
} // end radio()
radio();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off().parent().before(el).remove();
radio();
});
}
});
// end radio
// file
} else if (el.is(':file')) {
// прячем оригинальное поле
el.css({
position: 'absolute',
top: 0,
right: 0,
width: '100%',
height: '100%',
opacity: 0,
margin: 0,
padding: 0
}).each(function() {
if (el.parent('div.jq-file').length < 1) {
function file() {
var att = new attributes();
var file = $('');
var name = $('
' + opt.filePlaceholder + '
').appendTo(file);
var browse = $('
' + opt.fileBrowse + '
').appendTo(file);
el.after(file);
file.append(el);
if (el.is(':disabled')) file.addClass('disabled');
el.change(function() {
var value = el.val();
if (el.is('[multiple]')) {
value = '';
var files = el[0].files;
for (var i = 0; i < files.length; i++) {
value += ( (i > 0) ? ', ' : '' ) + files[i].name;
}
}
name.text(value.replace(/.+[\\\/]/, ''));
if (value == '') {
name.text(opt.filePlaceholder);
file.removeClass('changed');
} else {
file.addClass('changed');
}
})
.focus(function() {
file.addClass('focused');
})
.blur(function() {
file.removeClass('focused');
})
.click(function() {
file.removeClass('focused');
})
} // end file()
file();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off().parent().before(el).remove();
file();
})
}
});
// end file
// select
} else if (el.is('select')) {
el.each(function() {
if (el.parent('div.jqselect').length < 1) {
function selectbox() {
// запрещаем прокрутку страницы при прокрутке селекта
function preventScrolling(selector) {
selector.off('mousewheel DOMMouseScroll').on('mousewheel DOMMouseScroll', function(e) {
var scrollTo = null;
if (e.type == 'mousewheel') { scrollTo = (e.originalEvent.wheelDelta * -1); }
else if (e.type == 'DOMMouseScroll') { scrollTo = 40 * e.originalEvent.detail; }
if (scrollTo) {
e.stopPropagation();
e.preventDefault();
$(this).scrollTop(scrollTo + $(this).scrollTop());
}
});
}
var option = $('option', el);
var list = '';
// формируем список селекта
function makeList() {
for (i = 0, len = option.length; i < len; i++) {
var li = '',
liClass = '',
dataList = '',
optionClass = '',
optgroupClass = '',
dataJqfsClass = '';
var disabled = 'disabled';
var selDis = 'selected sel disabled';
if (option.eq(i).prop('selected')) liClass = 'selected sel';
if (option.eq(i).is(':disabled')) liClass = disabled;
if (option.eq(i).is(':selected:disabled')) liClass = selDis;
if (option.eq(i).attr('class') !== undefined) {
optionClass = ' ' + option.eq(i).attr('class');
dataJqfsClass = ' data-jqfs-class="' + option.eq(i).attr('class') + '"';
}
var data = option.eq(i).data();
for (var k in data) {
if (data[k] != '') dataList += ' data-' + k + '="' + data[k] + '"';
}
li = '
'+ option.eq(i).text() +'
';
// если есть optgroup
if (option.eq(i).parent().is('optgroup')) {
if (option.eq(i).parent().attr('class') !== undefined) optgroupClass = ' ' + option.eq(i).parent().attr('class');
li = '
'+ option.eq(i).text() +'
';
if (option.eq(i).is(':first-child')) {
li = '
' + option.eq(i).parent().attr('label') + '
' + li;
}
}
list += li;
}
} // end makeList()
// одиночный селект
function doSelect() {
var att = new attributes();
var selectbox =
$('
' +
'
' +
'' +
'
' +
'
' +
'
');
el.css({margin: 0, padding: 0}).after(selectbox).prependTo(selectbox);
var divSelect = $('div.jq-selectbox__select', selectbox);
var divText = $('div.jq-selectbox__select-text', selectbox);
var optionSelected = option.filter(':selected');
// берем опцию по умолчанию
if (optionSelected.length) {
divText.html(optionSelected.text());
} else {
divText.html(option.first().text());
}
makeList();
var searchHTML = '';
if (opt.selectSearch) searchHTML =
'' +
'
' + opt.selectSearchNotFound + '
';
var dropdown =
$('
' +
searchHTML +
'
' + list + '
' +
'
');
selectbox.append(dropdown);
var ul = $('ul', dropdown);
var li = $('li', dropdown);
var search = $('input', dropdown);
var notFound = $('div.jq-selectbox__not-found', dropdown).hide();
if (li.length < opt.selectSearchLimit) search.parent().hide();
// определяем самый широкий пункт селекта
var liWidth1 = 0,
liWidth2 = 0;
li.each(function() {
var l = $(this);
l.css({'display': 'inline-block', 'white-space': 'nowrap'});
if (l.innerWidth() > liWidth1) {
liWidth1 = l.innerWidth();
liWidth2 = l.width();
}
l.css({'display': 'block'});
});
// подстраиваем ширину псевдоселекта и выпадающего списка
// в зависимости от самого широкого пункта
var selClone = selectbox.clone().appendTo('body').width('auto');
var selCloneWidth = selClone.width();
selClone.remove();
if (selCloneWidth == selectbox.width()) {
divText.width(liWidth2);
liWidth1 += selectbox.find('div.jq-selectbox__trigger').width();
}
if ( liWidth1 > selectbox.width() ) {
dropdown.width(liWidth1);
}
// прячем оригинальный селект
el.css({
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
opacity: 0
});
var selectHeight = selectbox.outerHeight();
var searchHeight = search.outerHeight();
var isMaxHeight = ul.css('max-height');
var liSelected = li.filter('.selected');
if (liSelected.length < 1) li.first().addClass('selected sel');
if (li.data('li-height') === undefined) li.data('li-height', li.outerHeight());
var position = dropdown.css('top');
if (dropdown.css('left') == 'auto') dropdown.css({left: 0});
if (dropdown.css('top') == 'auto') dropdown.css({top: selectHeight});
dropdown.hide();
// если выбран не дефолтный пункт
if (liSelected.length) {
// добавляем класс, показывающий изменение селекта
if (option.first().text() != optionSelected.text()) {
selectbox.addClass('changed');
}
// передаем селекту класс выбранного пункта
selectbox.data('jqfs-class', liSelected.data('jqfs-class'));
selectbox.addClass(liSelected.data('jqfs-class'));
}
// если селект неактивный
if (el.is(':disabled')) {
selectbox.addClass('disabled');
return false;
}
// при клике на псевдоселекте
divSelect.click(function() {
el.focus();
// колбек при закрытии селекта
if ($('div.jq-selectbox').filter('.opened').length) {
opt.onSelectClosed.call($('div.jq-selectbox').filter('.opened'));
}
// если iOS, то не показываем выпадающий список
var iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false;
if (iOS) return;
// умное позиционирование
if (opt.selectSmartPositioning) {
var win = $(window);
var topOffset = selectbox.offset().top;
var bottomOffset = win.height() - selectHeight - (topOffset - win.scrollTop());
var visible = opt.selectVisibleOptions;
var liHeight = li.data('li-height');
var minHeight = liHeight * 5;
var newHeight = liHeight * visible;
if (visible > 0 && visible < 6) minHeight = newHeight;
if (visible == 0) newHeight = 'auto';
// раскрытие вниз
if (bottomOffset > (minHeight + searchHeight + 20)) {
dropdown.height('auto').css({bottom: 'auto', top: position});
function maxHeightBottom() {
ul.css('max-height', Math.floor((bottomOffset - 20 - searchHeight) / liHeight) * liHeight);
}
maxHeightBottom();
ul.css('max-height', newHeight);
if (isMaxHeight != 'none') {
ul.css('max-height', isMaxHeight);
}
if (bottomOffset < (dropdown.outerHeight() + 20)) {
maxHeightBottom();
}
// раскрытие вверх
} else {
dropdown.height('auto').css({top: 'auto', bottom: position});
function maxHeightTop() {
ul.css('max-height', Math.floor((topOffset - win.scrollTop() - 20 - searchHeight) / liHeight) * liHeight);
}
maxHeightTop();
ul.css('max-height', newHeight);
if (isMaxHeight != 'none') {
ul.css('max-height', isMaxHeight);
}
if ((topOffset - win.scrollTop() - 20) < (dropdown.outerHeight() + 20)) {
maxHeightTop();
}
}
}
$('div.jqselect').css({zIndex: (opt.singleSelectzIndex - 1)}).removeClass('opened');
selectbox.css({zIndex: opt.singleSelectzIndex});
if (dropdown.is(':hidden')) {
$('div.jq-selectbox__dropdown:visible').hide();
dropdown.show();
selectbox.addClass('opened focused');
// колбек при открытии селекта
opt.onSelectOpened.call(selectbox);
} else {
dropdown.hide();
selectbox.removeClass('opened');
// колбек при закрытии селекта
if ($('div.jq-selectbox').filter('.opened').length) {
opt.onSelectClosed.call(selectbox);
}
}
// прокручиваем до выбранного пункта при открытии списка
if (li.filter('.selected').length) {
// если нечетное количество видимых пунктов,
// то высоту пункта делим пополам для последующего расчета
if ( (ul.innerHeight() / liHeight) % 2 != 0 ) liHeight = liHeight / 2;
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - ul.innerHeight() / 2 + liHeight);
}
// поисковое поле
if (search.length) {
search.val('').keyup();
notFound.hide();
search.focus().keyup(function() {
var query = $(this).val();
li.each(function() {
if (!$(this).html().match(new RegExp('.*?' + query + '.*?', 'i'))) {
$(this).hide();
} else {
$(this).show();
}
});
if (li.filter(':visible').length < 1) {
notFound.show();
} else {
notFound.hide();
}
});
}
preventScrolling(ul);
return false;
});
// при наведении курсора на пункт списка
li.hover(function() {
$(this).siblings().removeClass('selected');
});
var selectedText = li.filter('.selected').text();
var selText = li.filter('.selected').text();
// при клике на пункт списка
li.filter(':not(.disabled):not(.optgroup)').click(function() {
var t = $(this);
var liText = t.text();
if (selectedText != liText) {
var index = t.index();
index -= t.prevAll('.optgroup').length;
t.addClass('selected sel').siblings().removeClass('selected sel');
option.prop('selected', false).eq(index).prop('selected', true);
selectedText = liText;
divText.html(liText);
// передаем селекту класс выбранного пункта
if (selectbox.data('jqfs-class')) selectbox.removeClass(selectbox.data('jqfs-class'));
selectbox.data('jqfs-class', t.data('jqfs-class'));
selectbox.addClass(t.data('jqfs-class'));
el.change();
}
if (search.length) {
search.val('').keyup();
notFound.hide();
}
dropdown.hide();
selectbox.removeClass('opened');
// колбек при закрытии селекта
opt.onSelectClosed.call(selectbox);
});
dropdown.mouseout(function() {
$('li.sel', dropdown).addClass('selected');
});
// изменение селекта
el.change(function() {
divText.html(option.filter(':selected').text());
li.removeClass('selected sel').not('.optgroup').eq(el[0].selectedIndex).addClass('selected sel');
// добавляем класс, показывающий изменение селекта
if (option.first().text() != li.filter('.selected').text()) {
selectbox.addClass('changed');
} else {
selectbox.removeClass('changed');
}
})
.focus(function() {
selectbox.addClass('focused');
$('div.jqselect').removeClass('opened');
})
.blur(function() {
selectbox.removeClass('focused');
})
// изменение селекта с клавиатуры
.on('keydown keyup', function(e) {
divText.html(option.filter(':selected').text());
li.removeClass('selected sel').not('.optgroup').eq(el[0].selectedIndex).addClass('selected sel');
// вверх, влево, PageUp
if (e.which == 38 || e.which == 37 || e.which == 33) {
dropdown.scrollTop(dropdown.scrollTop() + li.filter('.selected').position().top);
}
// вниз, вправо, PageDown
if (e.which == 40 || e.which == 39 || e.which == 34) {
dropdown.scrollTop(dropdown.scrollTop() + li.filter('.selected').position().top - dropdown.innerHeight() + liHeight);
}
// открываем выпадающий список при нажатии Space
if (e.which == 32) {
e.preventDefault();
// можно было бы открывать через запуск divSelect.click(),
// но почему-то список после открытия сразу закрывается
// решение пока не найдено
// divSelect.click();
}
// закрываем выпадающий список при нажатии Enter
if (e.which == 13) {
e.preventDefault();
dropdown.hide();
}
});
// прячем выпадающий список при клике за пределами селекта
$(document).on('click', function(e) {
// e.target.nodeName != 'OPTION' - добавлено для обхода бага в Opera на движке Presto
// (при изменении селекта с клавиатуры срабатывает событие onclick)
if (!$(e.target).parents().hasClass('jq-selectbox') && e.target.nodeName != 'OPTION') {
// колбек при закрытии селекта
if ($('div.jq-selectbox').filter('.opened').length) {
opt.onSelectClosed.call($('div.jq-selectbox').filter('.opened'));
}
if (search.length) search.val('').keyup();
dropdown.hide().find('li.sel').addClass('selected');
selectbox.removeClass('focused opened');
}
});
} // end doSelect()
// мультиселект
function doMultipleSelect() {
var att = new attributes();
var selectbox = $('');
el.css({margin: 0, padding: 0}).after(selectbox);
makeList();
selectbox.append('
' + list + '
');
var ul = $('ul', selectbox).css({
'position': 'relative',
'overflow-x': 'hidden',
'-webkit-overflow-scrolling': 'touch'
});
var li = $('li', selectbox).attr('unselectable', 'on').css({'-webkit-user-select': 'none', '-moz-user-select': 'none', '-ms-user-select': 'none', '-o-user-select': 'none', 'user-select': 'none', 'white-space': 'nowrap'});
var size = el.attr('size');
var ulHeight = ul.outerHeight();
var liHeight = li.outerHeight();
if (size !== undefined && size > 0) {
ul.css({'height': liHeight * size});
} else {
ul.css({'height': liHeight * 4});
}
if (ulHeight > selectbox.height()) {
ul.css('overflowY', 'scroll');
preventScrolling(ul);
// прокручиваем до выбранного пункта
if (li.filter('.selected').length) {
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top);
}
}
// прячем оригинальный селект
el.prependTo(selectbox).css({
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
opacity: 0
});
// если селект неактивный
if (el.is(':disabled')) {
selectbox.addClass('disabled');
option.each(function() {
if ($(this).is(':selected')) li.eq($(this).index()).addClass('selected');
});
// если селект активный
} else {
// при клике на пункт списка
li.filter(':not(.disabled):not(.optgroup)').click(function(e) {
el.focus();
selectbox.removeClass('focused');
var clkd = $(this);
if(!e.ctrlKey && !e.metaKey) clkd.addClass('selected');
if(!e.shiftKey) clkd.addClass('first');
if(!e.ctrlKey && !e.metaKey && !e.shiftKey) clkd.siblings().removeClass('selected first');
// выделение пунктов при зажатом Ctrl
if(e.ctrlKey || e.metaKey) {
if (clkd.is('.selected')) clkd.removeClass('selected first');
else clkd.addClass('selected first');
clkd.siblings().removeClass('first');
}
// выделение пунктов при зажатом Shift
if(e.shiftKey) {
var prev = false,
next = false;
clkd.siblings().removeClass('selected').siblings('.first').addClass('selected');
clkd.prevAll().each(function() {
if ($(this).is('.first')) prev = true;
});
clkd.nextAll().each(function() {
if ($(this).is('.first')) next = true;
});
if (prev) {
clkd.prevAll().each(function() {
if ($(this).is('.selected')) return false;
else $(this).not('.disabled, .optgroup').addClass('selected');
});
}
if (next) {
clkd.nextAll().each(function() {
if ($(this).is('.selected')) return false;
else $(this).not('.disabled, .optgroup').addClass('selected');
});
}
if (li.filter('.selected').length == 1) clkd.addClass('first');
}
// отмечаем выбранные мышью
option.prop('selected', false);
li.filter('.selected').each(function() {
var t = $(this);
var index = t.index();
if (t.is('.option')) index -= t.prevAll('.optgroup').length;
option.eq(index).prop('selected', true);
});
el.change();
});
// отмечаем выбранные с клавиатуры
option.each(function(i) {
$(this).data('optionIndex', i);
});
el.change(function() {
li.removeClass('selected');
var arrIndexes = [];
option.filter(':selected').each(function() {
arrIndexes.push($(this).data('optionIndex'));
});
li.not('.optgroup').filter(function(i) {
return $.inArray(i, arrIndexes) > -1;
}).addClass('selected');
})
.focus(function() {
selectbox.addClass('focused');
})
.blur(function() {
selectbox.removeClass('focused');
});
// прокручиваем с клавиатуры
if (ulHeight > selectbox.height()) {
el.keydown(function(e) {
// вверх, влево, PageUp
if (e.which == 38 || e.which == 37 || e.which == 33) {
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - liHeight);
}
// вниз, вправо, PageDown
if (e.which == 40 || e.which == 39 || e.which == 34) {
ul.scrollTop(ul.scrollTop() + li.filter('.selected:last').position().top - ul.innerHeight() + liHeight * 2);
}
});
}
}
} // end doMultipleSelect()
if (el.is('[multiple]')) doMultipleSelect(); else doSelect();
} // end selectbox()
selectbox();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off().parent().before(el).remove();
selectbox();
});
}
});
// end select
// reset
} else if (el.is(':reset')) {
el.click(function() {
setTimeout(function() {
el.closest(opt.wrapper).find('input, select').trigger('refresh');
}, 1)
});
}
// end reset
})
// колбек после выполнения плагина
.promise()
.done(function() {
opt.onFormStyled.call();
});
}
})(jQuery);