|
| 1 | +/* |
| 2 | + * jQuery gentleSelect plugin |
| 3 | + * http://shawnchin.github.com/jquery-gentleSelect |
| 4 | + * |
| 5 | + * Copyright (c) 2010 Shawn Chin. |
| 6 | + * Licensed under the MIT license. |
| 7 | + * |
| 8 | + * Usage: |
| 9 | + * (JS) |
| 10 | + * |
| 11 | + * $('#myselect').gentleSelect(); // default. single column |
| 12 | + * |
| 13 | + * $('#myselect').gentleSelect({ // 3 columns, 150px wide each |
| 14 | + * itemWidth : 150, |
| 15 | + * columns : 3, |
| 16 | + * }); |
| 17 | + * |
| 18 | + * (HTML) |
| 19 | + * <select id='myselect'><options> .... </options></select> |
| 20 | + * |
| 21 | + */ |
| 22 | +(function($) { |
| 23 | + |
| 24 | + var defaults = { |
| 25 | + minWidth : 100, // only applies if columns and itemWidth not set |
| 26 | + itemWidth : undefined, |
| 27 | + columns : undefined, |
| 28 | + rows : undefined, |
| 29 | + title : undefined, |
| 30 | + openSpeed : 400, |
| 31 | + closeSpeed : 400, |
| 32 | + openEffect : "slide", |
| 33 | + closeEffect : "slide", |
| 34 | + hideOnMouseOut : true |
| 35 | + } |
| 36 | + |
| 37 | + function defined(obj) { |
| 38 | + if (typeof obj == "undefined") { return false; } |
| 39 | + else { return true; } |
| 40 | + } |
| 41 | + |
| 42 | + function hasError(c, o) { |
| 43 | + if (c.attr("multiple") == true) { |
| 44 | + $.error("Sorry, gentleSelect does not work with multiple=true yet"); |
| 45 | + return true; |
| 46 | + } |
| 47 | + if (defined(o.columns) && defined(o.rows)) { |
| 48 | + $.error("gentleSelect: You cannot supply both 'rows' and 'columns'"); |
| 49 | + return true; |
| 50 | + } |
| 51 | + if (defined(o.columns) && !defined(o.itemWidth)) { |
| 52 | + $.error("gentleSelect: itemWidth must be supplied if 'columns' is specified"); |
| 53 | + return true; |
| 54 | + } |
| 55 | + if (defined(o.rows) && !defined(o.itemWidth)) { |
| 56 | + $.error("gentleSelect: itemWidth must be supplied if 'rows' is specified"); |
| 57 | + return true; |
| 58 | + } |
| 59 | + if (!defined(o.openSpeed) || typeof o.openSpeed != "number" && |
| 60 | + (typeof o.openSpeed == "string" && (o.openSpeed != "slow" && o.openSpeed != "fast"))) { |
| 61 | + $.error("gentleSelect: openSpeed must be an integer or \"slow\" or \"fast\""); |
| 62 | + return true; |
| 63 | + } |
| 64 | + if (!defined(o.closeSpeed) || typeof o.closeSpeed != "number" && |
| 65 | + (typeof o.closeSpeed == "string" && (o.closeSpeed != "slow" && o.closeSpeed != "fast"))) { |
| 66 | + $.error("gentleSelect: closeSpeed must be an integer or \"slow\" or \"fast\""); |
| 67 | + return true; |
| 68 | + } |
| 69 | + if (!defined(o.openEffect) || (o.openEffect != "fade" && o.openEffect != "slide")) { |
| 70 | + $.error("gentleSelect: openEffect must be either 'fade' or 'slide'!"); |
| 71 | + return true; |
| 72 | + } |
| 73 | + if (!defined(o.closeEffect)|| (o.closeEffect != "fade" && o.closeEffect != "slide")) { |
| 74 | + $.error("gentleSelect: closeEffect must be either 'fade' or 'slide'!"); |
| 75 | + return true; |
| 76 | + } |
| 77 | + if (!defined(o.hideOnMouseOut) || (typeof o.hideOnMouseOut != "boolean")) { |
| 78 | + $.error("gentleSelect: hideOnMouseOut must be supplied and either \"true\" or \"false\"!"); |
| 79 | + return true; |
| 80 | + } |
| 81 | + return false; |
| 82 | + } |
| 83 | + |
| 84 | + var methods = { |
| 85 | + init : function(options) { |
| 86 | + var o = $.extend({}, defaults, options); |
| 87 | + |
| 88 | + if (hasError(this, o)) { return this; }; // check for errors |
| 89 | + this.hide(); // hide original select box |
| 90 | + |
| 91 | + // initialise <span> to replace select box |
| 92 | + var label = $("<span class='gentleselect-label'>" + this.find(":selected").text() + "</span>") |
| 93 | + .insertBefore(this) |
| 94 | + .bind("mouseenter.gentleselect", event_handlers.labelHoverIn) |
| 95 | + .bind("mouseleave.gentleselect", event_handlers.labelHoverOut) |
| 96 | + .bind("click.gentleselect", event_handlers.labelClick) |
| 97 | + .data("root", this); |
| 98 | + this.data("label", label) |
| 99 | + .data("options", o); |
| 100 | + |
| 101 | + // generate list of options |
| 102 | + var ul = $("<ul></ul>"); |
| 103 | + this.find("option").each(function() { |
| 104 | + var li = $("<li>" + $(this).text() + "</li>") |
| 105 | + .data("value", $(this).attr("value")) |
| 106 | + .data("name", $(this).text()) |
| 107 | + .appendTo(ul); |
| 108 | + if ($(this).attr("selected")) { li.addClass("selected"); } |
| 109 | + }); |
| 110 | + |
| 111 | + // build dialog box |
| 112 | + var dialog = $("<div class='gentleselect-dialog'></div>") |
| 113 | + .append(ul) |
| 114 | + .insertAfter(label) |
| 115 | + .bind("click.gentleselect", event_handlers.dialogClick) |
| 116 | + .bind("mouseleave.gentleselect", event_handlers.dialogHoverOut) |
| 117 | + .data("label", label) |
| 118 | + .data("root", this); |
| 119 | + this.data("dialog", dialog); |
| 120 | + |
| 121 | + // if to be displayed in columns |
| 122 | + if (defined(o.columns) || defined(o.rows)) { |
| 123 | + |
| 124 | + // Update CSS |
| 125 | + ul.css("float", "left") |
| 126 | + .find("li").width(o.itemWidth).css("float","left"); |
| 127 | + |
| 128 | + var f = ul.find("li:first"); |
| 129 | + var actualWidth = o.itemWidth |
| 130 | + + parseInt(f.css("padding-left")) |
| 131 | + + parseInt(f.css("padding-right")); |
| 132 | + var elemCount = ul.find("li").length; |
| 133 | + if (defined(o.columns)) { |
| 134 | + var cols = parseInt(o.columns); |
| 135 | + var rows = Math.ceil(elemCount / cols); |
| 136 | + } else { |
| 137 | + var rows = parseInt(o.rows); |
| 138 | + var cols = Math.ceil(elemCount / rows); |
| 139 | + } |
| 140 | + dialog.width(actualWidth * cols); |
| 141 | + |
| 142 | + // add padding |
| 143 | + for (var i = 0; i < (rows * cols) - elemCount; i++) { |
| 144 | + $("<li style='float:left' class='gentleselect-dummy'><span> </span></li>").appendTo(ul); |
| 145 | + } |
| 146 | + |
| 147 | + // reorder elements |
| 148 | + var ptr = []; |
| 149 | + var idx = 0; |
| 150 | + ul.find("li").each(function() { |
| 151 | + if (idx < rows) { |
| 152 | + ptr[idx] = $(this); |
| 153 | + } else { |
| 154 | + var p = idx % rows; |
| 155 | + $(this).insertAfter(ptr[p]); |
| 156 | + ptr[p] = $(this); |
| 157 | + } |
| 158 | + idx++; |
| 159 | + }); |
| 160 | + } else if (typeof o.minWidth == "number") { |
| 161 | + dialog.css("min-width", o.minWidth); |
| 162 | + } |
| 163 | + |
| 164 | + if (defined(o.title)) { |
| 165 | + $("<div class='gentleselect-title'>" + o.title + "</div>").prependTo(dialog); |
| 166 | + } |
| 167 | + |
| 168 | + // ESC key should hide all dialog boxes |
| 169 | + $(document).bind("keyup.gentleselect", event_handlers.keyUp); |
| 170 | + |
| 171 | + return this; |
| 172 | + }, |
| 173 | + |
| 174 | + // if select box was updated externally, we need to bring everything |
| 175 | + // else up to speed. |
| 176 | + update : function() { |
| 177 | + var root = this; |
| 178 | + var v = this.val(); // current value of select box |
| 179 | + this.data("dialog").find("li").each(function() { |
| 180 | + if ($(this).data("value") == v) { |
| 181 | + $(this).addClass("selected"); |
| 182 | + root.data("label").text($(this).data("name")); |
| 183 | + } else { |
| 184 | + $(this).removeClass("selected"); |
| 185 | + }; |
| 186 | + }); |
| 187 | + return this; |
| 188 | + } |
| 189 | + }; |
| 190 | + |
| 191 | + var event_handlers = { |
| 192 | + |
| 193 | + labelHoverIn : function() { |
| 194 | + $(this).addClass('gentleselect-label-highlight'); |
| 195 | + }, |
| 196 | + |
| 197 | + labelHoverOut : function() { |
| 198 | + $(this).removeClass('gentleselect-label-highlight'); |
| 199 | + }, |
| 200 | + |
| 201 | + labelClick : function() { |
| 202 | + var pos = $(this).position(); |
| 203 | + var root = $(this).data("root"); |
| 204 | + var opts = root.data("options"); |
| 205 | + var dialog = root.data("dialog") |
| 206 | + .css("top", pos.top + root.height()) |
| 207 | + .css("left", pos.left + 1); |
| 208 | + if (opts.openEffect == "fade") { |
| 209 | + dialog.fadeIn(opts.openSpeed); |
| 210 | + } else { |
| 211 | + dialog.slideDown(opts.openSpeed); |
| 212 | + } |
| 213 | + }, |
| 214 | + |
| 215 | + dialogHoverOut : function() { |
| 216 | + if ($(this).data("root").data("options").hideOnMouseOut) { |
| 217 | + $(this).hide(); |
| 218 | + } |
| 219 | + }, |
| 220 | + |
| 221 | + dialogClick : function(e) { |
| 222 | + var clicked = $(e.target); |
| 223 | + var opts = $(this).data("root").data("options"); |
| 224 | + if (opts.closeEffect == "fade") { |
| 225 | + $(this).fadeOut(opts.closeSpeed); |
| 226 | + } else { |
| 227 | + $(this).slideUp(opts.closeSpeed); |
| 228 | + } |
| 229 | + |
| 230 | + if (clicked.is("li") && !clicked.hasClass("gentleselect-dummy")) { |
| 231 | + var value = clicked.data("value"); |
| 232 | + var name = clicked.data("name"); |
| 233 | + var label = $(this).data("label") |
| 234 | + .text(name); // update label |
| 235 | + |
| 236 | + // update selected li |
| 237 | + $(this).find("li.selected").removeClass("selected"); |
| 238 | + clicked.addClass("selected"); |
| 239 | + // update actual selectbox |
| 240 | + var actual = $(this).data("root").val(value).trigger("change"); |
| 241 | + |
| 242 | + } |
| 243 | + }, |
| 244 | + |
| 245 | + keyUp : function(e) { |
| 246 | + if (e.keyCode == 27 ) { // ESC |
| 247 | + $(".gentleselect-dialog").hide(); |
| 248 | + } |
| 249 | + } |
| 250 | + }; |
| 251 | + |
| 252 | + $.fn.gentleSelect = function(method) { |
| 253 | + if (methods[method]) { |
| 254 | + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); |
| 255 | + } else if (typeof method === 'object' || ! method) { |
| 256 | + return methods.init.apply(this, arguments); |
| 257 | + } else { |
| 258 | + $.error( 'Method ' + method + ' does not exist on jQuery.gentleSelect' ); |
| 259 | + } |
| 260 | + }; |
| 261 | + |
| 262 | + |
| 263 | +})(jQuery); |
0 commit comments