301 lines
11 KiB
JavaScript
301 lines
11 KiB
JavaScript
/*
|
|
* jQuery gentleSelect plugin
|
|
* http://shawnchin.github.com/jquery-gentleSelect
|
|
*
|
|
* Copyright (c) 2010 Shawn Chin.
|
|
* Licensed under the MIT license.
|
|
*
|
|
* Usage:
|
|
* (JS)
|
|
*
|
|
* $('#myselect').gentleSelect(); // default. single column
|
|
*
|
|
* $('#myselect').gentleSelect({ // 3 columns, 150px wide each
|
|
* itemWidth : 150,
|
|
* columns : 3,
|
|
* });
|
|
*
|
|
* (HTML)
|
|
* <select id='myselect'><options> .... </options></select>
|
|
*
|
|
*/
|
|
(function($) {
|
|
|
|
var defaults = {
|
|
minWidth : 100, // only applies if columns and itemWidth not set
|
|
itemWidth : undefined,
|
|
columns : undefined,
|
|
rows : undefined,
|
|
title : undefined,
|
|
prompt : "Make A Selection",
|
|
maxDisplay: 0, // set to 0 for unlimited
|
|
openSpeed : 400,
|
|
closeSpeed : 400,
|
|
openEffect : "slide",
|
|
closeEffect : "slide",
|
|
hideOnMouseOut : true
|
|
}
|
|
|
|
function defined(obj) {
|
|
if (typeof obj == "undefined") { return false; }
|
|
else { return true; }
|
|
}
|
|
|
|
function hasError(c, o) {
|
|
if (defined(o.columns) && defined(o.rows)) {
|
|
$.error("gentleSelect: You cannot supply both 'rows' and 'columns'");
|
|
return true;
|
|
}
|
|
if (defined(o.columns) && !defined(o.itemWidth)) {
|
|
$.error("gentleSelect: itemWidth must be supplied if 'columns' is specified");
|
|
return true;
|
|
}
|
|
if (defined(o.rows) && !defined(o.itemWidth)) {
|
|
$.error("gentleSelect: itemWidth must be supplied if 'rows' is specified");
|
|
return true;
|
|
}
|
|
if (!defined(o.openSpeed) || typeof o.openSpeed != "number" &&
|
|
(typeof o.openSpeed == "string" && (o.openSpeed != "slow" && o.openSpeed != "fast"))) {
|
|
$.error("gentleSelect: openSpeed must be an integer or \"slow\" or \"fast\"");
|
|
return true;
|
|
}
|
|
if (!defined(o.closeSpeed) || typeof o.closeSpeed != "number" &&
|
|
(typeof o.closeSpeed == "string" && (o.closeSpeed != "slow" && o.closeSpeed != "fast"))) {
|
|
$.error("gentleSelect: closeSpeed must be an integer or \"slow\" or \"fast\"");
|
|
return true;
|
|
}
|
|
if (!defined(o.openEffect) || (o.openEffect != "fade" && o.openEffect != "slide")) {
|
|
$.error("gentleSelect: openEffect must be either 'fade' or 'slide'!");
|
|
return true;
|
|
}
|
|
if (!defined(o.closeEffect)|| (o.closeEffect != "fade" && o.closeEffect != "slide")) {
|
|
$.error("gentleSelect: closeEffect must be either 'fade' or 'slide'!");
|
|
return true;
|
|
}
|
|
if (!defined(o.hideOnMouseOut) || (typeof o.hideOnMouseOut != "boolean")) {
|
|
$.error("gentleSelect: hideOnMouseOut must be supplied and either \"true\" or \"false\"!");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function optionOverrides(c, o) {
|
|
if (c.attr("multiple")) {
|
|
o.hideOnMouseOut = true; // must be true or dialog will never hide
|
|
}
|
|
}
|
|
|
|
function getSelectedAsText(elemList, opts) {
|
|
// If no items selected, return prompt
|
|
if (elemList.length < 1) { return opts.prompt; }
|
|
|
|
// Truncate if exceed maxDisplay
|
|
if (opts.maxDisplay != 0 && elemList.length > opts.maxDisplay) {
|
|
var arr = elemList.slice(0, opts.maxDisplay).map(function(){return $(this).text();});
|
|
arr.push("...");
|
|
} else {
|
|
var arr = elemList.map(function(){return $(this).text();});
|
|
}
|
|
return arr.get().join(", ");
|
|
}
|
|
|
|
var methods = {
|
|
init : function(options) {
|
|
var o = $.extend({}, defaults, options);
|
|
|
|
if (hasError(this, o)) { return this; }; // check for errors
|
|
optionOverrides(this, o); //
|
|
this.hide(); // hide original select box
|
|
|
|
// initialise <span> to replace select box
|
|
label_text = getSelectedAsText(this.find(":selected"), o);
|
|
var label = $("<span class='gentleselect-label'>" + label_text + "</span>")
|
|
.insertBefore(this)
|
|
.bind("mouseenter.gentleselect", event_handlers.labelHoverIn)
|
|
.bind("mouseleave.gentleselect", event_handlers.labelHoverOut)
|
|
.bind("click.gentleselect", event_handlers.labelClick)
|
|
.data("root", this);
|
|
this.data("label", label)
|
|
.data("options", o);
|
|
|
|
// generate list of options
|
|
var ul = $("<ul></ul>");
|
|
this.find("option").each(function() {
|
|
var li = $("<li>" + $(this).text() + "</li>")
|
|
.data("value", $(this).attr("value"))
|
|
.data("name", $(this).text())
|
|
.appendTo(ul);
|
|
if ($(this).attr("selected")) { li.addClass("selected"); }
|
|
});
|
|
|
|
// build dialog box
|
|
var dialog = $("<div class='gentleselect-dialog'></div>")
|
|
.append(ul)
|
|
.insertAfter(label)
|
|
.bind("click.gentleselect", event_handlers.dialogClick)
|
|
.bind("mouseleave.gentleselect", event_handlers.dialogHoverOut)
|
|
.data("label", label)
|
|
.data("root", this);
|
|
this.data("dialog", dialog);
|
|
|
|
// if to be displayed in columns
|
|
if (defined(o.columns) || defined(o.rows)) {
|
|
|
|
// Update CSS
|
|
ul.css("float", "left")
|
|
.find("li").width(o.itemWidth).css("float","left");
|
|
|
|
var f = ul.find("li:first");
|
|
var actualWidth = o.itemWidth
|
|
+ parseInt(f.css("padding-left"))
|
|
+ parseInt(f.css("padding-right"));
|
|
var elemCount = ul.find("li").length;
|
|
if (defined(o.columns)) {
|
|
var cols = parseInt(o.columns);
|
|
var rows = Math.ceil(elemCount / cols);
|
|
} else {
|
|
var rows = parseInt(o.rows);
|
|
var cols = Math.ceil(elemCount / rows);
|
|
}
|
|
dialog.width(actualWidth * cols);
|
|
|
|
// add padding
|
|
for (var i = 0; i < (rows * cols) - elemCount; i++) {
|
|
$("<li style='float:left' class='gentleselect-dummy'><span> </span></li>").appendTo(ul);
|
|
}
|
|
|
|
// reorder elements
|
|
var ptr = [];
|
|
var idx = 0;
|
|
ul.find("li").each(function() {
|
|
if (idx < rows) {
|
|
ptr[idx] = $(this);
|
|
} else {
|
|
var p = idx % rows;
|
|
$(this).insertAfter(ptr[p]);
|
|
ptr[p] = $(this);
|
|
}
|
|
idx++;
|
|
});
|
|
} else if (typeof o.minWidth == "number") {
|
|
dialog.css("min-width", o.minWidth);
|
|
}
|
|
|
|
if (defined(o.title)) {
|
|
$("<div class='gentleselect-title'>" + o.title + "</div>").prependTo(dialog);
|
|
}
|
|
|
|
// ESC key should hide all dialog boxes
|
|
$(document).bind("keyup.gentleselect", event_handlers.keyUp);
|
|
|
|
return this;
|
|
},
|
|
|
|
// if select box was updated externally, we need to bring everything
|
|
// else up to speed.
|
|
update : function() {
|
|
var opts = this.data("options");
|
|
|
|
// Update li with selected data
|
|
var v = (this.attr("multiple")) ? this.val() : [this.val()];
|
|
$("li", this.data("dialog")).each(function() {
|
|
var $li = $(this);
|
|
var isSelected = ($.inArray($li.data("value"), v) != -1);
|
|
$li.toggleClass("selected", isSelected);
|
|
});
|
|
|
|
// Update label
|
|
var label = getSelectedAsText(this.find(":selected"), opts);
|
|
this.data("label").text(label);
|
|
|
|
return this;
|
|
}
|
|
};
|
|
|
|
var event_handlers = {
|
|
|
|
labelHoverIn : function() {
|
|
$(this).addClass('gentleselect-label-highlight');
|
|
},
|
|
|
|
labelHoverOut : function() {
|
|
$(this).removeClass('gentleselect-label-highlight');
|
|
},
|
|
|
|
labelClick : function() {
|
|
var $this = $(this);
|
|
var pos = $this.position();
|
|
var root = $this.data("root");
|
|
var opts = root.data("options");
|
|
var dialog = root.data("dialog")
|
|
.css("top", pos.top + $this.height())
|
|
.css("left", pos.left + 1);
|
|
if (opts.openEffect == "fade") {
|
|
dialog.fadeIn(opts.openSpeed);
|
|
} else {
|
|
dialog.slideDown(opts.openSpeed);
|
|
}
|
|
},
|
|
|
|
dialogHoverOut : function() {
|
|
var $this = $(this);
|
|
if ($this.data("root").data("options").hideOnMouseOut) {
|
|
$this.hide();
|
|
}
|
|
},
|
|
|
|
dialogClick : function(e) {
|
|
var clicked = $(e.target);
|
|
var $this = $(this);
|
|
var root = $this.data("root");
|
|
var opts = root.data("options");
|
|
if (!root.attr("multiple")) {
|
|
if (opts.closeEffect == "fade") {
|
|
$this.fadeOut(opts.closeSpeed);
|
|
} else {
|
|
$this.slideUp(opts.closeSpeed);
|
|
}
|
|
}
|
|
|
|
if (clicked.is("li") && !clicked.hasClass("gentleselect-dummy")) {
|
|
var value = clicked.data("value");
|
|
var name = clicked.data("name");
|
|
var label = $this.data("label")
|
|
|
|
if ($this.data("root").attr("multiple")) {
|
|
clicked.toggleClass("selected");
|
|
var s = $this.find("li.selected");
|
|
label.text(getSelectedAsText(s, opts));
|
|
var v = s.map(function(){ return $(this).data("value"); });
|
|
// update actual selectbox and trigger change event
|
|
root.val(v.get()).trigger("change");
|
|
} else {
|
|
$this.find("li.selected").removeClass("selected");
|
|
clicked.addClass("selected");
|
|
label.text(clicked.data("name"));
|
|
// update actual selectbox and trigger change event
|
|
root.val(value).trigger("change");
|
|
}
|
|
}
|
|
},
|
|
|
|
keyUp : function(e) {
|
|
if (e.keyCode == 27 ) { // ESC
|
|
$(".gentleselect-dialog").hide();
|
|
}
|
|
}
|
|
};
|
|
|
|
$.fn.gentleSelect = function(method) {
|
|
if (methods[method]) {
|
|
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
} else if (typeof method === 'object' || ! method) {
|
|
return methods.init.apply(this, arguments);
|
|
} else {
|
|
$.error( 'Method ' + method + ' does not exist on jQuery.gentleSelect' );
|
|
}
|
|
};
|
|
|
|
|
|
})(jQuery);
|