fancy_cronfield/static/fancy_cronfield/js/jquery-gentleSelect.js
/*
* 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: gettext('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
var arr;
if (opts.maxDisplay !== 0 && elemList.length > opts.maxDisplay) {
arr = elemList.slice(0, opts.maxDisplay).map(function() {return $(this).text();});
arr.push('...');
} else {
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
var 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;
actualWidth += parseInt(f.css('padding-left'));
actualWidth += parseInt(f.css('padding-right'));
var elemCount = ul.find('li').length;
var cols;
var rows;
if (defined(o.columns)) {
cols = parseInt(o.columns);
rows = Math.ceil(elemCount / cols);
} else {
rows = parseInt(o.rows);
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(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_1_4_1);