From 6cc81d6a634bb0aa643c13cfad7cb88d85fed943 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Tue, 13 Aug 2013 16:58:06 +0200 Subject: [PATCH] Bug 9043: Add the dropdown check list jquery plugin --- .../en/css/ui.dropdownchecklist.themeroller.css | 29 + .../en/lib/jquery/plugins/ui.dropdownchecklist.js | 917 ++++++++++++++++++++ .../prog/en/modules/admin/preferences.tt | 16 +- 3 files changed, 953 insertions(+), 9 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/css/ui.dropdownchecklist.themeroller.css create mode 100644 koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/ui.dropdownchecklist.js diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/ui.dropdownchecklist.themeroller.css b/koha-tmpl/intranet-tmpl/prog/en/css/ui.dropdownchecklist.themeroller.css new file mode 100644 index 0000000..2bdfce9 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/css/ui.dropdownchecklist.themeroller.css @@ -0,0 +1,29 @@ +/** Simple modifications needed for DropDownCheckList to take advantage of ThemeRoller settings */ +.ui-dropdownchecklist .ui-widget-content +, .ui-dropdownchecklist .ui-widget-header { + border: none; +} +.ui-dropdownchecklist-indent { + padding-left: 7px; +} +/* Font size of 0 on the -selector and an explicit medium on -text required to eliminate + descender problems within the containers and still have a valid size for the text */ +.ui-dropdownchecklist-selector-wrapper +, .ui-widget.ui-dropdownchecklist-selector-wrapper { + vertical-align: middle; + font-size: 0px; +} +.ui-dropdownchecklist-selector { + padding: 1px 2px 2px 2px; + font-size: 0px; +} +.ui-dropdownchecklist-text { + font-size: medium; +} +.ui-dropdownchecklist-item +, .ui-dropdownchecklist-item input { + vertical-align: middle; +} +.ui-dropdownchecklist-group { + padding: 1px 2px 2px 2px; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/ui.dropdownchecklist.js b/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/ui.dropdownchecklist.js new file mode 100644 index 0000000..94a30b8 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/ui.dropdownchecklist.js @@ -0,0 +1,917 @@ +;(function($) { +/* + * ui.dropdownchecklist + * + * Copyright (c) 2008-2010 Adrian Tosca, Copyright (c) 2010-2011 Ittrium LLC + * Dual licensed under the MIT (MIT-LICENSE.txt) OR GPL (GPL-LICENSE.txt) licenses. + * +*/ + // The dropdown check list jQuery plugin transforms a regular select html element into a dropdown check list. + $.widget("ui.dropdownchecklist", { + // Some globlals + // $.ui.dropdownchecklist.gLastOpened - keeps track of last opened dropdowncheck list so we can close it + // $.ui.dropdownchecklist.gIDCounter - simple counter to provide a unique ID as needed + version: function() { + alert('DropDownCheckList v1.4'); + }, + // Creates the drop container that keeps the items and appends it to the document + _appendDropContainer: function( controlItem ) { + var wrapper = $("
"); + // the container is wrapped in a div + wrapper.addClass("ui-dropdownchecklist ui-dropdownchecklist-dropcontainer-wrapper"); + wrapper.addClass("ui-widget"); + // assign an id + wrapper.attr("id",controlItem.attr("id") + '-ddw'); + // initially positioned way off screen to prevent it from displaying + // NOTE absolute position to enable width/height calculation + wrapper.css({ position: 'absolute', left: "-33000px", top: "-33000px" }); + + var container = $("
"); // the actual container + container.addClass("ui-dropdownchecklist-dropcontainer ui-widget-content"); + container.css("overflow-y", "auto"); + wrapper.append(container); + + // insert the dropdown after the master control to try to keep the tab order intact + // if you just add it to the end, tabbing out of the drop down takes focus off the page + // @todo 22Sept2010 - check if size calculation is thrown off if the parent of the + // selector is hidden. We may need to add it to the end of the document here, + // calculate the size, and then move it back into proper position??? + //$(document.body).append(wrapper); + wrapper.insertAfter(controlItem); + + // flag that tells if the drop container is shown or not + wrapper.isOpen = false; + return wrapper; + }, + // Look for browser standard 'open' on a closed selector + _isDropDownKeyShortcut: function(e,keycode) { + return e.altKey && ($.ui.keyCode.DOWN == keycode);// Alt + Down Arrow + }, + // Look for key that will tell us to close the open dropdown + _isDropDownCloseKey: function(e,keycode) { + return ($.ui.keyCode.ESCAPE == keycode) || ($.ui.keyCode.ENTER == keycode); + }, + // Handler to change the active focus based on a keystroke, moving some count of + // items from the element that has the current focus + _keyFocusChange: function(target,delta,limitToItems) { + // Find item with current focus + var focusables = $(":focusable"); + var index = focusables.index(target); + if ( index >= 0 ) { + index += delta; + if ( limitToItems ) { + // Bound change to list of input elements + var allCheckboxes = this.dropWrapper.find("input:not([disabled])"); + var firstIndex = focusables.index(allCheckboxes.get(0)); + var lastIndex = focusables.index(allCheckboxes.get(allCheckboxes.length-1)); + if ( index < firstIndex ) { + index = lastIndex; + } else if ( index > lastIndex ) { + index = firstIndex; + } + } + focusables.get(index).focus(); + } + }, + // Look for navigation, open, close (wired to keyup) + _handleKeyboard: function(e) { + var self = this; + var keyCode = (e.keyCode || e.which); + if (!self.dropWrapper.isOpen && self._isDropDownKeyShortcut(e, keyCode)) { + // Key command to open the dropdown + e.stopImmediatePropagation(); + self._toggleDropContainer(true); + } else if (self.dropWrapper.isOpen && self._isDropDownCloseKey(e, keyCode)) { + // Key command to close the dropdown (but we retain focus in the control) + e.stopImmediatePropagation(); + self._toggleDropContainer(false); + self.controlSelector.focus(); + } else if (self.dropWrapper.isOpen + && (e.target.type == 'checkbox') + && ((keyCode == $.ui.keyCode.DOWN) || (keyCode == $.ui.keyCode.UP)) ) { + // Up/Down to cycle throught the open items + e.stopImmediatePropagation(); + self._keyFocusChange(e.target, (keyCode == $.ui.keyCode.DOWN) ? 1 : -1, true); + } else if (self.dropWrapper.isOpen && (keyCode == $.ui.keyCode.TAB) ) { + // I wanted to adjust normal 'tab' processing here, but research indicates + // that TAB key processing is NOT a cancelable event. You have to use a timer + // hack to pull the focus back to where you want it after browser tab + // processing completes. Not going to work for us. + //e.stopImmediatePropagation(); + //self._keyFocusChange(e.target, (e.shiftKey) ? -1 : 1, true); + } + }, + // Look for change of focus + _handleFocus: function(e,focusIn,forDropdown) { + var self = this; + if (forDropdown && !self.dropWrapper.isOpen) { + // if the focus changes when the control is NOT open, mark it to show where the focus is/is not + e.stopImmediatePropagation(); + if (focusIn) { + self.controlSelector.addClass("ui-state-hover"); + if ($.ui.dropdownchecklist.gLastOpened != null) { + $.ui.dropdownchecklist.gLastOpened._toggleDropContainer( false ); + } + } else { + self.controlSelector.removeClass("ui-state-hover"); + } + } else if (!forDropdown && !focusIn) { + // The dropdown is open, and an item (NOT the dropdown) has just lost the focus. + // we really need a reliable method to see who has the focus as we process the blur, + // but that mechanism does not seem to exist. Instead we rely on a delay before + // posting the blur, with a focus event cancelling it before the delay expires. + if ( e != null ) { e.stopImmediatePropagation(); } + self.controlSelector.removeClass("ui-state-hover"); + self._toggleDropContainer( false ); + } + }, + // Clear the pending change of focus, which keeps us 'in' the control + _cancelBlur: function(e) { + var self = this; + if (self.blurringItem != null) { + clearTimeout(self.blurringItem); + self.blurringItem = null; + } + }, + // Creates the control that will replace the source select and appends it to the document + // The control resembles a regular select with single selection + _appendControl: function() { + var self = this, sourceSelect = this.sourceSelect, options = this.options; + + // the control is wrapped in a basic container + // inline-block at this level seems to give us better size control + var wrapper = $(""); + wrapper.addClass("ui-dropdownchecklist ui-dropdownchecklist-selector-wrapper ui-widget"); + wrapper.css( { display: "inline-block", cursor: "default", overflow: "hidden" } ); + + // assign an ID + var baseID = sourceSelect.attr("id"); + if ((baseID == null) || (baseID == "")) { + baseID = "ddcl-" + $.ui.dropdownchecklist.gIDCounter++; + } else { + baseID = "ddcl-" + baseID; + } + wrapper.attr("id",baseID); + + // the actual control which you can style + // inline-block needed to enable 'width' but has interesting problems cross browser + var control = $(""); + control.addClass("ui-dropdownchecklist-selector ui-state-default"); + control.css( { display: "inline-block", overflow: "hidden", 'white-space': 'nowrap'} ); + // Setting a tab index means we are interested in the tab sequence + var tabIndex = sourceSelect.attr("tabIndex"); + if ( tabIndex == null ) { + tabIndex = 0; + } else { + tabIndex = parseInt(tabIndex); + if ( tabIndex < 0 ) { + tabIndex = 0; + } + } + control.attr("tabIndex", tabIndex); + control.keyup(function(e) {self._handleKeyboard(e);}); + control.focus(function(e) {self._handleFocus(e,true,true);}); + control.blur(function(e) {self._handleFocus(e,false,true);}); + wrapper.append(control); + + // the optional icon (which is inherently a block) which we can float + if (options.icon != null) { + var iconPlacement = (options.icon.placement == null) ? "left" : options.icon.placement; + var anIcon = $("
"); + anIcon.addClass("ui-icon"); + anIcon.addClass( (options.icon.toOpen != null) ? options.icon.toOpen : "ui-icon-triangle-1-e"); + anIcon.css({ 'float': iconPlacement }); + control.append(anIcon); + } + // the text container keeps the control text that is built from the selected (checked) items + // inline-block needed to prevent long text from wrapping to next line when icon is active + var textContainer = $(""); + textContainer.addClass("ui-dropdownchecklist-text"); + textContainer.css( { display: "inline-block", 'white-space': "nowrap", overflow: "hidden" } ); + control.append(textContainer); + + // add the hover styles to the control + wrapper.hover( + function() { + if (!self.disabled) { + control.addClass("ui-state-hover"); + } + } + , function() { + if (!self.disabled) { + control.removeClass("ui-state-hover"); + } + } + ); + // clicking on the control toggles the drop container + wrapper.click(function(event) { + if (!self.disabled) { + event.stopImmediatePropagation(); + self._toggleDropContainer( !self.dropWrapper.isOpen ); + } + }); + wrapper.insertAfter(sourceSelect); + + // Watch for a window resize and adjust the control if open + $(window).resize(function() { + if (!self.disabled && self.dropWrapper.isOpen) { + // Reopen yourself to get the position right + self._toggleDropContainer(true); + } + }); + return wrapper; + }, + // Creates a drop item that coresponds to an option element in the source select + _createDropItem: function(index, tabIndex, value, text, optCss, checked, disabled, indent) { + var self = this, options = this.options, sourceSelect = this.sourceSelect, controlWrapper = this.controlWrapper; + // the item contains a div that contains a checkbox input and a lable for the text + // the div + var item = $("
"); + item.addClass("ui-dropdownchecklist-item"); + item.css({'white-space': "nowrap"}); + var checkedString = checked ? ' checked="checked"' : ''; + var classString = disabled ? ' class="inactive"' : ' class="active"'; + + // generated id must be a bit unique to keep from colliding + var idBase = controlWrapper.attr("id"); + var id = idBase + '-i' + index; + var checkBox; + + // all items start out disabled to keep them out of the tab order + if (self.isMultiple) { // the checkbox + checkBox = $(''); + } else { // the radiobutton + checkBox = $(''); + } + checkBox = checkBox.attr("index", index).val(value); + item.append(checkBox); + + // the text + var label = $("