Joywork/js/formstyler.js
2026-05-22 21:21:54 +03:00

2295 lines
41 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* jQuery Form Styler v1.7.6
* https://github.com/Dimox/jQueryFormStyler
*
* Copyright 2012-2016 Dimox (http://dimox.name/)
* Released under the MIT license.
*
* Date: 2016.06.05
*
*/
;(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
}(function($) {
'use strict';
var pluginName = 'styler',
defaults = {
idSuffix: '-styler',
filePlaceholder: 'Файл не выбран',
fileBrowse: 'Обзор...',
fileNumber: 'Выбрано файлов: %s',
selectPlaceholder: 'Выберите...',
selectSearch: false,
selectSearchLimit: 10,
selectSearchNotFound: 'Совпадений не найдено',
selectSearchPlaceholder: 'Поиск...',
selectVisibleOptions: 0,
singleSelectzIndex: '100',
selectSmartPositioning: true,
onSelectOpened: function() {},
onSelectClosed: function() {},
onFormStyled: function() {}
};
function Plugin(element, options) {
this.element = element;
this.options = $.extend({}, defaults, options);
this.init();
}
Plugin.prototype = {
// инициализация
init: function() {
var el = $(this.element);
var opt = this.options;
var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/i) && !navigator.userAgent.match(/(Windows\sPhone)/i)) ? true : false;
var Android = (navigator.userAgent.match(/Android/i) && !navigator.userAgent.match(/(Windows\sPhone)/i)) ? true : false;
function Attributes() {
if (el.attr('id') !== undefined && el.attr('id') !== '') {
this.id = el.attr('id') + opt.idSuffix;
}
this.title = el.attr('title');
this.classes = el.attr('class');
this.data = el.data();
}
// checkbox
if (el.is(':checkbox')) {
var checkboxOutput = function() {
var att = new Attributes();
var checkbox = $('<div class="jq-checkbox"><div class="jq-checkbox__div"></div></div>')
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
// прячем оригинальный чекбокс
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(e) {
e.preventDefault();
if (!checkbox.is('.disabled')) {
if (el.is(':checked')) {
el.prop('checked', false);
checkbox.removeClass('checked');
} else {
el.prop('checked', true);
checkbox.addClass('checked');
}
el.focus().change();
}
});
// клик на label
el.closest('label').add('label[for="' + el.attr('id') + '"]').on('click.styler', function(e) {
if (!$(e.target).is('a') && !$(e.target).closest(checkbox).length) {
checkbox.triggerHandler('click');
e.preventDefault();
}
});
// переключение по Space или Enter
el.on('change.styler', function() {
if (el.is(':checked')) checkbox.addClass('checked');
else checkbox.removeClass('checked');
})
// чтобы переключался чекбокс, который находится в теге label
.on('keydown.styler', function(e) {
if (e.which == 32) checkbox.click();
})
.on('focus.styler', function() {
if (!checkbox.is('.disabled')) checkbox.addClass('focused');
})
.on('blur.styler', function() {
checkbox.removeClass('focused');
});
}; // end checkboxOutput()
checkboxOutput();
// обновление при динамическом изменении
el.on('refresh', function() {
el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
el.off('.styler').parent().before(el).remove();
checkboxOutput();
});
// end checkbox
// radio
} else if (el.is(':radio')) {
var radioOutput = function() {
var att = new Attributes();
var radio = $('<div class="jq-radio"><div class="jq-radio__div"></div></div>')
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
// прячем оригинальную радиокнопку
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');
// определяем общего родителя у радиокнопок с одинаковым name
// http://stackoverflow.com/a/27733847
$.fn.commonParents = function (){
var cachedThis = this;
return cachedThis.first().parents().filter(function () {
return $(this).find(cachedThis).length === cachedThis.length;
});
};
$.fn.commonParent = function (){
return $(this).commonParents().first();
};
// клик на псевдорадиокнопке
radio.click(function(e) {
e.preventDefault();
if (!radio.is('.disabled')) {
var inputName = $('input[name="' + el.attr('name') + '"]');
inputName.commonParent().find(inputName).prop('checked', false).parent().removeClass('checked');
el.prop('checked', true).parent().addClass('checked');
el.focus().change();
}
});
// клик на label
el.closest('label').add('label[for="' + el.attr('id') + '"]').on('click.styler', function(e) {
if (!$(e.target).is('a') && !$(e.target).closest(radio).length) {
radio.triggerHandler('click');
e.preventDefault();
}
});
// переключение стрелками
el.on('change.styler', function() {
el.parent().addClass('checked');
})
.on('focus.styler', function() {
if (!radio.is('.disabled')) radio.addClass('focused');
})
.on('blur.styler', function() {
radio.removeClass('focused');
});
}; // end radioOutput()
radioOutput();
// обновление при динамическом изменении
el.on('refresh', function() {
el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
el.off('.styler').parent().before(el).remove();
radioOutput();
});
// end radio
// file
} else if (el.is(':file')) {
// прячем оригинальное поле
el.css({
position: 'absolute',
top: 0,
right: 0,
margin: 0,
padding: 0,
opacity: 0,
fontSize: '100px'
});
var fileOutput = function() {
var att = new Attributes();
var placeholder = el.data('placeholder');
if (placeholder === undefined) placeholder = opt.filePlaceholder;
var browse = el.data('browse');
if (browse === undefined || browse === '') browse = opt.fileBrowse;
var file =
$('<div class="jq-file">' +
'<div class="jq-file__name">' + placeholder + '</div>' +
'<div class="jq-file__browse">' + browse + '</div>' +
'</div>')
.css({
display: 'inline-block',
position: 'relative',
overflow: 'hidden'
})
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
el.after(file).appendTo(file);
if (el.is(':disabled')) file.addClass('disabled');
el.on('change.styler', function() {
var value = el.val();
var name = $('div.jq-file__name', file);
if (el.is('[multiple]')) {
value = '';
var files = el[0].files.length;
if (files > 0) {
var number = el.data('number');
if (number === undefined) number = opt.fileNumber;
number = number.replace('%s', files);
value = number;
}
}
name.text(value.replace(/.+[\\\/]/, ''));
if (value === '') {
name.text(placeholder);
file.removeClass('changed');
} else {
file.addClass('changed');
}
})
.on('focus.styler', function() {
file.addClass('focused');
})
.on('blur.styler', function() {
file.removeClass('focused');
})
.on('click.styler', function() {
file.removeClass('focused');
});
}; // end fileOutput()
fileOutput();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off('.styler').parent().before(el).remove();
fileOutput();
});
// end file
} else if (el.is('input[type="number"]')) {
var numberOutput = function() {
var att = new Attributes();
var number =
$('<div class="jq-number">' +
'<div class="jq-number__spin minus ti-angle-down"></div>' +
'<div class="jq-number__spin plus ti-angle-up"></div>' +
'</div>')
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
el.after(number).prependTo(number).wrap('<div class="jq-number__field"></div>');
if (el.is(':disabled')) number.addClass('disabled');
var min,
max,
step,
timeout = null,
interval = null;
if (el.attr('min') !== undefined) min = el.attr('min');
if (el.attr('max') !== undefined) max = el.attr('max');
if (el.attr('step') !== undefined && $.isNumeric(el.attr('step')))
step = Number(el.attr('step'));
else
step = Number(1);
var changeValue = function(spin) {
var value = el.val(),
newValue;
if (!$.isNumeric(value)) {
value = 0;
el.val('0');
}
if (spin.is('.minus')) {
newValue = Number(value) - step;
} else if (spin.is('.plus')) {
newValue = Number(value) + step;
}
// определяем количество десятичных знаков после запятой в step
var decimals = (step.toString().split('.')[1] || []).length;
if (decimals > 0) {
var multiplier = '1';
while (multiplier.length <= decimals) multiplier = multiplier + '0';
// избегаем появления лишних знаков после запятой
newValue = Math.round(newValue * multiplier) / multiplier;
}
if ($.isNumeric(min) && $.isNumeric(max)) {
if (newValue >= min && newValue <= max) el.val(newValue);
} else if ($.isNumeric(min) && !$.isNumeric(max)) {
if (newValue >= min) el.val(newValue);
} else if (!$.isNumeric(min) && $.isNumeric(max)) {
if (newValue <= max) el.val(newValue);
} else {
el.val(newValue);
}
};
if (!number.is('.disabled')) {
number.on('mousedown', 'div.jq-number__spin', function() {
var spin = $(this);
changeValue(spin);
timeout = setTimeout(function(){
interval = setInterval(function(){ changeValue(spin); }, 40);
}, 350);
}).on('mouseup mouseout', 'div.jq-number__spin', function() {
clearTimeout(timeout);
clearInterval(interval);
}).on('mouseup', 'div.jq-number__spin', function() {
el.change();
});
el.on('focus.styler', function() {
number.addClass('focused');
})
.on('blur.styler', function() {
number.removeClass('focused');
});
}
}; // end numberOutput()
numberOutput();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off('.styler').closest('.jq-number').before(el).remove();
numberOutput();
});
// end number
// select
} else if (el.is('select')) {
var selectboxOutput = function() {
// запрещаем прокрутку страницы при прокрутке селекта
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 (var i = 0; i < option.length; i++) {
var op = option.eq(i);
var li = '',
liClass = '',
liClasses = '',
id = '',
title = '',
dataList = '',
optionClass = '',
optgroupClass = '',
dataJqfsClass = '';
var disabled = 'disabled';
var selDis = 'selected sel disabled';
if (op.prop('selected')) liClass = 'selected sel';
if (op.is(':disabled')) liClass = disabled;
if (op.is(':selected:disabled')) liClass = selDis;
if (op.attr('id') !== undefined && op.attr('id') !== '') id = ' id="' + op.attr('id') + opt.idSuffix + '"';
if (op.attr('title') !== undefined && option.attr('title') !== '') title = ' title="' + op.attr('title') + '"';
if (op.attr('class') !== undefined) {
optionClass = ' ' + op.attr('class');
dataJqfsClass = ' data-jqfs-class="' + op.attr('class') + '"';
}
var data = op.data();
for (var k in data) {
if (data[k] !== '') dataList += ' data-' + k + '="' + data[k] + '"';
}
if ( (liClass + optionClass) !== '' ) liClasses = ' class="' + liClass + optionClass + '"';
li = '<li' + dataJqfsClass + dataList + liClasses + title + id + '>'+ op.html() +'</li>';
// если есть optgroup
if (op.parent().is('optgroup')) {
if (op.parent().attr('class') !== undefined) optgroupClass = ' ' + op.parent().attr('class');
li = '<li' + dataJqfsClass + dataList + ' class="' + liClass + optionClass + ' option' + optgroupClass + '"' + title + id + '>'+ op.html() +'</li>';
if (op.is(':first-child')) {
li = '<li class="optgroup' + optgroupClass + '">' + op.parent().attr('label') + '</li>' + li;
}
}
list += li;
}
} // end makeList()
// одиночный селект
function doSelect() {
var att = new Attributes();
var searchHTML = '';
var selectPlaceholder = el.data('placeholder');
var selectSearch = el.data('search');
var selectSearchLimit = el.data('search-limit');
var selectSearchNotFound = el.data('search-not-found');
var selectSearchPlaceholder = el.data('search-placeholder');
var singleSelectzIndex = el.data('z-index');
var selectSmartPositioning = el.data('smart-positioning');
if (selectPlaceholder === undefined) selectPlaceholder = opt.selectPlaceholder;
if (selectSearch === undefined || selectSearch === '') selectSearch = opt.selectSearch;
if (selectSearchLimit === undefined || selectSearchLimit === '') selectSearchLimit = opt.selectSearchLimit;
if (selectSearchNotFound === undefined || selectSearchNotFound === '') selectSearchNotFound = opt.selectSearchNotFound;
if (selectSearchPlaceholder === undefined) selectSearchPlaceholder = opt.selectSearchPlaceholder;
if (singleSelectzIndex === undefined || singleSelectzIndex === '') singleSelectzIndex = opt.singleSelectzIndex;
if (selectSmartPositioning === undefined || selectSmartPositioning === '') selectSmartPositioning = opt.selectSmartPositioning;
var selectbox =
$('<div class="jq-selectbox jqselect">' +
'<div class="jq-selectbox__select" style="position: relative">' +
'<div class="jq-selectbox__select-text"></div>' +
'<div class="jq-selectbox__trigger">' +
'<div class="jq-selectbox__trigger-arrow"><i class="ti-angle-down"></i></div></div>' +
'</div>' +
'</div>')
.css({
display: 'inline-block',
position: 'relative',
zIndex: singleSelectzIndex
})
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
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');
makeList();
if (selectSearch) searchHTML =
'<div class="jq-selectbox__search"><input type="search" autocomplete="off" placeholder="' + selectSearchPlaceholder + '"></div>' +
'<div class="jq-selectbox__not-found">' + selectSearchNotFound + '</div>';
var dropdown =
$('<div class="jq-selectbox__dropdown" style="position: absolute">' +
searchHTML +
'<ul style="position: relative; list-style: none; overflow: auto; overflow-x: hidden">' + list + '</ul>' +
'</div>');
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 < selectSearchLimit) search.parent().hide();
// показываем опцию по умолчанию
// если у 1-й опции нет текста и она выбрана по умолчанию, то показываем плейсхолдер
if (option.first().text() === '' && option.first().is(':selected')) {
divText.text(selectPlaceholder).addClass('placeholder');
} else {
divText.text(optionSelected.text());
}
// определяем самый широкий пункт селекта
var liWidthInner = 0,
liWidth = 0;
li.css({'display': 'inline-block'});
li.each(function() {
var l = $(this);
if (l.innerWidth() > liWidthInner) {
liWidthInner = l.innerWidth();
liWidth = l.width();
}
});
li.css({'display': ''});
// подстраиваем ширину свернутого селекта в зависимости
// от ширины плейсхолдера или самого широкого пункта
if (divText.is('.placeholder') && (divText.width() > liWidthInner)) {
divText.width(divText.width());
} else {
var selClone = selectbox.clone().appendTo('body').width('auto');
var selCloneWidth = selClone.outerWidth();
selClone.remove();
if (selCloneWidth == selectbox.outerWidth()) {
divText.width(liWidth);
}
}
// подстраиваем ширину выпадающего списка в зависимости от самого широкого пункта
if (liWidthInner > selectbox.width()) dropdown.width(liWidthInner);
// прячем 1-ю пустую опцию, если она есть и если атрибут data-placeholder не пустой
// если все же нужно, чтобы первая пустая опция отображалась, то указываем у селекта: data-placeholder=""
if (option.first().text() === '' && el.data('placeholder') !== '') {
li.first().hide();
}
// прячем оригинальный селект
el.css({
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
opacity: 0
});
var selectHeight = selectbox.outerHeight(true);
var searchHeight = search.parent().outerHeight(true);
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});
position = 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() {
// колбек при закрытии селекта
if ($('div.jq-selectbox').filter('.opened').length) {
opt.onSelectClosed.call($('div.jq-selectbox').filter('.opened'));
}
el.focus();
// если iOS, то не показываем выпадающий список,
// т.к. отображается нативный и неизвестно, как его спрятать
if (iOS) return;
// умное позиционирование
var win = $(window);
var liHeight = li.data('li-height');
var topOffset = selectbox.offset().top;
var bottomOffset = win.height() - selectHeight - (topOffset - win.scrollTop());
var visible = el.data('visible-options');
if (visible === undefined || visible === '') visible = opt.selectVisibleOptions;
var minHeight = liHeight * 5;
var newHeight = liHeight * visible;
if (visible > 0 && visible < 6) minHeight = newHeight;
if (visible === 0) newHeight = 'auto';
var dropDown = function() {
dropdown.height('auto').css({bottom: 'auto', top: position});
var maxHeightBottom = function() {
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();
}
};
var dropUp = function() {
dropdown.height('auto').css({top: 'auto', bottom: position});
var maxHeightTop = function() {
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();
}
};
if (selectSmartPositioning === true || selectSmartPositioning === 1) {
// раскрытие вниз
if (bottomOffset > (minHeight + searchHeight + 20)) {
dropDown();
selectbox.removeClass('dropup').addClass('dropdown');
// раскрытие вверх
} else {
dropUp();
selectbox.removeClass('dropdown').addClass('dropup');
}
} else if (selectSmartPositioning === false || selectSmartPositioning === 0) {
// раскрытие вниз
if (bottomOffset > (minHeight + searchHeight + 20)) {
dropDown();
selectbox.removeClass('dropup').addClass('dropdown');
}
}
// если выпадающий список выходит за правый край окна браузера,
// то меняем позиционирование с левого на правое
if (selectbox.offset().left + dropdown.outerWidth() > win.width()) {
dropdown.css({left: 'auto', right: 0});
}
// конец умного позиционирования
$('div.jqselect').css({zIndex: (singleSelectzIndex - 1)}).removeClass('opened');
selectbox.css({zIndex: 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 dropup dropdown');
// колбек при закрытии селекта
if ($('div.jq-selectbox').filter('.opened').length) {
opt.onSelectClosed.call(selectbox);
}
}
// поисковое поле
if (search.length) {
search.val('').keyup();
notFound.hide();
search.keyup(function() {
var query = $(this).val();
li.each(function() {
if (!$(this).html().match(new RegExp('.*?' + query + '.*?', 'i'))) {
$(this).hide();
} else {
$(this).show();
}
});
// прячем 1-ю пустую опцию
if (option.first().text() === '' && el.data('placeholder') !== '') {
li.first().hide();
}
if (li.filter(':visible').length < 1) {
notFound.show();
} else {
notFound.hide();
}
});
}
// прокручиваем до выбранного пункта при открытии списка
if (li.filter('.selected').length) {
if (el.val() === '') {
ul.scrollTop(0);
} else {
// если нечетное количество видимых пунктов,
// то высоту пункта делим пополам для последующего расчета
if ( (ul.innerHeight() / liHeight) % 2 !== 0 ) liHeight = liHeight / 2;
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - ul.innerHeight() / 2 + liHeight);
}
}
preventScrolling(ul);
}); // end divSelect.click()
// при наведении курсора на пункт списка
li.hover(function() {
$(this).siblings().removeClass('selected');
});
var selectedText = li.filter('.selected').text();
// при клике на пункт списка
li.filter(':not(.disabled):not(.optgroup)').click(function() {
el.focus();
var t = $(this);
var liText = t.text();
if (!t.is('.selected')) {
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.text(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();
}
dropdown.hide();
selectbox.removeClass('opened dropup dropdown');
// колбек при закрытии селекта
opt.onSelectClosed.call(selectbox);
});
dropdown.mouseout(function() {
$('li.sel', dropdown).addClass('selected');
});
// изменение селекта
el.on('change.styler', function() {
divText.text(option.filter(':selected').text()).removeClass('placeholder');
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');
}
})
.on('focus.styler', function() {
selectbox.addClass('focused');
$('div.jqselect').not('.focused').removeClass('opened dropup dropdown').find('div.jq-selectbox__dropdown').hide();
})
.on('blur.styler', function() {
selectbox.removeClass('focused');
})
// изменение селекта с клавиатуры
.on('keydown.styler keyup.styler', function(e) {
var liHeight = li.data('li-height');
if (el.val() === '') {
divText.text(selectPlaceholder).addClass('placeholder');
} else {
divText.text(option.filter(':selected').text());
}
li.removeClass('selected sel').not('.optgroup').eq(el[0].selectedIndex).addClass('selected sel');
// вверх, влево, Page Up, Home
if (e.which == 38 || e.which == 37 || e.which == 33 || e.which == 36) {
if (el.val() === '') {
ul.scrollTop(0);
} else {
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top);
}
}
// вниз, вправо, Page Down, End
if (e.which == 40 || e.which == 39 || e.which == 34 || e.which == 35) {
ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - ul.innerHeight() + liHeight);
}
// закрываем выпадающий список при нажатии Enter
if (e.which == 13) {
e.preventDefault();
dropdown.hide();
selectbox.removeClass('opened dropup dropdown');
// колбек при закрытии селекта
opt.onSelectClosed.call(selectbox);
}
}).on('keydown.styler', function(e) {
// открываем выпадающий список при нажатии Space
if (e.which == 32) {
e.preventDefault();
divSelect.click();
}
});
// прячем выпадающий список при клике за пределами селекта
if (!onDocumentClick.registered) {
$(document).on('click', onDocumentClick);
onDocumentClick.registered = true;
}
} // end doSelect()
// мультиселект
function doMultipleSelect() {
var att = new Attributes();
var selectbox =
$('<div class="jq-select-multiple jqselect"></div>')
.css({
display: 'inline-block',
position: 'relative'
})
.attr({
id: att.id,
title: att.title
})
.addClass(att.classes)
.data(att.data)
;
el.css({margin: 0, padding: 0}).after(selectbox);
makeList();
selectbox.append('<ul>' + list + '</ul>');
var ul = $('ul', selectbox).css({
'position': 'relative',
'overflow-x': 'hidden',
'-webkit-overflow-scrolling': 'touch'
});
var li = $('li', selectbox).attr('unselectable', 'on');
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();
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.on('change.styler', 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');
})
.on('focus.styler', function() {
selectbox.addClass('focused');
})
.on('blur.styler', function() {
selectbox.removeClass('focused');
});
// прокручиваем с клавиатуры
if (ulHeight > selectbox.height()) {
el.on('keydown.styler', 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]')) {
// если Android или iOS, то мультиселект не стилизуем
// причина для Android - в стилизованном селекте нет возможности выбрать несколько пунктов
// причина для iOS - в стилизованном селекте неправильно отображаются выбранные пункты
if (Android || iOS) return;
doMultipleSelect();
} else {
doSelect();
}
}; // end selectboxOutput()
selectboxOutput();
// обновление при динамическом изменении
el.on('refresh', function() {
el.off('.styler').parent().before(el).remove();
selectboxOutput();
});
// end select
// reset
} else if (el.is(':reset')) {
el.on('click', function() {
setTimeout(function() {
el.closest('form').find('input, select').trigger('refresh');
}, 1);
});
} // end reset
}, // init: function()
// деструктор
destroy: function() {
var el = $(this.element);
if (el.is(':checkbox') || el.is(':radio')) {
el.removeData('_' + pluginName).off('.styler refresh').removeAttr('style').parent().before(el).remove();
el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
} else if (el.is('input[type="number"]')) {
el.removeData('_' + pluginName).off('.styler refresh').closest('.jq-number').before(el).remove();
} else if (el.is(':file') || el.is('select')) {
el.removeData('_' + pluginName).off('.styler refresh').removeAttr('style').parent().before(el).remove();
}
} // destroy: function()
}; // Plugin.prototype
$.fn[pluginName] = function(options) {
var args = arguments;
if (options === undefined || typeof options === 'object') {
this.each(function() {
if (!$.data(this, '_' + pluginName)) {
$.data(this, '_' + pluginName, new Plugin(this, options));
}
})
// колбек после выполнения плагина
.promise()
.done(function() {
var opt = $(this[0]).data('_' + pluginName);
if (opt) opt.options.onFormStyled.call();
});
return this;
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
var returns;
this.each(function() {
var instance = $.data(this, '_' + pluginName);
if (instance instanceof Plugin && typeof instance[options] === 'function') {
returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
return returns !== undefined ? returns : this;
}
};
// прячем выпадающий список при клике за пределами селекта
function onDocumentClick(e) {
// e.target.nodeName != 'OPTION' - добавлено для обхода бага в Opera на движке Presto
// (при изменении селекта с клавиатуры срабатывает событие onclick)
if (!$(e.target).parents().hasClass('jq-selectbox') && e.target.nodeName != 'OPTION') {
if ($('div.jq-selectbox.opened').length) {
var selectbox = $('div.jq-selectbox.opened'),
search = $('div.jq-selectbox__search input', selectbox),
dropdown = $('div.jq-selectbox__dropdown', selectbox),
opt = selectbox.find('select').data('_' + pluginName).options;
// колбек при закрытии селекта
opt.onSelectClosed.call(selectbox);
if (search.length) search.val('').keyup();
dropdown.hide().find('li.sel').addClass('selected');
selectbox.removeClass('focused opened dropup dropdown');
}
}
}
onDocumentClick.registered = false;
}));