/* * NOTICE OF LICENSE * * This product is licensed for one customer to use on one installation (test stores and multishop included). * Site developer has the right to modify this module to suit their needs, but can not redistribute the module in * whole or in part. Any other use of this module constitutes a violation of the user agreement. * * DISCLAIMER * * NO WARRANTIES OF DATA SAFETY OR MODULE SECURITY * ARE EXPRESSED OR IMPLIED. USE THIS MODULE IN ACCORDANCE * WITH YOUR MERCHANT AGREEMENT, KNOWING THAT VIOLATIONS OF * PCI COMPLIANCY OR A DATA BREACH CAN COST THOUSANDS OF DOLLARS * IN FINES AND DAMAGE A STORES REPUTATION. USE AT YOUR OWN RISK. * * @author idnovate.com * @copyright 2022 idnovate.com * @license See above */ /* * jQuery UIx Multiselect 2.0 * * Authors: * Yanick Rochon (yanick.rochon[at]gmail[dot]com) * * Licensed under the MIT (MIT-LICENSE.txt) license. * * http://mind2soft.com/labs/jquery/multiselect/ * * * Depends: * jQuery UI 1.8+ * */ ;(function($, window, undefined) { // ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/) "use strict"; // Each instance must have their own drag and drop scope. We use a global page scope counter // so we do not create two instances with mistankenly the same scope! We do not support // cross instance drag and drop; this would require also copying the OPTION element and it // would slow the component down. This is not the widget's contract anyhow. var globalScope = 0; var DEF_OPTGROUP = ''; var PRE_OPTGROUP = 'group-'; // these events will trigger on the original element //var NATIVE_EVENTS = ["change"]; // for version 2.1 // a list of predefined events //var EVENT_CHANGE = 'change'; // for version 2.1 var EVENT_CHANGE = 'multiselectChange'; //var EVENT_SEARCH = 'beforesearch'; // for version 2.1 var EVENT_SEARCH = 'multiselectSearch'; var EVENT_REORDERED = 'multiselectReordered'; // The jQuery.uix namespace will automatically be created if it doesn't exist $.widget("uix.multiselect", { options: { availableListPosition: 'left',// 'top', 'right', 'bottom', 'left'; the position of the available list (default: 'right') // beforesearch: null, // a funciton called before searching. If the default is prevented, search will not happen (for version 2.1) collapsableGroups: true, // tells whether the option groups can be collapsed or not (default: true) created: null, // a function called when the widget is done loading (default: null) defaultGroupName: '', // the name of the default option group (default: '') filterSelected: false, // when searching, filter selected options also? (default: false) locale: 'auto', // any valid locale, 'auto', or '' for default built-in strings (default: 'auto') moveEffect: null, // 'blind','bounce','clip','drop','explode','fold','highlight','puff','pulsate','shake','slide' (default: null) moveEffectOptions: {}, // effect options (see jQuery UI documentation) (default: {}) moveEffectSpeed: null, // string ('slow','fast') or number in millisecond (ignored if moveEffect is 'show') (default: null) optionRenderer: false, // a function that will return the item element to be rendered in the list (default: false) optionGroupRenderer: false, // a function that will return the group item element to be rendered (default: false) searchDelay: 500, // the search delay in ms (default: 500) searchField: true, // false, true, 'toggle'; set the search field behaviour (default: 'toggle') searchPreFilter: null, // prepare the search term before filtering. searchFilter: null, // a search filter. Will receive the term and OPTION element and should return a boolean value. searchHeader: 'available', // 'available', 'selected'; set the list header that will host the search field (default: 'available') selectionMode: 'click,d&d', // how options can be selected separated by commas: 'click', "dblclick" and 'd&d' (default: 'click,d&d') showDefaultGroupHeader: false, // show the default option group header (default: false) showEmptyGroups: false, // always display option groups even if empty (default: false) splitRatio: 1, // % of the left list's width of the widget total width (default 0.55) sortable: true, // if the selected list should be user sortable or not sortMethod: null, // null, 'standard', 'natural'; a sort function name (see ItemComparators), or a custom function (default: null) selectAll: 'both' // 'available', 'selected', 'both', 'none' - Whether or not to display a select or deselect all icon (default: 'both') }, _create: function() { var that = this; var selListHeader, selListContent, avListHeader, avListContent; var btnSelectAll, btnDeselectAll; this.scope = 'multiselect' + (globalScope++); this.optionGroupIndex = 1; this._setLocale(this.options.locale); this.element.addClass('uix-multiselect-original'); this._elementWrapper = $('
').addClass('uix-multiselect ui-widget') .css({ width: '100%', height: 200 }) .append( $('
').addClass('multiselect-selected-list') .append( $('
').addClass('ui-widget-header') .append( btnDeselectAll = $('', { type:"button" }).addClass('uix-control-right') .attr('data-localekey', 'deselectAll') .attr('title', this._t('deselectAll')) .button({icons:{primary:'ui-icon-arrowthickstop-1-e'}, text:false}) .click(function(e) { e.preventDefault(); e.stopPropagation(); that.optionCache.setSelectedAll(false); return false; }) ['both,selected'.indexOf(this.options.selectAll)>=0 ? 'show' : 'hide']() ) .append( selListHeader = $('
').addClass('header-text') ) ) .append( selListContent = $('
').addClass('uix-list-container ui-widget-content') ) ) ['right,top'.indexOf(this.options.availableListPosition)>=0?'prepend':'append']( $('
').addClass('multiselect-available-list') .append( $('
').addClass('ui-widget-header') .append( btnSelectAll = $('', { type:"button" }).addClass('uix-control-right') .attr('data-localekey', 'selectAll') .attr('title', this._t('selectAll')) .button({icons:{primary:'ui-icon-arrowthickstop-1-w'}, text:false}) .click(function(e) { e.preventDefault(); e.stopPropagation(); that.optionCache.setSelectedAll(true); return false; }) ['both,available'.indexOf(this.options.selectAll)>=0 ? 'show' : 'hide']() ) .append( avListHeader = $('
').addClass('header-text') ) ) .append( avListContent = $('
').addClass('uix-list-container ui-widget-content') ) ) .insertAfter(this.element) ; this._buttons = { 'selectAll': btnSelectAll, 'deselectAll': btnDeselectAll }; this._headers = { 'selected': selListHeader, 'available': avListHeader }; this._lists = { 'selected': selListContent.attr('id', this.scope+'_selListContent'), 'available': avListContent.attr('id', this.scope+'_avListContent') }; this.optionCache = new OptionCache(this); this._searchDelayed = new SearchDelayed(this); this._initSearchable(); this._applyListDroppable(); this.refresh(this.options.created); }, /** * *************************************** * PUBLIC * *************************************** */ /** * Refresh all the lists from the underlaying element. This method is executed * asynchronously from the call, therefore it returns immediately. However, the * method accepts a callback parameter which will be executed when the refresh is * complete. * * @param callback function a callback function called when the refresh is complete */ refresh: function(callback) { this._resize(); // just make sure we display the widget right without delay asyncFunction(function() { this.optionCache.cleanup(); var opt, options = this.element[0].childNodes; for (var i=0, l1=options.length; i').addClass('uix-search ui-widget-content ui-corner-' + (isToggle ? 'left' : 'all'))[isToggle ? 'hide' : 'show']() .insertBefore( this._headers[searchHeader] ) .focus(function() { $(this).select(); }) .on("keydown keypress", function(e) { if (e.keyCode == 13) { e.preventDefault(); e.stopPropagation(); return false; } }) .keyup($.proxy(this._searchDelayed.request, this._searchDelayed)); } }, _applyListDroppable: function() { if (this.options.selectionMode.indexOf('d&d') == -1) return; var _optionCache = this.optionCache; var currentScope = this.scope; var getElementData = function(d) { return _optionCache._elements[d.data('element-index')]; }; var initDroppable = function(e, s) { e.droppable({ accept: function(draggable) { var eData = getElementData(draggable); return eData && (eData.selected != s); // from different seleciton only }, activeClass: 'ui-state-highlight', scope: currentScope, drop: function(evt, ui) { ui.draggable.removeClass('ui-state-disabled'); ui.helper.remove(); _optionCache.setSelected(getElementData(ui.draggable), s); } }); } initDroppable(this._lists['selected'], true); initDroppable(this._lists['available'], false); if (this.options.sortable) { var that = this; this._lists['selected'].sortable({ appendTo: 'parent', axis: "y", containment: $('.multiselect-selected-list', this._elementWrapper), //"parent", items: '.multiselect-element-wrapper', handle: '.group-element', revert: true, stop: $.proxy(function(evt, ui) { var prevGroup; $('.multiselect-element-wrapper', that._lists['selected']).each(function() { var currGroup = that.optionCache._groups.get($(this).data('option-group')); if (!prevGroup) { that.element.append(currGroup.groupElement); } else { currGroup.groupElement.insertAfter(prevGroup.groupElement); } prevGroup = currGroup; }); }, this) }); } }, _search: function(term, silent) { if (this._searchField.is(':visible')) { if (typeof term === "string") { // issue #36 this._searchField.val(term); } else { term = this._searchField.val(); } } this.optionCache.filter(term, silent); }, _setLocale: function(locale) { if (locale == 'auto') { locale = navigator.userLanguage || navigator.language || navigator.browserLanguage || navigator.systemLanguage || ''; } //if (!$.uix.multiselect.i18n[locale]) { locale = ''; // revert to default is not supported auto locale //} this.options.locale = locale; }, _t: function(key, plural, data) { return _({locale:this.options.locale, key:key, plural:plural, data:data}); }, _updateControls: function() { var that = this; $('.uix-control-left,.uix-control-right', this._elementWrapper).each(function() { $(this).attr('title', that._t( $(this).attr('data-localekey') )); }); }, _updateHeaders: function() { var t, info = this.optionCache.getSelectionInfo(); this._headers['selected'] .text( t = this._t('itemsSelected', info.selected.total, {count:info.selected.total}) ) .parent().attr('title', this.options.filterSelected ? this._t('itemsSelected', info.selected.count, {count:info.selected.count}) + ", " + this._t('itemsFiltered', info.selected.filtered, {count:info.selected.filtered}) : t ); this._headers['available'] .text( this._t('itemsAvailable', info.available.total, {count:info.available.total}) ) .parent().attr('title', this._t('itemsAvailable', info.available.count, {count:info.available.count}) + ", " + this._t('itemsFiltered', info.available.filtered, {count:info.available.filtered}) ); }, // call this method whenever the widget resizes // NOTE : the widget MUST be visible and have a width and height when calling this _resize: function() { var pos = this.options.availableListPosition.toLowerCase(); // shortcut var sSize = ('left,right'.indexOf(pos) >= 0) ? 'Width' : 'Height'; // split size fn var tSize = ('left,right'.indexOf(pos) >= 0) ? 'Height' : 'Width'; // total size fn var cSl = this.element['outer'+sSize]() * this.options.splitRatio; // list container size selected var cAv = this.element['outer'+sSize]() - cSl; // ... available var hSl = (tSize === 'Width') ? cSl : this.element.outerHeight(); // scrollable area size selected var hAv = (tSize === 'Width') ? cAv : this.element.outerHeight(); // ... available var styleRule = ('left,right'.indexOf(pos) >= 0) ? 'left' : 'top'; // CSS rule for offsetting var swap = ('left,top'.indexOf(pos) >= 0); // true if we swap left-right or top-bottom var isToggle = ('toggle' === this.options.searchField); // true if search field is toggle-able var headerBordersBoth = 'ui-corner-tl ui-corner-tr ui-corner-bl ui-corner-br ui-corner-top'; var hSlCls = (tSize === 'Width') ? (swap ? '' : 'ui-corner-top') : (swap ? 'ui-corner-tr' : 'ui-corner-tl'); var hAvCls = (tSize === 'Width') ? (swap ? 'ui-corner-top' : '') : (swap ? 'ui-corner-tl' : 'ui-corner-tr'); // calculate outer lists dimensions this._elementWrapper.find('.multiselect-available-list') [sSize.toLowerCase()](cAv).css(styleRule, swap ? 0 : cSl) [tSize.toLowerCase()](this.element['outer'+tSize]() + 1); // account for borders this._elementWrapper.find('.multiselect-selected-list') [sSize.toLowerCase()](cSl).css(styleRule, swap ? cAv : 0) [tSize.toLowerCase()](this.element['outer'+tSize]() + 1); // account for borders // selection all button this._buttons['selectAll'].button('option', 'icons', {primary: transferIcon(pos, 'ui-icon-arrowthickstop-1-', false) }); this._buttons['deselectAll'].button('option', 'icons', {primary: transferIcon(pos, 'ui-icon-arrowthickstop-1-', true) }); // header borders this._headers['available'].parent().removeClass(headerBordersBoth).addClass(hAvCls); this._headers['selected'].parent().removeClass(headerBordersBoth).addClass(hSlCls); // make both headers equal! if (!isToggle) { var h = Math.max(this._headers['selected'].parent().height(), this._headers['available'].parent().height()); this._headers['available'].parent().height(h); this._headers['selected'].parent().height(h); } // adjust search field width if (this._searchField) { this._searchField.width( (sSize === 'Width' ? cAv : this.element.width()) - (isToggle ? 52 : 26) ); // issue #50 } // calculate inner lists height this._lists['available'].height(hAv - this._headers['available'].parent().outerHeight() - 2); // account for borders this._lists['selected'].height(hSl - this._headers['selected'].parent().outerHeight() - 2); // account for borders }, /** * return false if the event was prevented by an handler, true otherwise */ _triggerUIEvent: function(event, ui) { var eventType; if (typeof event === 'string') { eventType = event; event = $.Event(event); } else { eventType = event.type; } //console.log($.inArray(event.type, NATIVE_EVENTS)); //if ($.inArray(event.type, NATIVE_EVENTS) > -1) { this.element.trigger(event, ui); //} else { // this._trigger(eventType, event, ui); //} return !event.isDefaultPrevented(); }, _setOption: function(key, value) { // Use the _setOption method to respond to changes to options switch(key) { // TODO } if (typeof(this._superApply) == 'function'){ this._superApply(arguments); }else{ $.Widget.prototype._setOption.apply(this, arguments); } } }); /** * Comparator registry. * * function(a, b, g) where a is compared to b and g is true if they are groups */ var ItemComparators = { /** * Naive general implementation */ standard: function(a, b) { if (a > b) return 1; if (a < b) return -1; return 0; }, /* * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license * Author: Jim Palmer (based on chunking idea from Dave Koelle) */ natural: function naturalSort(a, b) { var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, sre = /(^[ ]*|[ ]*$)/g, dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, hre = /^0x[0-9a-f]+$/i, ore = /^0/, i = function(s) { return naturalSort.insensitive && (''+s).toLowerCase() || ''+s }, // convert all to strings strip whitespace x = i(a).replace(sre, '') || '', y = i(b).replace(sre, '') || '', // chunk/tokenize xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'), // numeric, hex or date detection xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)), yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null, oFxNcL, oFyNcL; // first try and sort Hex codes or Dates if (yD) if ( xD < yD ) return -1; else if ( xD > yD ) return 1; // natural sorting through split numeric strings and default strings for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) { // find floats not starting with '0', string or 0 if not defined (Clint Priest) oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; // handle numeric vs string comparison - number < string - (Kyle Adams) if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; } // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' else if (typeof oFxNcL !== typeof oFyNcL) { oFxNcL += ''; oFyNcL += ''; } if (oFxNcL < oFyNcL) return -1; if (oFxNcL > oFyNcL) return 1; } return 0; } }; var transferDirection = ['n','e','s','w']; // button icon direction var transferOrientation = ['bottom','left','top','right']; // list of matching directions with icons var transferIcon = function(pos, prefix, selected) { return prefix + transferDirection[($.inArray(pos.toLowerCase(), transferOrientation) + (selected ? 2 : 0)) % 4]; }; /** * setTimeout on steroids! */ var asyncFunction = function(callback, timeout, self) { var args = Array.prototype.slice.call(arguments, 3); return setTimeout(function() { callback.apply(self || window, args); }, timeout); }; var SearchDelayed = function(widget, options) { this._widget = widget; this._options = options; this._lastSearchValue = null; }; SearchDelayed.prototype = { request: function() { if (this._widget._searchField.val() == this._lastSearchValue) return; // prevent searching twice same term this.cancelLastRequest(); this._timeout = asyncFunction(function() { this._timeout = null; this._lastSearchValue = this._widget._searchField.val(); this._widget._search(); }, this._widget.options.searchDelay, this); }, cancelLastRequest: function() { if (this._timeout) { clearTimeout(this._timeout); } } }; /** * Map of all option groups */ var GroupCache = function(comp) { // private members var keys = []; var items = {}; var comparator = comp; // public methods this.setComparator = function(comp) { comparator = comp; return this; }; this.clear = function() { keys = []; items = {}; return this; }; this.containsKey = function(key) { return !!items[key]; }; this.get = function(key) { return items[key]; }; this.put = function(key, val) { if (!items[key]) { if (comparator) { keys.splice((function() { var low = 0, high = keys.length; var mid = -1, c = 0; while (low < high) { mid = parseInt((low + high)/2); var a = items[keys[mid]].groupElement; var b = val.groupElement; c = comparator(a ? a.attr('label') : DEF_OPTGROUP, b ? b.attr('label') : DEF_OPTGROUP); if (c < 0) { low = mid + 1; } else if (c > 0) { high = mid; } else { return mid; } } return low; })(), 0, key); } else { keys.push(key); } } items[key] = val; return this; }; this.remove = function(key) { delete items[key]; return keys.splice(keys.indexOf(key), 1); }; this.each = function(callback) { var args = Array.prototype.slice.call(arguments, 1); args.splice(0, 0, null, null); for (var i=0, len=keys.length; i').appendTo(this._widget._lists['selected']), 'available': $('
').appendTo(this._widget._lists['available']) }; this._elements = []; this._groups = new GroupCache(); this._moveEffect = { fn: widget.options.moveEffect, options: widget.options.moveEffectOptions, speed: widget.options.moveEffectSpeed }; this._selectionMode = this._widget.options.selectionMode.indexOf('dblclick') > -1 ? 'dblclick' : this._widget.options.selectionMode.indexOf('click') > -1 ? 'click' : false; this.reset(); }; OptionCache.Options = { batchCount: 200, batchDelay: 50 }; OptionCache.prototype = { _createGroupElement: function(grpElement, optGroup, selected) { var that = this; var gData; var getLocalData = function() { if (!gData) gData = that._groups.get(optGroup); return gData; }; var getGroupName = function() { return grpElement ? grpElement.attr('label') : that._widget.options.defaultGroupName; }; var labelCount = $('').addClass('label') .text(getGroupName() + ' (0)') .attr('title', getGroupName() + ' (0)'); var fnUpdateCount = function() { var gDataDst = getLocalData()[selected?'selected':'available']; gDataDst.listElement[(!selected && (gDataDst.count || that._widget.options.showEmptyGroups)) || (gDataDst.count && ((gData.optionGroup != DEF_OPTGROUP) || that._widget.options.showDefaultGroupHeader)) ? 'show' : 'hide'](); var t = getGroupName() + ' (' + gDataDst.count + ')'; labelCount.text(t).attr('title', t); }; var e = $('
') .addClass('ui-widget-header ui-priority-secondary group-element') .append( $('', { type:"button" }).addClass('uix-control-right') .attr('data-localekey', (selected?'de':'')+'selectAllGroup') .attr('title', this._widget._t((selected?'de':'')+'selectAllGroup')) .button({icons:{primary:transferIcon(this._widget.options.availableListPosition, 'ui-icon-arrowstop-1-', selected)}, text:false}) .click(function(e) { e.preventDefault(); e.stopPropagation(); var gDataDst = getLocalData()[selected?'selected':'available']; if (gData.count > 0) { var _transferedOptions = []; that._bufferedMode(true); for (var i=gData.startIndex, len=gData.startIndex+gData.count, eData; i').addClass('ui-icon collapse-handle') .attr('data-localekey', 'collapseGroup') .attr('title', this._widget._t('collapseGroup')) .addClass(grpCollapseIcon) .mousedown(function(e) { e.stopPropagation(); }) .click(function(e) { e.preventDefault(); e.stopPropagation(); fnToggle(grpElement); return false; }) .prependTo(e.addClass('group-element-collapsable')) ; fnToggle = function(grpElement) { var gDataDst = getLocalData()[selected?'selected':'available'], collapseIconAttr = (grpElement) ? grpElement.attr('data-collapse-icon') : null, expandIconAttr = (grpElement) ? grpElement.attr('data-expand-icon') : null, collapseIcon = (collapseIconAttr) ? 'ui-icon ' + collapseIconAttr : 'ui-icon ui-icon-triangle-1-s', expandIcon = (expandIconAttr) ? 'ui-icon ' + expandIconAttr : 'ui-icon ui-icon-triangle-1-e'; gDataDst.collapsed = !gDataDst.collapsed; gDataDst.listContainer.slideToggle(); // animate options? h.removeClass(gDataDst.collapsed ? collapseIcon : expandIcon) .addClass(gDataDst.collapsed ? expandIcon : collapseIcon); }; }else{ if (groupIcon) { $('').addClass('collapse-handle '+groupIcon) .css('cursor','default') .prependTo(e.addClass('group-element-collapsable')); } } return $('
') // create an utility function to update group element count .data('fnUpdateCount', fnUpdateCount) .data('fnToggle', fnToggle || $.noop) .append(e) ; }, _createGroupContainerElement: function(grpElement, optGroup, selected) { var that = this; var e = $('
'); var _received_index; if (this._widget.options.sortable && selected) { e.sortable({ tolerance: "pointer", appendTo: this._widget._elementWrapper, connectWith: this._widget._lists['available'].attr('id'), scope: this._widget.scope, helper: 'clone', receive: function(evt, ui) { var e = that._elements[_received_index = ui.item.data('element-index')]; e.selected = true; e.optionElement.prop('selected', true); e.listElement.removeClass('ui-state-active'); }, stop: function(evt, ui) { var e; if (_received_index != undefined) { e = that._elements[_received_index]; _received_index = undefined; ui.item.replaceWith(e.listElement.addClass('ui-state-highlight option-selected')); that._widget._updateHeaders(); that._widget._triggerUIEvent(EVENT_CHANGE, { optionElements:[e.optionElement[0]], selected:true } ); } else { e = that._elements[ui.item.data('element-index')]; if (e && !e.selected) { that._bufferedMode(true); that._appendToList(e); that._bufferedMode(false); } } if (e) that._reorderSelected(e.optionGroup); }, revert: true }); } if (this._selectionMode) { $(e).on(this._selectionMode, 'div.option-element', function() { var eData = that._elements[$(this).data('element-index')]; eData.listElement.removeClass('ui-state-hover'); that.setSelected(eData, !selected); }); } return e; }, _createElement: function(optElement, optGroup) { var o = this._widget.options.optionRenderer ? this._widget.options.optionRenderer(optElement, optGroup) : $('
').text(optElement.text()); var optIcon = optElement.attr("data-option-icon"); var e = $('
').append(o).addClass('ui-state-default option-element') .attr("unselectable", "on") // disable text selection on this element (IE, Opera) .data('element-index', -1) .hover( function() { if (optElement.prop('selected')) $(this).removeClass('ui-state-highlight'); $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); if (optElement.prop('selected')) $(this).addClass('ui-state-highlight'); } ); if (this._widget.options.selectionMode.indexOf('d&d') > -1) { var that = this; e.draggable({ addClasses: false, cancel: (this._widget.options.sortable ? '.option-selected, ' : '') + '.ui-state-disabled', appendTo: this._widget._elementWrapper, scope: this._widget.scope, start: function(evt, ui) { $(this).addClass('ui-state-disabled ui-state-active'); ui.helper.width($(this).width()).height($(this).height()); }, stop: function(evt, ui) { $(this).removeClass('ui-state-disabled ui-state-active'); }, helper: 'clone', revert: 'invalid', zIndex: 99999, disabled: optElement.prop('disabled') }); if (optElement.prop('disabled')) { e.addClass('ui-state-disabled'); } if (this._widget.options.sortable) { e.draggable('option', 'connectToSortable', this._groups.get(optGroup)['selected'].listContainer); } } else if (optElement.prop('disabled')) { e[(optElement.prop('disabled') ? "add" : "remove") + "Class"]('ui-state-disabled'); } if (optIcon) { e.addClass('grouped-option').prepend($('').addClass('ui-icon ' + optIcon)); } return e; }, _isOptionCollapsed: function(eData) { return this._groups.get(eData.optionGroup)[eData.selected?'selected':'available'].collapsed; }, _updateGroupElements: function(gData) { if (gData) { gData['selected'].count = 0; gData['available'].count = 0; for (var i=gData.startIndex, len=gData.startIndex+gData.count; i= gData.startIndex) && (this._elements[insertIndex].selected != eData.selected)) { insertIndex--; } if (insertIndex < gData.startIndex) { gDataDst.listContainer.prepend(eData.listElement); } else { var prev = this._elements[insertIndex].listElement; // FIX : if previous element is animated, get it's animated parent as reference if (prev.parent().hasClass('ui-effects-wrapper')) { prev = prev.parent(); } eData.listElement.insertAfter(prev); } eData.listElement[(eData.selected?'add':'remove')+'Class']('ui-state-highlight option-selected'); if ((eData.selected || !eData.filtered) && !this._isOptionCollapsed(eData) && this._moveEffect && this._moveEffect.fn) { eData.listElement.hide().show(this._moveEffect.fn, this._moveEffect.options, this._moveEffect.speed); } else if (eData.filtered) { eData.listElement.hide(); } }, _reorderSelected: function(optGroup) { var e = this._elements; var g = this._groups.get(optGroup); var container = g.groupElement ? g.groupElement : this._widget.element; var prevElement; $('.option-element', g['selected'].listContainer).each(function() { var currElement = e[$(this).data('element-index')].optionElement; if (!prevElement) { container.prepend(currElement); } else { currElement.insertAfter(prevElement); } prevElement = currElement; }); this._widget._triggerUIEvent(EVENT_REORDERED, { selectElement:container.context } ); }, _bufferedMode: function(enabled) { if (enabled) { this._oldMoveEffect = this._moveEffect; this._moveEffect = null; // backup lists' scroll position before going into buffered mode this._widget._lists['selected'].data('scrollTop', this._widget._lists['selected'].scrollTop()); this._widget._lists['available'].data('scrollTop', this._widget._lists['available'].scrollTop()); this._listContainers['selected'].detach(); this._listContainers['available'].detach(); } else { // restore scroll position (if available) this._widget._lists['selected'].append(this._listContainers['selected']) .scrollTop( this._widget._lists['selected'].data('scrollTop') || 0 ); this._widget._lists['available'].append(this._listContainers['available']) .scrollTop( this._widget._lists['available'].data('scrollTop') || 0 ); this._moveEffect = this._oldMoveEffect; delete this._oldMoveEffect; } }, reset: function(destroy) { this._groups.clear(); this._listContainers['selected'].empty(); this._listContainers['available'].empty(); if (destroy) { for (var i=0, e=this._elements, len=e.length; i -1)) { this._elements.splice(i--, 1)[0].listElement.remove(); } } for (var i=0, len=_groupsRemoved.length; i').addClass('multiselect-element-wrapper').data('option-group', g); var wrapper_available = $('
').addClass('multiselect-element-wrapper').data('option-group', g); wrapper_selected.append(v.selected.listElement.hide()); if (g != DEF_OPTGROUP || (g == DEF_OPTGROUP && showDefGroupName)) { wrapper_available.append(v['available'].listElement.show()); } wrapper_selected.append(v['selected'].listContainer); wrapper_available.append(v['available'].listContainer); l['selected'].append(wrapper_selected); l['available'].append(wrapper_available); } v.count = 0; }, this._listContainers, this._widget.options.showDefaultGroupHeader); for (var i=0, eData, gData, len=this._elements.length; i= i) { gData.startIndex = i; gData.count = 1; } else { gData.count++; } // save element index for back ref eData.listElement.data('element-index', eData.index = i); if (eData.optionElement.data('element-index') == undefined || eData.selected != eData.optionElement.prop('selected')) { eData.selected = eData.optionElement.prop('selected'); eData.optionElement.data('element-index', i); // also save for back ref here this._appendToList(eData); } } this._updateGroupElements(); this._widget._updateHeaders(); this._groups.each(function(g,v,t) { t._reorderSelected(g); }, this); this._bufferedMode(false); }, filter: function(term, silent) { if (term && !silent) { var ui = { term:term }; if (this._widget._triggerUIEvent(EVENT_SEARCH, ui )) { term = ui.term; // update term } else { return; } } this._bufferedMode(true); var filterSelected = this._widget.options.filterSelected; var filterFn = this._widget.options.searchFilter || function(term, opt) { return opt.innerHTML.toLocaleLowerCase().indexOf(term) > -1; }; term = (this._widget.options.searchPreFilter || function(term) { return term ? (term+"").toLocaleLowerCase() : false; })(term); for (var i=0, eData, len=this._elements.length, filtered; i 1 && i18n[p.key+'_plural']) { t = i18n[p.key+'_plural']; } else if (plural === 0 && i18n[p.key+'_nil']) { t = i18n[p.key+'_nil']; } else { t = i18n[p.key] || ''; } return t.replace(/\{([^\}]+)\}/g, function(m, n) { return data[n]; }); }; /** * Default translation */ $.uix.multiselect.i18n = { '': { selectAll: selectAll, deselectAll: deselectAll, search: searchOptions, collapseGroup: collapseGroup, expandGroup: expandGroup, selectAllGroup: searchAllGroup, deselectAllGroup: deselectAllGroup, itemsSelected_nil: itemsSelected_nil, // 0 itemsSelected: '{count} ' + itemsSelected, // 0, 1 itemsSelected_plural: '{count} ' + itemsSelected_plural, // n //itemsSelected_plural_two: ... // 2 //itemsSelected_plural_few: ... // 3, 4 itemsAvailable_nil: itemsAvailable_nil, itemsAvailable: '{count} ' + itemsAvailable, itemsAvailable_plural: '{count} ' + itemsAvailable_plural, //itemsAvailable_plural_two: ... //itemsAvailable_plural_few: ... itemsFiltered_nil: itemsFiltered_nil, itemsFiltered: '{count} ' + itemsFiltered, itemsFiltered_plural: '{count} ' + itemsFiltered_plural, //itemsFiltered_plural_two: ... //itemsFiltered_plural_few: ... } }; })(jQuery, window);