saeedsq/django-fancy-cronfield

View on GitHub
fancy_cronfield/static/fancy_cronfield/js/jquery-gentleSelect.js

Summary

Maintainability
D
2 days
Test Coverage
/*
 * 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>&nbsp;</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);