From f39cb3fbc9c8cce13b9cc759b257cb7d4bd9587a Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Thu, 6 Nov 2014 15:48:34 +0100 Subject: Migrate multiselect to user_ldap --- .scrutinizer.yml | 2 +- apps/user_ldap/settings.php | 4 +- apps/user_ldap/vendor/ui-multiselect/MIT-LICENSE | 20 + .../vendor/ui-multiselect/jquery.multiselect.css | 23 + .../ui-multiselect/src/jquery.multiselect.js | 705 +++++++++++++++++++++ core/css/jquery.multiselect.css | 23 - core/js/jquery.multiselect.js | 705 --------------------- 7 files changed, 751 insertions(+), 731 deletions(-) create mode 100644 apps/user_ldap/vendor/ui-multiselect/MIT-LICENSE create mode 100644 apps/user_ldap/vendor/ui-multiselect/jquery.multiselect.css create mode 100644 apps/user_ldap/vendor/ui-multiselect/src/jquery.multiselect.js delete mode 100644 core/css/jquery.multiselect.css delete mode 100644 core/js/jquery.multiselect.js diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 2a555ad28f6..f66c1b86eac 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -2,6 +2,7 @@ filter: excluded_paths: - '3rdparty/*' - 'apps/*/3rdparty/*' + - 'apps/*/vendor/*' - 'l10n/*' - 'core/l10n/*' - 'apps/*/l10n/*' @@ -13,7 +14,6 @@ filter: - 'core/js/jquery-tipsy.js' - 'core/js/jquery-ui-1.10.0.custom.js' - 'core/js/placeholders.js' - - 'core/js/jquery.multiselect.js' imports: diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index ca61a53b196..3b448aec757 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -28,9 +28,9 @@ OC_Util::checkAdminUser(); OCP\Util::addScript('user_ldap', 'ldapFilter'); OCP\Util::addScript('user_ldap', 'experiencedAdmin'); OCP\Util::addScript('user_ldap', 'settings'); -OCP\Util::addScript('core', 'jquery.multiselect'); +\OC_Util::addVendorScript('user_ldap', 'ui-multiselect/src/jquery.multiselect'); OCP\Util::addStyle('user_ldap', 'settings'); -OCP\Util::addStyle('core', 'jquery.multiselect'); +\OC_Util::addVendorStyle('user_ldap', 'ui-multiselect/jquery.multiselect'); OCP\Util::addStyle('core', 'jquery-ui-1.10.0.custom'); // fill template diff --git a/apps/user_ldap/vendor/ui-multiselect/MIT-LICENSE b/apps/user_ldap/vendor/ui-multiselect/MIT-LICENSE new file mode 100644 index 00000000000..2dc8e79e3ad --- /dev/null +++ b/apps/user_ldap/vendor/ui-multiselect/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2011 Eric Hynds + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/apps/user_ldap/vendor/ui-multiselect/jquery.multiselect.css b/apps/user_ldap/vendor/ui-multiselect/jquery.multiselect.css new file mode 100644 index 00000000000..9b81c3bdcfb --- /dev/null +++ b/apps/user_ldap/vendor/ui-multiselect/jquery.multiselect.css @@ -0,0 +1,23 @@ +.ui-multiselect { padding:2px 0 2px 4px; text-align:left; } +.ui-multiselect span.ui-icon { float:right; } +.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; } +.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; } + +.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px; } +.ui-multiselect-header ul { font-size:14px; } +.ui-multiselect-header ul li { float:left; padding:0 10px 0 0; } +.ui-multiselect-header a { text-decoration:none; } +.ui-multiselect-header a:hover { text-decoration:underline; } +.ui-multiselect-header span.ui-icon { float:left;} +.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0; } + +.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left; } +.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll; } +.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px; } +.ui-multiselect-checkboxes label input { position:relative; top:1px; } +.ui-multiselect-checkboxes li { clear:both; font-size:14px; padding-right:3px; } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid; } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none; } + +/* remove label borders in IE6 because IE6 does not support transparency */ +* html .ui-multiselect-checkboxes label { border:none; } diff --git a/apps/user_ldap/vendor/ui-multiselect/src/jquery.multiselect.js b/apps/user_ldap/vendor/ui-multiselect/src/jquery.multiselect.js new file mode 100644 index 00000000000..16ae4264177 --- /dev/null +++ b/apps/user_ldap/vendor/ui-multiselect/src/jquery.multiselect.js @@ -0,0 +1,705 @@ +/* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */ +/* + * jQuery MultiSelect UI Widget 1.13 + * Copyright (c) 2012 Eric Hynds + * + * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/ + * + * Depends: + * - jQuery 1.4.2+ + * - jQuery UI 1.8 widget factory + * + * Optional: + * - jQuery UI effects + * - jQuery UI position utility + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * +*/ +(function($, undefined){ + +var multiselectID = 0; + +$.widget("ech.multiselect", { + + // default options + options: { + header: true, + height: 175, + minWidth: 225, + classes: '', + checkAllText: 'Check all', + uncheckAllText: 'Uncheck all', + noneSelectedText: 'Select options', + selectedText: '# selected', + selectedList: 0, + show: null, + hide: null, + autoOpen: false, + multiple: true, + position: {} + }, + + _create: function(){ + var el = this.element.hide(), + o = this.options; + + this.speed = $.fx.speeds._default; // default speed for effects + this._isOpen = false; // assume no + + var + button = (this.button = $('')) + .addClass('ui-multiselect ui-widget ui-state-default ui-corner-all') + .addClass( o.classes ) + .attr({ 'title':el.attr('title'), 'aria-haspopup':true, 'tabIndex':el.attr('tabIndex') }) + .insertAfter( el ), + + buttonlabel = (this.buttonlabel = $('')) + .html( o.noneSelectedText ) + .appendTo( button ), + + menu = (this.menu = $('
')) + .addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all') + .addClass( o.classes ) + .appendTo( document.body ), + + header = (this.header = $('
')) + .addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix') + .appendTo( menu ), + + headerLinkContainer = (this.headerLinkContainer = $('
    ')) + .addClass('ui-helper-reset') + .html(function(){ + if( o.header === true ){ + return '
  • ' + o.checkAllText + '
  • ' + o.uncheckAllText + '
  • '; + } else if(typeof o.header === "string"){ + return '
  • ' + o.header + '
  • '; + } else { + return ''; + } + }) + .append('
  • ') + .appendTo( header ), + + checkboxContainer = (this.checkboxContainer = $('
      ')) + .addClass('ui-multiselect-checkboxes ui-helper-reset') + .appendTo( menu ); + + // perform event bindings + this._bindEvents(); + + // build menu + this.refresh( true ); + + // some addl. logic for single selects + if( !o.multiple ){ + menu.addClass('ui-multiselect-single'); + } + }, + + _init: function(){ + if( this.options.header === false ){ + this.header.hide(); + } + if( !this.options.multiple ){ + this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide(); + } + if( this.options.autoOpen ){ + this.open(); + } + if( this.element.is(':disabled') ){ + this.disable(); + } + }, + + refresh: function( init ){ + var el = this.element, + o = this.options, + menu = this.menu, + checkboxContainer = this.checkboxContainer, + optgroups = [], + html = "", + id = el.attr('id') || multiselectID++; // unique ID for the label & option tags + + // build items + el.find('option').each(function( i ){ + var $this = $(this), + parent = this.parentNode, + title = this.innerHTML, + description = this.title, + value = this.value, + inputID = 'ui-multiselect-' + (this.id || id + '-option-' + i), + isDisabled = this.disabled, + isSelected = this.selected, + labelClasses = [ 'ui-corner-all' ], + liClasses = (isDisabled ? 'ui-multiselect-disabled ' : ' ') + this.className, + optLabel; + + // is this an optgroup? + if( parent.tagName === 'OPTGROUP' ){ + optLabel = parent.getAttribute( 'label' ); + + // has this optgroup been added already? + if( $.inArray(optLabel, optgroups) === -1 ){ + html += '
    • ' + optLabel + '
    • '; + optgroups.push( optLabel ); + } + } + + if( isDisabled ){ + labelClasses.push( 'ui-state-disabled' ); + } + + // browsers automatically select the first option + // by default with single selects + if( isSelected && !o.multiple ){ + labelClasses.push( 'ui-state-active' ); + } + + html += '
    • '; + + // create the label + html += '
    • '; + }); + + // insert into the DOM + checkboxContainer.html( html ); + + // cache some moar useful elements + this.labels = menu.find('label'); + this.inputs = this.labels.children('input'); + + // set widths + this._setButtonWidth(); + this._setMenuWidth(); + + // remember default value + this.button[0].defaultValue = this.update(); + + // broadcast refresh event; useful for widgets + if( !init ){ + this._trigger('refresh'); + } + }, + + // updates the button text. call refresh() to rebuild + update: function(){ + var o = this.options, + $inputs = this.inputs, + $checked = $inputs.filter(':checked'), + numChecked = $checked.length, + value; + + if( numChecked === 0 ){ + value = o.noneSelectedText; + } else { + if($.isFunction( o.selectedText )){ + value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get()); + } else if( /\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList){ + value = $checked.map(function(){ return $(this).next().html(); }).get().join(', '); + } else { + value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length); + } + } + + this.buttonlabel.html( value ); + return value; + }, + + // binds events + _bindEvents: function(){ + var self = this, button = this.button; + + function clickHandler(){ + self[ self._isOpen ? 'close' : 'open' ](); + return false; + } + + // webkit doesn't like it when you click on the span :( + button + .find('span') + .bind('click.multiselect', clickHandler); + + // button events + button.bind({ + click: clickHandler, + keypress: function( e ){ + switch(e.which){ + case 27: // esc + case 38: // up + case 37: // left + self.close(); + break; + case 39: // right + case 40: // down + self.open(); + break; + } + }, + mouseenter: function(){ + if( !button.hasClass('ui-state-disabled') ){ + $(this).addClass('ui-state-hover'); + } + }, + mouseleave: function(){ + $(this).removeClass('ui-state-hover'); + }, + focus: function(){ + if( !button.hasClass('ui-state-disabled') ){ + $(this).addClass('ui-state-focus'); + } + }, + blur: function(){ + $(this).removeClass('ui-state-focus'); + } + }); + + // header links + this.header + .delegate('a', 'click.multiselect', function( e ){ + // close link + if( $(this).hasClass('ui-multiselect-close') ){ + self.close(); + + // check all / uncheck all + } else { + self[ $(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll' ](); + } + + e.preventDefault(); + }); + + // optgroup label toggle support + this.menu + .delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function( e ){ + e.preventDefault(); + + var $this = $(this), + $inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)'), + nodes = $inputs.get(), + label = $this.parent().text(); + + // trigger event and bail if the return is false + if( self._trigger('beforeoptgrouptoggle', e, { inputs:nodes, label:label }) === false ){ + return; + } + + // toggle inputs + self._toggleChecked( + $inputs.filter(':checked').length !== $inputs.length, + $inputs + ); + + self._trigger('optgrouptoggle', e, { + inputs: nodes, + label: label, + checked: nodes[0].checked + }); + }) + .delegate('label', 'mouseenter.multiselect', function(){ + if( !$(this).hasClass('ui-state-disabled') ){ + self.labels.removeClass('ui-state-hover'); + $(this).addClass('ui-state-hover').find('input').focus(); + } + }) + .delegate('label', 'keydown.multiselect', function( e ){ + e.preventDefault(); + + switch(e.which){ + case 9: // tab + case 27: // esc + self.close(); + break; + case 38: // up + case 40: // down + case 37: // left + case 39: // right + self._traverse(e.which, this); + break; + case 13: // enter + $(this).find('input')[0].click(); + break; + } + }) + .delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function( e ){ + var $this = $(this), + val = this.value, + checked = this.checked, + tags = self.element.find('option'); + + // bail if this input is disabled or the event is cancelled + if( this.disabled || self._trigger('click', e, { value: val, text: this.title, checked: checked }) === false ){ + e.preventDefault(); + return; + } + + // make sure the input has focus. otherwise, the esc key + // won't close the menu after clicking an item. + $this.focus(); + + // toggle aria state + $this.attr('aria-selected', checked); + + // change state on the original option tags + tags.each(function(){ + if( this.value === val ){ + this.selected = checked; + } else if( !self.options.multiple ){ + this.selected = false; + } + }); + + // some additional single select-specific logic + if( !self.options.multiple ){ + self.labels.removeClass('ui-state-active'); + $this.closest('label').toggleClass('ui-state-active', checked ); + + // close menu + self.close(); + } + + // fire change on the select box + self.element.trigger("change"); + + // setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827 + // http://bugs.jquery.com/ticket/3827 + setTimeout($.proxy(self.update, self), 10); + }); + + // close each widget when clicking on any other element/anywhere else on the page + $(document).bind('mousedown.multiselect', function( e ){ + if(self._isOpen && !$.contains(self.menu[0], e.target) && !$.contains(self.button[0], e.target) && e.target !== self.button[0]){ + self.close(); + } + }); + + // deal with form resets. the problem here is that buttons aren't + // restored to their defaultValue prop on form reset, and the reset + // handler fires before the form is actually reset. delaying it a bit + // gives the form inputs time to clear. + $(this.element[0].form).bind('reset.multiselect', function(){ + setTimeout($.proxy(self.refresh, self), 10); + }); + }, + + // set button width + _setButtonWidth: function(){ + var width = this.element.outerWidth(), + o = this.options; + + if( /\d/.test(o.minWidth) && width < o.minWidth){ + width = o.minWidth; + } + + // set widths + this.button.width( width ); + }, + + // set menu width + _setMenuWidth: function(){ + var m = this.menu, + width = this.button.outerWidth()- + parseInt(m.css('padding-left'),10)- + parseInt(m.css('padding-right'),10)- + parseInt(m.css('border-right-width'),10)- + parseInt(m.css('border-left-width'),10); + + m.width( width || this.button.outerWidth() ); + }, + + // move up or down within the menu + _traverse: function( which, start ){ + var $start = $(start), + moveToLast = which === 38 || which === 37, + + // select the first li that isn't an optgroup label / disabled + $next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[ moveToLast ? 'last' : 'first'](); + + // if at the first/last element + if( !$next.length ){ + var $container = this.menu.find('ul').last(); + + // move to the first/last + this.menu.find('label')[ moveToLast ? 'last' : 'first' ]().trigger('mouseover'); + + // set scroll position + $container.scrollTop( moveToLast ? $container.height() : 0 ); + + } else { + $next.find('label').trigger('mouseover'); + } + }, + + // This is an internal function to toggle the checked property and + // other related attributes of a checkbox. + // + // The context of this function should be a checkbox; do not proxy it. + _toggleState: function( prop, flag ){ + return function(){ + if( !this.disabled ) { + this[ prop ] = flag; + } + + if( flag ){ + this.setAttribute('aria-selected', true); + } else { + this.removeAttribute('aria-selected'); + } + }; + }, + + _toggleChecked: function( flag, group ){ + var $inputs = (group && group.length) ? group : this.inputs, + self = this; + + // toggle state on inputs + $inputs.each(this._toggleState('checked', flag)); + + // give the first input focus + $inputs.eq(0).focus(); + + // update button text + this.update(); + + // gather an array of the values that actually changed + var values = $inputs.map(function(){ + return this.value; + }).get(); + + // toggle state on original option tags + this.element + .find('option') + .each(function(){ + if( !this.disabled && $.inArray(this.value, values) > -1 ){ + self._toggleState('selected', flag).call( this ); + } + }); + + // trigger the change event on the select + if( $inputs.length ) { + this.element.trigger("change"); + } + }, + + _toggleDisabled: function( flag ){ + this.button + .attr({ 'disabled':flag, 'aria-disabled':flag })[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled'); + + var inputs = this.menu.find('input'); + var key = "ech-multiselect-disabled"; + + if(flag) { + // remember which elements this widget disabled (not pre-disabled) + // elements, so that they can be restored if the widget is re-enabled. + inputs = inputs.filter(':enabled') + .data(key, true) + } else { + inputs = inputs.filter(function() { + return $.data(this, key) === true; + }).removeData(key); + } + + inputs + .attr({ 'disabled':flag, 'arial-disabled':flag }) + .parent()[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled'); + + this.element + .attr({ 'disabled':flag, 'aria-disabled':flag }); + }, + + // open the menu + open: function( e ){ + var self = this, + button = this.button, + menu = this.menu, + speed = this.speed, + o = this.options, + args = []; + + // bail if the multiselectopen event returns false, this widget is disabled, or is already open + if( this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen ){ + return; + } + + var $container = menu.find('ul').last(), + effect = o.show, + pos = button.offset(); + + // figure out opening effects/speeds + if( $.isArray(o.show) ){ + effect = o.show[0]; + speed = o.show[1] || self.speed; + } + + // if there's an effect, assume jQuery UI is in use + // build the arguments to pass to show() + if( effect ) { + args = [ effect, speed ]; + } + + // set the scroll of the checkbox container + $container.scrollTop(0).height(o.height); + + // position and show menu + if( $.ui.position && !$.isEmptyObject(o.position) ){ + o.position.of = o.position.of || button; + + menu + .show() + .position( o.position ) + .hide(); + + // if position utility is not available... + } else { + menu.css({ + top: pos.top + button.outerHeight(), + left: pos.left + }); + } + + // show the menu, maybe with a speed/effect combo + $.fn.show.apply(menu, args); + + // select the first option + // triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover + // will actually trigger mouseenter. the mouseenter trigger is there for when it's eventually fixed + this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus'); + + button.addClass('ui-state-active'); + this._isOpen = true; + this._trigger('open'); + }, + + // close the menu + close: function(){ + if(this._trigger('beforeclose') === false){ + return; + } + + var o = this.options, + effect = o.hide, + speed = this.speed, + args = []; + + // figure out opening effects/speeds + if( $.isArray(o.hide) ){ + effect = o.hide[0]; + speed = o.hide[1] || this.speed; + } + + if( effect ) { + args = [ effect, speed ]; + } + + $.fn.hide.apply(this.menu, args); + this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave'); + this._isOpen = false; + this._trigger('close'); + }, + + enable: function(){ + this._toggleDisabled(false); + }, + + disable: function(){ + this._toggleDisabled(true); + }, + + checkAll: function( e ){ + this._toggleChecked(true); + this._trigger('checkAll'); + }, + + uncheckAll: function(){ + this._toggleChecked(false); + this._trigger('uncheckAll'); + }, + + getChecked: function(){ + return this.menu.find('input').filter(':checked'); + }, + + destroy: function(){ + // remove classes + data + $.Widget.prototype.destroy.call( this ); + + this.button.remove(); + this.menu.remove(); + this.element.show(); + + return this; + }, + + isOpen: function(){ + return this._isOpen; + }, + + widget: function(){ + return this.menu; + }, + + getButton: function(){ + return this.button; + }, + + // react to option changes after initialization + _setOption: function( key, value ){ + var menu = this.menu; + + switch(key){ + case 'header': + menu.find('div.ui-multiselect-header')[ value ? 'show' : 'hide' ](); + break; + case 'checkAllText': + menu.find('a.ui-multiselect-all span').eq(-1).text(value); + break; + case 'uncheckAllText': + menu.find('a.ui-multiselect-none span').eq(-1).text(value); + break; + case 'height': + menu.find('ul').last().height( parseInt(value,10) ); + break; + case 'minWidth': + this.options[ key ] = parseInt(value,10); + this._setButtonWidth(); + this._setMenuWidth(); + break; + case 'selectedText': + case 'selectedList': + case 'noneSelectedText': + this.options[key] = value; // these all needs to update immediately for the update() call + this.update(); + break; + case 'classes': + menu.add(this.button).removeClass(this.options.classes).addClass(value); + break; + case 'multiple': + menu.toggleClass('ui-multiselect-single', !value); + this.options.multiple = value; + this.element[0].multiple = value; + this.refresh(); + } + + $.Widget.prototype._setOption.apply( this, arguments ); + } +}); + +})(jQuery); diff --git a/core/css/jquery.multiselect.css b/core/css/jquery.multiselect.css deleted file mode 100644 index 9b81c3bdcfb..00000000000 --- a/core/css/jquery.multiselect.css +++ /dev/null @@ -1,23 +0,0 @@ -.ui-multiselect { padding:2px 0 2px 4px; text-align:left; } -.ui-multiselect span.ui-icon { float:right; } -.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; } -.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; } - -.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px; } -.ui-multiselect-header ul { font-size:14px; } -.ui-multiselect-header ul li { float:left; padding:0 10px 0 0; } -.ui-multiselect-header a { text-decoration:none; } -.ui-multiselect-header a:hover { text-decoration:underline; } -.ui-multiselect-header span.ui-icon { float:left;} -.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0; } - -.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left; } -.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll; } -.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px; } -.ui-multiselect-checkboxes label input { position:relative; top:1px; } -.ui-multiselect-checkboxes li { clear:both; font-size:14px; padding-right:3px; } -.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid; } -.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none; } - -/* remove label borders in IE6 because IE6 does not support transparency */ -* html .ui-multiselect-checkboxes label { border:none; } diff --git a/core/js/jquery.multiselect.js b/core/js/jquery.multiselect.js deleted file mode 100644 index 16ae4264177..00000000000 --- a/core/js/jquery.multiselect.js +++ /dev/null @@ -1,705 +0,0 @@ -/* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */ -/* - * jQuery MultiSelect UI Widget 1.13 - * Copyright (c) 2012 Eric Hynds - * - * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/ - * - * Depends: - * - jQuery 1.4.2+ - * - jQuery UI 1.8 widget factory - * - * Optional: - * - jQuery UI effects - * - jQuery UI position utility - * - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * -*/ -(function($, undefined){ - -var multiselectID = 0; - -$.widget("ech.multiselect", { - - // default options - options: { - header: true, - height: 175, - minWidth: 225, - classes: '', - checkAllText: 'Check all', - uncheckAllText: 'Uncheck all', - noneSelectedText: 'Select options', - selectedText: '# selected', - selectedList: 0, - show: null, - hide: null, - autoOpen: false, - multiple: true, - position: {} - }, - - _create: function(){ - var el = this.element.hide(), - o = this.options; - - this.speed = $.fx.speeds._default; // default speed for effects - this._isOpen = false; // assume no - - var - button = (this.button = $('')) - .addClass('ui-multiselect ui-widget ui-state-default ui-corner-all') - .addClass( o.classes ) - .attr({ 'title':el.attr('title'), 'aria-haspopup':true, 'tabIndex':el.attr('tabIndex') }) - .insertAfter( el ), - - buttonlabel = (this.buttonlabel = $('')) - .html( o.noneSelectedText ) - .appendTo( button ), - - menu = (this.menu = $('
      ')) - .addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all') - .addClass( o.classes ) - .appendTo( document.body ), - - header = (this.header = $('
      ')) - .addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix') - .appendTo( menu ), - - headerLinkContainer = (this.headerLinkContainer = $('
        ')) - .addClass('ui-helper-reset') - .html(function(){ - if( o.header === true ){ - return '
      • ' + o.checkAllText + '
      • ' + o.uncheckAllText + '
      • '; - } else if(typeof o.header === "string"){ - return '
      • ' + o.header + '
      • '; - } else { - return ''; - } - }) - .append('
      • ') - .appendTo( header ), - - checkboxContainer = (this.checkboxContainer = $('
          ')) - .addClass('ui-multiselect-checkboxes ui-helper-reset') - .appendTo( menu ); - - // perform event bindings - this._bindEvents(); - - // build menu - this.refresh( true ); - - // some addl. logic for single selects - if( !o.multiple ){ - menu.addClass('ui-multiselect-single'); - } - }, - - _init: function(){ - if( this.options.header === false ){ - this.header.hide(); - } - if( !this.options.multiple ){ - this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide(); - } - if( this.options.autoOpen ){ - this.open(); - } - if( this.element.is(':disabled') ){ - this.disable(); - } - }, - - refresh: function( init ){ - var el = this.element, - o = this.options, - menu = this.menu, - checkboxContainer = this.checkboxContainer, - optgroups = [], - html = "", - id = el.attr('id') || multiselectID++; // unique ID for the label & option tags - - // build items - el.find('option').each(function( i ){ - var $this = $(this), - parent = this.parentNode, - title = this.innerHTML, - description = this.title, - value = this.value, - inputID = 'ui-multiselect-' + (this.id || id + '-option-' + i), - isDisabled = this.disabled, - isSelected = this.selected, - labelClasses = [ 'ui-corner-all' ], - liClasses = (isDisabled ? 'ui-multiselect-disabled ' : ' ') + this.className, - optLabel; - - // is this an optgroup? - if( parent.tagName === 'OPTGROUP' ){ - optLabel = parent.getAttribute( 'label' ); - - // has this optgroup been added already? - if( $.inArray(optLabel, optgroups) === -1 ){ - html += '
        • ' + optLabel + '
        • '; - optgroups.push( optLabel ); - } - } - - if( isDisabled ){ - labelClasses.push( 'ui-state-disabled' ); - } - - // browsers automatically select the first option - // by default with single selects - if( isSelected && !o.multiple ){ - labelClasses.push( 'ui-state-active' ); - } - - html += '
        • '; - - // create the label - html += '
        • '; - }); - - // insert into the DOM - checkboxContainer.html( html ); - - // cache some moar useful elements - this.labels = menu.find('label'); - this.inputs = this.labels.children('input'); - - // set widths - this._setButtonWidth(); - this._setMenuWidth(); - - // remember default value - this.button[0].defaultValue = this.update(); - - // broadcast refresh event; useful for widgets - if( !init ){ - this._trigger('refresh'); - } - }, - - // updates the button text. call refresh() to rebuild - update: function(){ - var o = this.options, - $inputs = this.inputs, - $checked = $inputs.filter(':checked'), - numChecked = $checked.length, - value; - - if( numChecked === 0 ){ - value = o.noneSelectedText; - } else { - if($.isFunction( o.selectedText )){ - value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get()); - } else if( /\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList){ - value = $checked.map(function(){ return $(this).next().html(); }).get().join(', '); - } else { - value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length); - } - } - - this.buttonlabel.html( value ); - return value; - }, - - // binds events - _bindEvents: function(){ - var self = this, button = this.button; - - function clickHandler(){ - self[ self._isOpen ? 'close' : 'open' ](); - return false; - } - - // webkit doesn't like it when you click on the span :( - button - .find('span') - .bind('click.multiselect', clickHandler); - - // button events - button.bind({ - click: clickHandler, - keypress: function( e ){ - switch(e.which){ - case 27: // esc - case 38: // up - case 37: // left - self.close(); - break; - case 39: // right - case 40: // down - self.open(); - break; - } - }, - mouseenter: function(){ - if( !button.hasClass('ui-state-disabled') ){ - $(this).addClass('ui-state-hover'); - } - }, - mouseleave: function(){ - $(this).removeClass('ui-state-hover'); - }, - focus: function(){ - if( !button.hasClass('ui-state-disabled') ){ - $(this).addClass('ui-state-focus'); - } - }, - blur: function(){ - $(this).removeClass('ui-state-focus'); - } - }); - - // header links - this.header - .delegate('a', 'click.multiselect', function( e ){ - // close link - if( $(this).hasClass('ui-multiselect-close') ){ - self.close(); - - // check all / uncheck all - } else { - self[ $(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll' ](); - } - - e.preventDefault(); - }); - - // optgroup label toggle support - this.menu - .delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function( e ){ - e.preventDefault(); - - var $this = $(this), - $inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)'), - nodes = $inputs.get(), - label = $this.parent().text(); - - // trigger event and bail if the return is false - if( self._trigger('beforeoptgrouptoggle', e, { inputs:nodes, label:label }) === false ){ - return; - } - - // toggle inputs - self._toggleChecked( - $inputs.filter(':checked').length !== $inputs.length, - $inputs - ); - - self._trigger('optgrouptoggle', e, { - inputs: nodes, - label: label, - checked: nodes[0].checked - }); - }) - .delegate('label', 'mouseenter.multiselect', function(){ - if( !$(this).hasClass('ui-state-disabled') ){ - self.labels.removeClass('ui-state-hover'); - $(this).addClass('ui-state-hover').find('input').focus(); - } - }) - .delegate('label', 'keydown.multiselect', function( e ){ - e.preventDefault(); - - switch(e.which){ - case 9: // tab - case 27: // esc - self.close(); - break; - case 38: // up - case 40: // down - case 37: // left - case 39: // right - self._traverse(e.which, this); - break; - case 13: // enter - $(this).find('input')[0].click(); - break; - } - }) - .delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function( e ){ - var $this = $(this), - val = this.value, - checked = this.checked, - tags = self.element.find('option'); - - // bail if this input is disabled or the event is cancelled - if( this.disabled || self._trigger('click', e, { value: val, text: this.title, checked: checked }) === false ){ - e.preventDefault(); - return; - } - - // make sure the input has focus. otherwise, the esc key - // won't close the menu after clicking an item. - $this.focus(); - - // toggle aria state - $this.attr('aria-selected', checked); - - // change state on the original option tags - tags.each(function(){ - if( this.value === val ){ - this.selected = checked; - } else if( !self.options.multiple ){ - this.selected = false; - } - }); - - // some additional single select-specific logic - if( !self.options.multiple ){ - self.labels.removeClass('ui-state-active'); - $this.closest('label').toggleClass('ui-state-active', checked ); - - // close menu - self.close(); - } - - // fire change on the select box - self.element.trigger("change"); - - // setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827 - // http://bugs.jquery.com/ticket/3827 - setTimeout($.proxy(self.update, self), 10); - }); - - // close each widget when clicking on any other element/anywhere else on the page - $(document).bind('mousedown.multiselect', function( e ){ - if(self._isOpen && !$.contains(self.menu[0], e.target) && !$.contains(self.button[0], e.target) && e.target !== self.button[0]){ - self.close(); - } - }); - - // deal with form resets. the problem here is that buttons aren't - // restored to their defaultValue prop on form reset, and the reset - // handler fires before the form is actually reset. delaying it a bit - // gives the form inputs time to clear. - $(this.element[0].form).bind('reset.multiselect', function(){ - setTimeout($.proxy(self.refresh, self), 10); - }); - }, - - // set button width - _setButtonWidth: function(){ - var width = this.element.outerWidth(), - o = this.options; - - if( /\d/.test(o.minWidth) && width < o.minWidth){ - width = o.minWidth; - } - - // set widths - this.button.width( width ); - }, - - // set menu width - _setMenuWidth: function(){ - var m = this.menu, - width = this.button.outerWidth()- - parseInt(m.css('padding-left'),10)- - parseInt(m.css('padding-right'),10)- - parseInt(m.css('border-right-width'),10)- - parseInt(m.css('border-left-width'),10); - - m.width( width || this.button.outerWidth() ); - }, - - // move up or down within the menu - _traverse: function( which, start ){ - var $start = $(start), - moveToLast = which === 38 || which === 37, - - // select the first li that isn't an optgroup label / disabled - $next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)')[ moveToLast ? 'last' : 'first'](); - - // if at the first/last element - if( !$next.length ){ - var $container = this.menu.find('ul').last(); - - // move to the first/last - this.menu.find('label')[ moveToLast ? 'last' : 'first' ]().trigger('mouseover'); - - // set scroll position - $container.scrollTop( moveToLast ? $container.height() : 0 ); - - } else { - $next.find('label').trigger('mouseover'); - } - }, - - // This is an internal function to toggle the checked property and - // other related attributes of a checkbox. - // - // The context of this function should be a checkbox; do not proxy it. - _toggleState: function( prop, flag ){ - return function(){ - if( !this.disabled ) { - this[ prop ] = flag; - } - - if( flag ){ - this.setAttribute('aria-selected', true); - } else { - this.removeAttribute('aria-selected'); - } - }; - }, - - _toggleChecked: function( flag, group ){ - var $inputs = (group && group.length) ? group : this.inputs, - self = this; - - // toggle state on inputs - $inputs.each(this._toggleState('checked', flag)); - - // give the first input focus - $inputs.eq(0).focus(); - - // update button text - this.update(); - - // gather an array of the values that actually changed - var values = $inputs.map(function(){ - return this.value; - }).get(); - - // toggle state on original option tags - this.element - .find('option') - .each(function(){ - if( !this.disabled && $.inArray(this.value, values) > -1 ){ - self._toggleState('selected', flag).call( this ); - } - }); - - // trigger the change event on the select - if( $inputs.length ) { - this.element.trigger("change"); - } - }, - - _toggleDisabled: function( flag ){ - this.button - .attr({ 'disabled':flag, 'aria-disabled':flag })[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled'); - - var inputs = this.menu.find('input'); - var key = "ech-multiselect-disabled"; - - if(flag) { - // remember which elements this widget disabled (not pre-disabled) - // elements, so that they can be restored if the widget is re-enabled. - inputs = inputs.filter(':enabled') - .data(key, true) - } else { - inputs = inputs.filter(function() { - return $.data(this, key) === true; - }).removeData(key); - } - - inputs - .attr({ 'disabled':flag, 'arial-disabled':flag }) - .parent()[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled'); - - this.element - .attr({ 'disabled':flag, 'aria-disabled':flag }); - }, - - // open the menu - open: function( e ){ - var self = this, - button = this.button, - menu = this.menu, - speed = this.speed, - o = this.options, - args = []; - - // bail if the multiselectopen event returns false, this widget is disabled, or is already open - if( this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen ){ - return; - } - - var $container = menu.find('ul').last(), - effect = o.show, - pos = button.offset(); - - // figure out opening effects/speeds - if( $.isArray(o.show) ){ - effect = o.show[0]; - speed = o.show[1] || self.speed; - } - - // if there's an effect, assume jQuery UI is in use - // build the arguments to pass to show() - if( effect ) { - args = [ effect, speed ]; - } - - // set the scroll of the checkbox container - $container.scrollTop(0).height(o.height); - - // position and show menu - if( $.ui.position && !$.isEmptyObject(o.position) ){ - o.position.of = o.position.of || button; - - menu - .show() - .position( o.position ) - .hide(); - - // if position utility is not available... - } else { - menu.css({ - top: pos.top + button.outerHeight(), - left: pos.left - }); - } - - // show the menu, maybe with a speed/effect combo - $.fn.show.apply(menu, args); - - // select the first option - // triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover - // will actually trigger mouseenter. the mouseenter trigger is there for when it's eventually fixed - this.labels.eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus'); - - button.addClass('ui-state-active'); - this._isOpen = true; - this._trigger('open'); - }, - - // close the menu - close: function(){ - if(this._trigger('beforeclose') === false){ - return; - } - - var o = this.options, - effect = o.hide, - speed = this.speed, - args = []; - - // figure out opening effects/speeds - if( $.isArray(o.hide) ){ - effect = o.hide[0]; - speed = o.hide[1] || this.speed; - } - - if( effect ) { - args = [ effect, speed ]; - } - - $.fn.hide.apply(this.menu, args); - this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave'); - this._isOpen = false; - this._trigger('close'); - }, - - enable: function(){ - this._toggleDisabled(false); - }, - - disable: function(){ - this._toggleDisabled(true); - }, - - checkAll: function( e ){ - this._toggleChecked(true); - this._trigger('checkAll'); - }, - - uncheckAll: function(){ - this._toggleChecked(false); - this._trigger('uncheckAll'); - }, - - getChecked: function(){ - return this.menu.find('input').filter(':checked'); - }, - - destroy: function(){ - // remove classes + data - $.Widget.prototype.destroy.call( this ); - - this.button.remove(); - this.menu.remove(); - this.element.show(); - - return this; - }, - - isOpen: function(){ - return this._isOpen; - }, - - widget: function(){ - return this.menu; - }, - - getButton: function(){ - return this.button; - }, - - // react to option changes after initialization - _setOption: function( key, value ){ - var menu = this.menu; - - switch(key){ - case 'header': - menu.find('div.ui-multiselect-header')[ value ? 'show' : 'hide' ](); - break; - case 'checkAllText': - menu.find('a.ui-multiselect-all span').eq(-1).text(value); - break; - case 'uncheckAllText': - menu.find('a.ui-multiselect-none span').eq(-1).text(value); - break; - case 'height': - menu.find('ul').last().height( parseInt(value,10) ); - break; - case 'minWidth': - this.options[ key ] = parseInt(value,10); - this._setButtonWidth(); - this._setMenuWidth(); - break; - case 'selectedText': - case 'selectedList': - case 'noneSelectedText': - this.options[key] = value; // these all needs to update immediately for the update() call - this.update(); - break; - case 'classes': - menu.add(this.button).removeClass(this.options.classes).addClass(value); - break; - case 'multiple': - menu.toggleClass('ui-multiselect-single', !value); - this.options.multiple = value; - this.element[0].multiple = value; - this.refresh(); - } - - $.Widget.prototype._setOption.apply( this, arguments ); - } -}); - -})(jQuery); -- cgit v1.2.3