(function($) { "use strict"; /** * @author Eugene Gantz (EG) * @alias MSelectDBox * @constructor * @param {Object} arg * @param {String=} arg.name - instance name * @param {Array} arg.list - list of options * @param {Boolean=} arg.autoComplete - on|off autocompletion * @param {Boolean=} arg.multiple - on|off multiselection * @param {Number=} arg.zIndex - z-index of list container * @param {String | Number=} arg.width - width of list container (10px | auto) * @param {Array=} arg.optionFilters - autocomplete filter's functions * @param {Boolean} arg.freeWrite * @param {String} arg.language - language of instance * @param {Boolean} arg.embeddedInput - show text input in list container * @param {Boolean=true} arg.openOnFocus. true by default **/ var MSelectDBox = function(arg) { this.init(arg); }; MSelectDBox.prototype = { "instances": [], /** * Индикатор холодной загрузки. * После первой загрузки класса становится true * */ "_coldInit": 0, /** * Get instance property * @param {String} key - key * @param {Object=} arg - optional arguments (deprecated), * @param {Boolean=} e - event trigger on|off. If "false" then "get" won't trigger event * @return {*} * */ "get": function(key, arg, e) { if (typeof key != "string") return; key = key.toLowerCase(); if (e || e === void 0) { this.trigger("get"); this.trigger("get:" + key); } return this._props[key]; }, /** * Set instance property * @param {String | Object} key - key or hash of key-value * @param {*=} value * @param {Object=} arg - optional arguments (deprecated), * @param {Boolean=} e - event trigger on|off. If "false" then "set" won't trigger event * @return {MSelectDBox} * */ "set": function(key, value, arg, e) { if (!key) return this; // ...... множественное присваивание ...... if ( typeof key == "object" ) { var return_ = Object.create(null); for (var prop in key) { if (!Object.prototype.hasOwnProperty.call(key, prop)) continue; return_[prop] = this.set(prop, key[prop], null, e); } return this; } // ........................................................... if (typeof key != "string") return this; key = key.toLowerCase(); var triggerArg = { value: value }; if (typeof e == "undefined") e = true; if (e) { this.trigger("set", triggerArg); this.trigger("set:" + key, triggerArg); this.trigger("beforeSet:" + key, triggerArg); } this._props[key] = value; e && this.trigger("afterSet:" + key, triggerArg); return this; }, /** * @description Return instance of class * @memberof MSelectDBox * @return {Array} * */ "getInstances": function(arg) { // TODO this.fx.filter if (!arguments.length) return this.instances; if (typeof arg != "object") arg = Object.create(null); var c, tmp = [], name = $.inArray(typeof arg.name, ["string","number"]) > -1 ? arg.name : null; // TODO name !=== null - это что за херня? for (c = 0; c < this.instances.length; c++) { if ( name !== null && this.instances[c].get("name", void 0, false) != name ) { continue; } tmp.push(this.instances[c]); } return tmp; }, /** * @description Remove instances * @param {Object} arg - arguments * @param {String} arg.name - Instance name // msdb.get("name") * @memberof MSelectDBox * */ "removeInstances": function(arg) { // TODO this.fx.filter if (typeof arg != "object") return this; var name = typeof arg.name != "string" ? null : arg.name, c, tmp = []; for (c=0; c -1 ) { // left, right, tab, alt, ctrl, shift, caps } else if ( e.keyCode == 13 ) { // 13 = Enter if ( !self.isActive() ) { self.open(); self._eventOpen.call(self, null, e); } else { self.close(); } } else if ( 27 == e.keyCode ) { // esc - закрыть self.close(); } else if ( $.inArray(e.keyCode, [38,39,40]) > -1 ) { // other keys if ( !self.isActive() ) return; if (!selected) { // ничего не выбрано - сделать шаг вверх начиная с первого selected = self.selectPrevVisibleItem(self.getFirstVisibleItem({ exceptDisabled: true })); } else if ( e.keyCode == 38 ) { // up selected = self.selectPrevVisibleItem(selected); } else if ( e.keyCode == 40 ) { // down selected = self.selectNextVisibleItem(selected); } else { return; } self.calcScrollBarPosition(); e.listItem = selected; self.trigger("select", e); } }, "_eventDefaultKeyDownMultipleTrue": function(e) { var selectedIds, self = this, hovered = self.getHoveredItems()[0]; if ( self.get("multiple") ) { if ( $.inArray(e.keyCode, [37,39,9,18,17,16,20]) > -1 ) { // left, right, tab, alt, ctrl, shift, caps, esc } else if (e.keyCode == 13) { // Enter if ( !self.isActive() ) self.open(); if (!hovered) return; selectedIds = self.getSelectedKeys(); hovered.selected ? self._deselectByID(hovered.id) : self._selectByID(hovered.id); selectedIds = selectedIds.concat(self.getSelectedKeys()); self.applySelectedToInput(); self.applySelectedToList(self._getItemsByID(selectedIds)); e.listItem = hovered; self.trigger("select", e); return; } else if ( 27 == e.keyCode ) { // esc - закрыть self.close(); } else if (e.keyCode == 38 || e.keyCode == 40) { if (hovered) { if (e.keyCode == 38) self.hoverPrevVisibleItem(hovered); if (e.keyCode == 40) self.hoverNextVisibleItem(hovered); } else { self.hoverItem(self.getFirstVisibleItem()); } } self.calcScrollBarPosition(); } }, "_eventFocus": function(context, e) { if ( this.get("openOnFocus") ) { this.open(); this._eventOpen(context, e); } }, "_eventClick": function(context, e) { this.open(); this._eventOpen(context, e); }, "_eventOpen": function(context, e) { var c, value, msdb_value, self = this, selectedIds = self.getSelectedKeys(), list = self.get("list", null, !1), contextElement = e.currentTarget || (this instanceof Element ? this : null); if ( self.fx.isTextInput(contextElement) ) { msdb_value = contextElement.getAttribute('data-msdb-value'); if (msdb_value) msdb_value = msdb_value.trim(); // Если в инпуте уже есть значения, отметить их в списке как выбранные if (!msdb_value) { value = self.fx.trim(contextElement.value,",; ").split(/[;,]/ig); for (c = 0; c < value.length; c++) value[c] = value[c].trim(); self._selectByLabel(value, true); } else { msdb_value = msdb_value.split(/[;,]/ig); for (c = 0; c < msdb_value.length; c++) msdb_value[c] = msdb_value[c].trim(); self._selectByValue(msdb_value, true); } } // Снять hover со строки self.unhideAllOpt(); Object.keys(list).forEach(function(key) { var item = list[key]; if ( !( ( typeof contextElement.type != "undefined" && $.inArray( contextElement.type.toLowerCase(), ["submit","button"] ) > -1 ) || ( $.inArray( contextElement.tagName.toLowerCase(), ["submit","body","select"] ) > -1 ) ) ) { self.unhoverItem(item); } }); selectedIds = selectedIds.concat(self.getSelectedKeys()); // Записать value внутри инпута !self.get("freeWrite") && self.applySelectedToInput(); // Отметить выбранные строки self.applySelectedToList(self._getItemsByID(selectedIds)); // Положение ползунка self.calcScrollBarPosition(); // Пересчет высоты внутри списка self._calcListContainerHeight(); // Запустить событие // self.trigger("focus", e); }, "_onFocusOut": function() { this.get("multiple", null, !1) && this.applySelectedToInput(); }, "_initEvents": function(arg) { var eventName, self = this, tmpEvents = {}; // TODO переименовать this.events -> this._events; this.events = Object.create(null); // ---------------------------------------------------------------- // События которые передаются в коррне обьекта-параметра for (eventName in arg) { if (!arg.hasOwnProperty(eventName)) continue; if (typeof arg[eventName] != "function") continue; tmpEvents[eventName.replace(/^on/,'')] = arg[eventName]; } // События которые передаются в обькте events for (eventName in arg.events) { if (!arg.events.hasOwnProperty(eventName)) continue; if (typeof arg.events[eventName] != "function") continue; tmpEvents[eventName] = arg.events[eventName]; } // Установка событий for (eventName in tmpEvents) { if (!tmpEvents.hasOwnProperty(eventName)) continue; this.on( eventName, tmpEvents[eventName] ); } // ---------------------------------------------------------------- this.on( "keyup", function(context, e) { self._eventCheckInputEmpty(e); } ); this.on( "change", function(context, e) { self._eventCheckInputEmpty(e); } ); this.on( "input:empty", self._eventDefaultInputEmpty ); this.on("focus", self._eventFocus.bind(self)); this.on("click", self._eventClick.bind(self)); this.on( "focusout", function(context, e) { // Хак для FireFox. В нем нет relatedTarget для focusout if ( !e.relatedTarget ) { self._timers.focusoutInputs = setTimeout( function() { self.close(); }, self.get("focusOutInputsTimeoutDelay", null, !1) ); return; } if ( self._isDBoxInput(e.relatedTarget) ) { return; } if ( self._isDBoxElement(e.relatedTarget) || self._isTargetElement(e.relatedTarget) ) { return; } self.close(); } ); this.on("focusout", this._onFocusOut); // Отменяет таймаут для FF relatedTarget хака // Поскольку является частью блока-списка $(this.get("dbox_input")).bind("focus", function() { clearTimeout(self._timers.focusoutInputs); }); this.on("afterSet:list", this._eventSetList); // ---------------------------------------------------------------- // При самой первой инициализации if ( self._isColdInit() ) { window.addEventListener("resize", self._eventWindowResize.bind(self), false); } }, "_deactivateInstances": function(e) { for (var c=0; c'); styleElem.attr("id", "m-select-d-box-style"); body.append(styleElem); } styleElem.html(css); return this; }, "_initProps": function(arg) { // var self = this; var c, v, prop, defaultProps = {}; // TODO Сделать объект с ключами вместо массива var allowedKeys = [ { "key": "name", "type": "string" }, { "key": "list", "type": "array" }, { "key": "autoApply", "type": "any", "into": "boolean" }, { "key": "autoPosition", "type": "any", "into": "boolean" }, { "key": "autoComplete", "type": "any", "into": "boolean" }, { "key": "target", "type": "object" }, { "key": "multiple", "type": "any", "into": "boolean" }, { "key": "zIndex", "type": "numeric", "into": "integer" }, { "key": "width", "type": "any" }, { "key": "embeddedInput", "type": "any", "into": "boolean" }, { "key": "optionFilters", "type": "array" }, { "key": "closeButton", "type": "boolean" }, { "key": "language", "type": "string", "default": this.detectLanguage() }, { "key": "openOnFocus", "type": "boolean", "default": true }, { "key": "freeWrite", "type": "any", "into": "boolean" } ]; for (c = 0; c < allowedKeys.length; c++) { allowedKeys[c].key = allowedKeys[c].key.toLowerCase(); if ( "default" in allowedKeys[c] ) { defaultProps[allowedKeys[c].key] = allowedKeys[c].default; } } this.set(defaultProps); if (typeof arg != "object") return; for (prop in arg) { if ( !arg.hasOwnProperty(prop) ) continue; var key = prop.toLowerCase(); var option = null; for (v = 0; v < allowedKeys.length; v++) { if ( allowedKeys[v].key.toLowerCase() == key ) { option = allowedKeys[v]; break; } } if ( option ) { if ( option.type == "any" ) { } else if ( option.type == "array" ) { if ( !Array.isArray(arg[prop]) ) { throw new Error("Argument data type mismatch (key: '" + prop + "')"); } } else if ( option.type == "numeric" ) { if ( isNaN(arg[prop]) ) { throw new Error("Argument data type mismatch (key: '" + prop + "')"); } } else { if ( option.type != typeof arg[prop] ) { throw new Error("Argument data type mismatch (key: '" + prop + "')"); } } if ( option.hasOwnProperty("into") ) { if ( option.into == "boolean" ) { arg[prop] = Boolean(arg[prop]) } else if ( option.into == "integer" ) { arg[prop] = parseInt(arg[prop]) } else if ( option.into == "float" ) { arg[prop] = parseFloat(arg[prop]) } } // Исключение if (prop == "width") { arg[prop] = ( typeof arg.width == "undefined" ? "min" : ( $.inArray(arg.width, ["min","auto"]) > -1 ? arg.width : parseInt(arg.width) ) ) } // Запись в props this.set(key, arg[prop]); } } this.set({ "firstItem": void 0, "lastItem": void 0, "hoveredCache": Object.create(null), "hiddenCache": Object.create(null), "selectedCache": Object.create(null), "valuesCache": Object.create(null), "labelsCache": Object.create(null) }, null, !1); }, "_initElements": function() { var body = $("body").get(0); // -------------------------------------------------------------- // "Фоновая тьма" для моб. устройств if ( !this._globalElems.fade ) { this._globalElems.fade = $( '
' + '
' + '
' + '' + this.getText(".m-select-d-box-fade__outside-click-label-text") + '' + '
' + '
' ).get(0); body.appendChild(this._globalElems.fade); } // -------------------------------------------------------------- // Диалоговое окно var dbox = document.createElement('div'); // var jqDBox = $(dbox); this.set("dbox", dbox); dbox.className = "m-select-d-box m-select-d-box_hidden"; // jqDBox.addClass("m-select-d-box"); // jqDBox.addClass("m-select-d-box_hidden"); var searchInputContainer = $( '
' + '' + '
' ).get(0); this.set("dbox_input", $("input",searchInputContainer).get(0)); dbox.appendChild(searchInputContainer); if ( Boolean(this.get("embeddedInput")) ) { searchInputContainer.className += " m-select-d-box__search-input-container_active"; } this.set("dbox", dbox); if ( this.get("zIndex") ) dbox.style.zIndex = this.get("zIndex"); // -------------------------------------------------------------- dbox.appendChild($('
    ').get(0)); // -------------------------------------------------------------- // Ширина var width = this.get("width"); if (width == "auto") { dbox.style.width = this.get("target").clientWidth + "px"; } else if (width == "min") { } else { dbox.style.width = width + "px"; } body.appendChild(dbox); }, "_initList": function() { var list = this.get("list", null, !1); if ( !$.isArray(list) ) list = []; list = list.slice(0); var c, listItem, prev, labelCache = Object.create(null), valueCache = Object.create(null), self = this, dbox = this.get("dbox", null, !1), ul = $(".m-select-d-box__list-container", dbox).get(0); !this._onItemClick && (this._onItemClick = function(e) { clearTimeout(self._timers.focusoutInputs); var msdbid = this.getAttribute('data-msdbid'), list = self.get("list", null, !1), selectedIds = self.getSelectedKeys().concat(msdbid); if (list[msdbid].disabled) return; if ( self.get("multiple", null, !1) ) { list[msdbid].selected ? self._deselectByID(msdbid) : self._selectByID(msdbid, !1); } else { self._selectByID(msdbid, true); } self.applySelectedToInput(); self.applySelectedToList(self._getItemsByID(selectedIds)); e.listItem = list[msdbid]; self.trigger("select", e); self.calcPosition(); if (!self.get("multiple", null, !1)) self.close(); }); !this._onMouseLeave && (this._onMouseLeave = function() { var jqThis = $(this); if (jqThis.hasClass("m-select-d-box__list-item_hover")) jqThis.removeClass("m-select-d-box__list-item_hover"); }); ul.innerHTML = ""; for (c = 0; c < list.length; c++) { if (!([list[c]] + "")) continue; if ( $.inArray(typeof list[c], ["number","string"]) > -1 ) { list[c] = { "value": list[c] + "", "label": list[c] + "", "selected": false }; } else if ( typeof list[c] == "object" && this.fx.hop(list[c], "value") && this.fx.hop(list[c], "label") ) { if ( typeof list[c].selected == "undefined" || list[c].disabled ) { list[c].selected = false; } else { list[c].selected = Boolean(list[c].selected); } list[c].value += ""; list[c].label += ""; } else { continue; } list[c].id = c; valueCache[list[c].value] = list[c]; labelCache[list[c].label] = list[c]; if (prev) { prev.next = list[c]; list[c].prev = prev; } prev = list[c]; // ------------------------------------------------------------ listItem = list[c]; listItem.isHovered = false; listItem.elem = document.createElement("li"); listItem.elem.className = "m-select-d-box__list-item"; listItem.elem.setAttribute('data-msdbid', c + ''); listItem.$elem = $(listItem.elem); if (listItem.disabled) listItem.$elem.addClass("m-select-d-box__list-item_disabled"); // addEventListener || attachEvent $(listItem.elem).bind("click", this._onItemClick, null); // addEventListener || attachEvent $(listItem.elem).bind("mouseleave", this._onMouseLeave, null); listItem.elem.innerHTML = listItem.label; ul.appendChild(listItem.elem); } // close.list.for this.set('firstItem', list[0], null, !1); this.set("lastItem", list[list.length - 1], null, !1); this.set("labelsCache", labelCache, null, !1); this.set("valuesCache", valueCache, null, !1); this.set("list", list, null, false); }, "init": function(arg) { this._props = Object.create(null); var self = this; var body = $('body').get(0); // -------------------------------------------------------------------------------- /* * "this._initEvents" внутри использует ссылку на target. * target инициализируется в "this._initProps" и "this._initTarget" и внутри выполняют "this.set" * "this.set" внутри выполняет "this.trigger", для выполнение которого необходим инициализированный this.events * => парадокс того что нужно обьявить первично. * Решается предварительным обьявленимм this.events (Обьявляется дважды: this.initEvents, this.init) * */ this.events = Object.create(null); this._initProps(arg); this._initTarget(); this._initElements(); this._initEvents(arg); this._initList(); this._initStyles(); // -------------------------------------------------------------------------------- // Таймеры this.set("autoCompleteTimeoutDelay", 500, null, false); this.set("focusOutInputsTimeoutDelay", 250, null, false); this._timers = Object.create(null); this._timers.autoComplete = null; this._timers.focusedInputs = null; this._timers.focusoutInputs = null; // -------------------------------------------------------------------------------- // Целевой элемент var target = this.get("target", null, !1); // -------------------------------------------------------------------------------- // Контейнер списка var dbox = this.get("dbox", null, !1); // -------------------------------------------------------------------------------- if ( self.get("name", null, !1) ) { target.setAttribute("data-msdb-name", self.get("name", null, !1)); dbox.setAttribute("data-msdb-name", self.get("name", null, !1)); } self.on( "keydown", function(context, e) { self._eventDefaultKeyDownMultipleFalse(e); self._eventDefaultKeyDownMultipleTrue(e); } ); self.on( "keyup", function(context, e) { self._eventDefaultKeyUp(e); } ); // -------------------------------------------------------------- // Инициализация матчеров строк if ( !self.get("optionFilters", null, !1) ) { self.set( "optionFilters", [self.defaultOptionFilters.default], null, false ); } // -------------------------------------------------------------- if ( self._isColdInit() ) { $(body).bind( "click", function(e) { self._deactivateInstances(e); }, null ); } // -------------------------------------------------------------- // instances размещен в конце для правильной работы this._isColdInit() this.instances.push(this); self.trigger("init"); }, /** * Применяет языковые настройки к глобальным (общим) элементам имеющие подписи * @protected * @param {String} lang - устанавливаемый язык * */ "_applyLang": function(lang) { if (this._lastLang == lang) return; Object.getPrototypeOf(this)._lastLang = lang; // TODO существуют и другие места где необходимо сменить язык $(this._globalElems.fade) .find(".m-select-d-box-fade__outside-click-label-text") .html(this.getText(".m-select-d-box-fade__outside-click-label-text")); }, /** * @description Calculate position of list container * */ "calcPosition": function() { var self = this, body = $("body").get(0), target = this.get("target", null, !1), dbox = this.get("dbox", null, !1), jqDBox = $(dbox), offset = $(target).offset(), thisWidth = target.clientWidth, thisHeight = target.clientHeight, dboxWidth = dbox.clientWidth; jqDBox.removeClass("m-select-d-box_bottom"); dbox.style.left = (offset.left + (thisWidth / 2) - ((dboxWidth + (self._globalStyles[".m-select-d-box"].padding.replace(/[px]/gi,"") * 2)) / 2)) + "px"; var scrollY = window.scrollY || body.scrollTop; // TODO _isBoxBottomState() if ( (dbox.clientHeight + offset.top + thisHeight + 12 - scrollY) > window.innerHeight) { dbox.style.top = (offset.top - 12 - dbox.clientHeight) + "px"; jqDBox.addClass("m-select-d-box_bottom"); } else { dbox.style.top = (offset.top + thisHeight + 12) + "px"; } }, /** * Calc and apply scrollbar position of list container * */ "calcScrollBarPosition": function() { var dbox = this.get("dbox", null, !1), ul = $(".m-select-d-box__list-container", dbox).get(0), selected = !this.get("multiple", null, !1) ? this.getSelectedItems()[0] : this.getHoveredItems()[0]; if (!selected) return; var top = selected.$elem.position().top + ul.scrollTop; if (top < ul.clientHeight / 2) return ul.scrollTop = 0; ul.scrollTop = top - ul.clientHeight / 2; }, /** * Пересчитывает высоту для контейнера списка. * Важно для мобильных устройств, * где стили устанавливают макс-высоту ".m-select-d-box" относительно всего окна (80%) * => как следствие контейнер списка ".m-select-d-box__list-container" не может иметь высоту 100% (ведь высота устанавливается автоматически) * и сам при этом не может растягивать высоту родительского контента * Для этого необходимо чтобы кто-то имел высоту. * Решение: высоту получает ".m-select-d-box__list-container" он же растягивает родитель у которого height: auto и max-height: 80% * @protected * @ignore * */ "_calcListContainerHeight": function() { var listContainer = $(this.get("dbox", null, !1)) .find(".m-select-d-box__list-container") .get(0); if (this._isMobileState()) { var vh = window.innerHeight / 100; return listContainer.style.maxHeight = ((vh * 90) - 64 - 40) + "px"; } if (listContainer.style.maxHeight) listContainer.style.maxHeight = ""; }, "_isDBoxElement": function(element) { var dbox = this.get("dbox", null, !1); return !!$(dbox).find(element).length || dbox == element }, "_isTargetElement": function(element) { var target = this.get("target", null, !1); return !!$(target).find(element).length || target == element }, "_isDBoxInput": function(elem) { return this.get("dbox_input") === elem; }, /** * return true если соблюдены условия для показа мобильной версии * @protected * @return {Boolean} * @ignore * */ "_isMobileState": function() { return ( window.innerWidth <= 640 || ( window.innerWidth <= 740 && this.fx.isHDensScreen() ) ); }, "_isBoxBottomState": function() { var dbox = this.get("dbox", null, null); return Boolean(dbox.className.match(new RegExp("m-select-d-box_bottom"))); }, "_isColdInit": function() { return !this.instances.length; }, /** * Returns hovered options (Array of objects) * @return {Array} * */ "getHoveredItems": function() { var cache = this.get("hoveredCache", null, !1); return Object.keys(cache).map(function(c) { return cache[c]; }); }, /** * @description Return keys of selected options * @return {Array} * */ "getSelectedKeys": function() { return Object.keys(this.get('selectedCache', null, !1)); }, /** * @description Return values of selected options * @return {Array} * */ "getSelectedValues": function() { var cache = this.get("selectedCache", null, !1); return Object.keys(cache).map(function(c) { return cache[c].value; }); }, /** * @description Return labels of selected options * @return {Array} * */ "getSelectedLabels": function() { var cache = this.get("selectedCache", null, !1); return Object.keys(cache).map(function(c) { return cache[c].label; }); }, /** * Returns selected list options (Array of object) * @return {Array} * */ "getSelectedItems": function() { var cache = this.get("selectedCache", null, !1); return Object.keys(cache).map(function(c) { return cache[c]; }); }, /** * @description Check existence of value in list * @param {String} value * @return {Boolean} * */ "hasValue": function(value) { var cache = this.get("valuesCache", null, !1); return !!cache[value]; }, /** * @description Check existence of label in list * @param {String} label * @return {Boolean} * */ "hasLabel": function(label) { var cache = this.get("labelsCache", null, !1); return !!cache[label]; }, /** * Apply selected options to list container * @param {Array=} list - in purpose of optimisation (performance) you can use specific list options. By default method uses all list options * @return {MSelectDBox} * */ "applySelectedToList": function(list) { !list && (list = this.get("list", null, !1)); var _classNameSelected = "m-select-d-box__list-item_selected"; var _classNameDisabled = "m-select-d-box__list-item_disabled"; Object.keys(list).forEach(function(c) { var item = list[c]; if (!item) return; item.selected ? item.$elem.addClass(_classNameSelected) : item.$elem.removeClass(_classNameSelected); item.disabled ? item.$elem.addClass(_classNameDisabled) : item.$elem.removeClass(_classNameDisabled); }); return this; }, /** * Apply selected options to control * @return {MSelectDBox} * */ "applySelectedToInput": function() { var self = this, listValue = self.getSelectedValues(), listLabel = self.getSelectedLabels(), target = this.get("target", null, !1), dboxInput = this.get("dbox_input", null, !1); dboxInput.value = listLabel.join("; ") + (!listLabel.length || !this.get("multiple") ? "" : ";"); var tagName = target.tagName.toLowerCase(); if ( tagName == "input" ) { if ( self.fx.isTextInput(target) ) target.value = dboxInput.value; } else if ( tagName == "textarea" ) { target.value = listLabel.join(";\n") + (!listLabel.length || !this.get("multiple") ? "" : ";\n"); } else if ( tagName == "select" ) { for (var v = 0; v < target.options.length; v++) { target.options[v].selected = $.inArray(target.options[v].value, listValue) > -1; } } target.setAttribute("data-msdb-value", listValue.join(";") + (!listValue.length || !this.get("multiple", null, !1) ? "" : ";")); return this; }, /** * @description Select specified option in list * @param {Object} arg * @param {String | Array} arg.value - select by value * @param {String | Array} arg.label - select by label * @param {Number} arg.id - select by id * @param {Boolean} arg.blank - reset previous selected options * */ "select": function(arg) { var selectedIds = this.getSelectedKeys(); this._select(arg); this.applySelectedToList( this._getItemsByID( selectedIds.concat(this.getSelectedKeys()) ) ); this.applySelectedToInput(); return this; }, "_select": function(arg) { if (typeof arg != "object" || $.isArray(arg)) return; var value, key, blank = true; if ("blank" in arg) blank = !!arg.blank; if ("id" in arg) { this._selectByID(arg.id, blank); return; } if ("value" in arg) { value = arg.value; key = "value"; } else if ("label" in arg) { value = arg.label; key = "label"; } else { return; } value = [].concat(value); if (!this.get("multiple", null, !1) && value.length > 1) return null; if (key == "value") this._selectByValue(value, blank); else if (key == "label") this._selectByLabel(value, blank); }, /** * Выбрать по значению в списке * @protected * @param {Array} value * @param {Boolean} reset - обнуть ранее выбранные строки * */ "_selectByValue": function(value, reset) { reset && this._deselectAll(); var c, valuesCache = this.get("valuesCache", null, !1), selectedCache = this.get("selectedCache", null, !1); for (c = 0; c < value.length; c++) { if (!this.fx.hop(valuesCache, value[c])) continue; if (valuesCache[value[c]].disabled) continue; valuesCache[value[c]].selected = true; selectedCache[valuesCache[value[c]].id] = valuesCache[value[c]]; } }, /** * Выбрать по названию строки * @protected * @param {Array} label * @param {Boolean} reset - обнуть ранее выбранные строки * */ "_selectByLabel": function(label, reset) { reset && this._deselectAll(); var c, labelsCache = this.get("labelsCache", null, !1), selectedCache = this.get("selectedCache", null, !1); for (c = 0; c < label.length; c++) { if (!this.fx.hop(labelsCache, label[c])) continue; if (labelsCache[label[c]].disabled) continue; labelsCache[label[c]].selected = true; selectedCache[labelsCache[label[c]].id] = labelsCache[label[c]]; } }, /** * @description Снять выделение с указанного элемента * @param {Object} arg * @param {String | Array} arg.value - deselect by value * @param {String | Array} arg.label - deselect by label * @param {Number | String | Array} arg.id - deselect by id * */ "deselect": function(arg) { var selectedIds = this.getSelectedKeys(); this._deselect(arg); this.applySelectedToList( this._getItemsByID( selectedIds.concat(this.getSelectedKeys()) ) ); this.applySelectedToInput(); return this; }, /** * Снять выделение * @protected * @param {Object} arg * @param {Array | String} arg.value * @param {Array | String} arg.label * @param {Array | String | Number} arg.id * */ "_deselect": function(arg) { if (!$.isPlainObject(arg)) return; if ("value" in arg) return this._deselectByValue([].concat(arg.value)); if ("label" in arg) return this._deselectByLabel([].concat(arg.label)); if ("id" in arg) this._deselectByID(arg.id); }, /** * Снять выделение по значению * @protected * @param {Array | String} argVal * */ "_deselectByValue": function(argVal) { argVal = [].concat(argVal); var c, value, id, valuesCache = this.get("valuesCache", null, !1), selectedCache = this.get("selectedCache", null, !1); for (c = 0; c < argVal.length; c++) { value = argVal[c]; if (!this.fx.hop(valuesCache, value)) continue; id = valuesCache[value].id; valuesCache[value].selected = false; delete selectedCache[id]; } }, /** * Снять выделение по label строки списка * @protected * @param {Array | String} argLab - заголовки строк списка * */ "_deselectByLabel": function(argLab) { argLab = [].concat(argLab); var c, label, id, labelsCache = this.get("labelsCache", null, !1), selectedCache = this.get("selectedCache", null, !1); for (c = 0; c < argLab.length; c++) { label = argLab[c]; if (!this.fx.hop(labelsCache, label)) continue; id = labelsCache[label].id; labelsCache[label].selected = false; delete selectedCache[id]; } }, /** * Получить элементы списка по ключам (id) * @protected * @param {Array} ids - ключи списка * @return {Object} * */ "_getItemsByID": function(ids) { var c, obj = Object.create(null), list = this.get("list", null, !1); for (c=0; c":"ю" }; var value2 = value.toLowerCase().split(""); for (var c= 0, L=value2.length; c -1 ) { return true; } } else if ( tagName == "textarea" ) { return true; } return false; }, "isHDensScreen": function() { // http://stackoverflow.com/questions/19689715/what-is-the-best-way-to-detect-retina-support-on-a-device-using-javascript return ( ( window.matchMedia && ( window.matchMedia('only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)').matches ) ) || ( window.devicePixelRatio && window.devicePixelRatio > 1.3 ) ) || false }, "isRetinaScreen": function() { return ( ( window.matchMedia && ( window.matchMedia('only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx), only screen and (min-resolution: 75.6dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min--moz-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)').matches ) ) || ( window.devicePixelRatio && window.devicePixelRatio >= 2 ) ) || false }, "msplit": function(d,s) { s = s.replace(new RegExp('['+d.join('')+']','g'),d[0]); return s.split(d[0]); }, "trim": function(str, ch, di) { var regEx = []; if (!di || di == "left") regEx.push("^[" + ch + "]+"); if (!di || di == "right") regEx.push("[" + ch + "]+$"); return str.replace(new RegExp(regEx.join("|"), "g"), ''); }, "rest": function(arr, n) { if (typeof arr != "object") return []; if (typeof n != "number") return []; return Array.prototype.slice.call(arr, n); }, // hasOwnProperty "hop": function(obj, prop) { if ("hasOwnProperty" in obj) { return Object.prototype.hasOwnProperty.call(obj, prop); } return prop in obj; } } }; MSelectDBox.prototype.unhoverAllOpt = MSelectDBox.prototype.unhoverAllItems; MSelectDBox.prototype.unhideAllOpt = MSelectDBox.prototype.unhideAllItems; // --------------------------------------------------------------------------------------------------- var methodsList = { "open": 1, "close": 1, "isActive": 1, "get": 1, "set": 1, "select": 1, "selectAll": 1, "deselectAll": 1, "on": 1, "trigger": 1, "getSelectedLabels": 1, "getSelectedValues": 1, "getSelectedKeys": 1, "getText": 1, "setText": 1 }, jQueryChainMethods = { "open": 1, "close": 1, "set": 1, "select": 1, "selectAll": 1, "deselectAll": 1, "on": 1, "trigger": 1, "setText": 1 }, jQueryValFn = $.fn.val; $.fn.extend({ "val": function(value) { if (this.attr('data-msdb-value')) { if (!value) return this.mSelectDBox().getSelectedValues(); return this.mSelectDBox().select({ value: value }) && this; } return jQueryValFn.apply(this, arguments); }, "mSelectDBox": function(arg) { if (!this.length) return; // var name = this[0].getAttribute("data-msdb-name"); var c, ret, rest, instances = MSelectDBox.prototype.getInstances(), instance = void 0, input = this[0]; // TODO Получать экземпляр непосредственно из DOM элемента for (c=0; c