r2703 - New plugin: selectmenu

r2703 - New plugin: selectmenu


Author: scottjehl
Date: Thu Jun 11 10:39:13 2009
New Revision: 2703
Added:
branches/labs/ui-selectmenu/
branches/labs/ui-selectmenu/index.html (contents, props changed)
branches/labs/ui-selectmenu/ui.selectmenu.css (contents, props changed)
branches/labs/ui-selectmenu/ui.selectmenu.js (contents, props changed)
Log:
New plugin: selectmenu
For custom select elements
Added: branches/labs/ui-selectmenu/index.html
==============================================================================
--- (empty file)
+++ branches/labs/ui-selectmenu/index.html    Thu Jun 11 10:39:13 2009
@@ -0,0 +1,111 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+    <head>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <title>Demo Page for jQuery UI selectmenu</title>
+    <link rel="Stylesheet"
href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-darkness/jquery-ui.css"
type="text/css" />
+    <link rel="Stylesheet" href="ui.selectmenu.css" type="text/css" />
+    <script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
+    <script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
+    <script type="text/javascript" src="ui.selectmenu.js"></script>
+    <style type="text/css">
+        /*demo styles*/
+        body {font-size: 62.5%; font-family:"Verdana",sans-serif; margin: 100px;}
+        fieldset { border:0; display: block; margin-bottom: 40px;}    
+        label,select,.ui-select-menu { float: left; margin-right: 10px; }
+        select { width: 200px; }
+    </style>
+    <script type="text/javascript">
+    
+    
+        $(function(){
+            
+            $('select#speedA').selectmenu();
+            
+            $('select#speedB').selectmenu({
+                width: 300,
+                format: addressFormatting
+            });
+            
+            $('select#speedC').selectmenu({style:'dropdown'});
+            
+            $('select#speedD').selectmenu({
+                style:'dropdown',
+                menuWidth: 400,
+                format: addressFormatting
+            });
+            
+            $('

<a href="#">Destroy Menus</a>

')
+                .appendTo('body')
+                .click(function(){
+                        $('select').selectmenu('destroy');
+                    });
+            
+            
+        });
+        //format options
+        var addressFormatting = [
+            {find:/^([^\-]+) \- /g, rep: '<span
class="ui-selectmenu-item-header">$1</span>'},
+            {find:/([^\|><]+) \| /g, rep: '<span
class="ui-selectmenu-item-content">$1</span>'},
+            {find:/([^\|><\(\)]+) (\()/g, rep: '<span
class="ui-selectmenu-item-content">$1</span>$2'},
+            {find:/([^\|><\(\)]+)$/g, rep: '<span
class="ui-selectmenu-item-content">$1</span>'},
+            {find:/(\([^\|><]+\))$/g, rep: '<span
class="ui-selectmenu-item-footer">$1</span>'}
+        ];
+        
+        
+    </script>
+    <script type="text/javascript"
src="http://ui.jquery.com/applications/themeroller/themeswitchertool/"></script>
+<script type="text/javascript"> $(function(){ $('<div style="position:
absolute; top: 20px; left: 20px;" />').appendTo('body').themeswitcher();
}); </script>
+</head>
+<body>
+    <form action="#">
+        <h2>Default: "popup" Style
(<code>$('select#speedA').selectmenu();</code>)</h2>
+        <fieldset>
+            <label for="speedA">Select a Speed:</label>
+            <select name="speedA" id="speedA">
+                <option value="Slower" class="whoo">Slower</option>
+                <option value="Slow">Slow</option>
+                <option value="Medium" selected="selected">Medium</option>
+                <option value="Fast">Fast</option>
+                <option value="Faster">Faster</option>
+            </select>
+        </fieldset>
+        
+        <h2>Same with option text formatting</h2>
+        <fieldset>
+            <label for="speedB">Select an Address:</label>
+            <select name="speedB" id="speedB">
+                <option>John Doe - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+                <option selected="selected">Jane Doe - 78 West Main St Apt 3A |
Bloomsburg, PA 12345 (footer text)</option>
+                <option>Joseph Doe - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+                <option>Mad Doe Kiiid - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+            </select>
+        </fieldset>
+        
+        <h2>"dropdown" Style</h2>
+        <fieldset>
+            <label for="speedC">Select a Speed:</label>
+            <select name="speedC" id="speedC">
+                <option value="Slower" class="whoo">Slower</option>
+                <option value="Slow">Slow</option>
+                <option value="Medium" selected="selected">Medium</option>
+                <option value="Fast">Fast</option>
+                <option value="Faster">Faster</option>
+            </select>
+        </fieldset>
+        
+        
+        <h2>"dropdown" Style with menuWidth wider than menu and text
formatting</h2>
+        <fieldset>
+            <label for="speedD">Select an Address:</label>
+            <select name="speedD" id="speedD">
+                <option>John Doe - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+                <option>Jane Doe - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+                <option selected="selected">Joseph Doe - 78 West Main St Apt 3A |
Bloomsburg, PA 12345 (footer text)</option>
+                <option>Mad Doe Kiiid - 78 West Main St Apt 3A | Bloomsburg, PA 12345
(footer text)</option>
+            </select>
+        </fieldset>
+
+    </form>
+</body>
+</html>
\ No newline at end of file
Added: branches/labs/ui-selectmenu/ui.selectmenu.css
==============================================================================
--- (empty file)
+++ branches/labs/ui-selectmenu/ui.selectmenu.css    Thu Jun 11 10:39:13 2009
@@ -0,0 +1,25 @@
+ /*
+ * jQuery UI selectmenu
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+
+.ui-selectmenu { display: block; position:relative; height:2em;
text-decoration: none; overflow:hidden;}
+.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top:
50%; }
+.ui-selectmenu-menu { padding:0; margin:0; list-style:none;
position:absolute; visibility: hidden; }
+.ui-selectmenu-open { visibility: visible; }
+.ui-selectmenu-menu-popup { margin-top: -1px; }
+.ui-selectmenu-menu-dropdown { }
+.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top:
1px dotted transparent; border-bottom: 1px dotted transparent;
border-right-width: 0 !important; border-left-width: 0 !important;
font-weight: normal !important; }
+.ui-selectmenu-menu li a,.ui-selectmenu-status {line-height: 1.4em;
display:block; padding:.3em 1em; outline:none; text-decoration:none; }
+.ui-selectmenu-status { line-height: 1.4em; }
+.ui-selectmenu-open li.ui-selectmenu-item-focus a { }
+.ui-selectmenu-open li.ui-selectmenu-item-selected { }
+.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block;
margin-bottom: .2em; }
+.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; }
+.ui-selectmenu-menu li .ui-selectmenu-item-content { }
+.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; }
\ No newline at end of file
Added: branches/labs/ui-selectmenu/ui.selectmenu.js
==============================================================================
--- (empty file)
+++ branches/labs/ui-selectmenu/ui.selectmenu.js    Thu Jun 11 10:39:13 2009
@@ -0,0 +1,371 @@
+ /*
+ * jQuery UI selectmenu
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+
+(function($) {
+
+$.widget("ui.selectmenu", $.extend({}, $.ui.mouse, {
+    _init: function() {
+        var self = this, o = this.options;
+        var that = this;
+        this._mouseInit();
+        
+        //quick array of button and menu id's
+        this.ids = this._genId();
+        
+        //create menu button wrapper
+        this.newelement = $('<a class="ui-selectmenu ui-widget ui-state-default
ui-corner-all" id="'+this.ids[0]+'" href="#" aria-haspopup="true"
aria-owns="'+this.ids[1]+'"></a>')
+            .insertAfter(this.element);
+        
+        //transfer classes to selectmenu
+        if(o.transferClasses){
+            var transferClasses = this.element.attr('class') || '';
+            this.newelement.addClass(transferClasses);
+        }
+        
+        //transfer tabindex
+        var tabindex = this.element.attr('tabindex') || '0';
+        this.newelement.attr('tabindex', tabindex);
+        
+        
+        //menu icon
+        this.selectmenuIcon = $('<span class="ui-selectmenu-icon
ui-icon"></span>')
+            .prependTo(this.newelement)
+            .addClass( (o.style
== "popup")? 'ui-icon-triangle-2-n-s' : 'ui-icon-triangle-1-s' );    
+
+            
+        //make associated form label trigger focus
+        $('label[for='+this.element.attr('id')+']')
+            .click(function(){
+                that.newelement.focus();
+                return false;
+            });    
+
+        //click toggle for menu visibility
+        this.newelement
+        .bind('click', function(){
+            that._closeOthers();
+            that._toggle();
+            return false;
+        })
+        .keydown(function(event){
+            var ret = true;
+            switch (event.keyCode) {
+                case $.ui.keyCode.ENTER:
+                case $.ui.keyCode.SPACE:
+                    ret = false;
+                    that._closeOthers();
+                    that._toggle();    
+                    break;
+                case $.ui.keyCode.UP:
+                case $.ui.keyCode.LEFT:
+                    //trigger click event on previous (should be abstracted out)
+                    
that.list.find('li.ui-selectmenu-item-selected').prev().trigger('mouseup');    
+                    break;
+                case $.ui.keyCode.DOWN:
+                case $.ui.keyCode.RIGHT:
+                    //trigger click event on next (should be abstracted out)
+                    
that.list.find('li.ui-selectmenu-item-selected').next().trigger('mouseup');    
+                    break;    
+            }
+            return ret;
+        })
+        .bind('mouseover focus', function(){
$(this).addClass('ui-selectmenu-focus ui-state-hover'); })
+        .bind('mouseout blur', function(){
+            $(this).removeClass('ui-selectmenu-focus ui-state-hover');
+        });
+        
+        //document click closes menu
+        $(document).click(function(){
+            that.close();
+        });
+
+        //change event on original selectmenu
+            this.element
+                .click(function(){ this._refreshValue(); })
+                .focus(function(){ this.newelement.focus(); });
+        
+        
+        //create menu portion, append to body
+        var cornerClass = (o.style == "dropdown")? " ui-corner-bottom" : "
ui-corner-all"
+        this.list = $('<ul class="ui-selectmenu-menu ui-widget
ui-widget-content'+cornerClass+'" role="menu"
aria-labelledby="'+this.ids[0]+'"
id="'+this.ids[1]+'"></ul>').appendTo('body');                
+        
+        //get selectmenu element option data    
+        var s_options = this._selectOptions();
+        var activeClass = (that.options.style == "popup") ? "
ui-state-active" : "";
+        //write li's
+        for(var i in s_options){
+            var thisLi = $('<li><a href="#" tabindex="-1" role="option"
aria-selected="false">'+ s_options[i].text +'</a></li>')
+                .data('index',i)
+                .addClass(s_options[i].classes)
+                .mouseup(function(){
+                    that.value($(this).data('index'));
+                })
+                .bind('mouseover focus', function(){
+                    
$(this).parent().find('.ui-selectmenu-item-selected').addClass(activeClass);
+                    
$(this).parent().find('.ui-selectmenu-item-focus').removeClass('ui-selectmenu-item-focus
ui-state-hover');
+                    
$(this).removeClass('ui-state-active').addClass('ui-selectmenu-item-focus
ui-state-hover');
+                })
+                .bind('mouseout blur', function(){
+                    if($(this).is('.ui-selectmenu-item-selected')){
$(this).addClass(activeClass); }
+                    $(this).removeClass('ui-selectmenu-item-focus ui-state-hover');
+                })
+                .appendTo(this.list);
+        }    
+        
+        //add corners to top and bottom menu items
+        this.list.find('li:last').addClass("ui-corner-bottom");
+        if(o.style == 'popup'){
+            this.list.find('li:first').addClass("ui-corner-top");
+        }
+        
+        //original selectmenu width
+        var selectWidth = this.element.width();
+        //set menu button width
+        this.newelement.width( (o.width) ? o.width : selectWidth);
+        //set menu width to either menuWidth option value, width option value,
or select width
+        if(o.style == 'dropdown'){ this.list.width( (o.menuWidth) ?
o.menuWidth : ((o.width) ? o.width : selectWidth)); }
+        else { this.list.width( (o.menuWidth) ? o.menuWidth : ((o.width) ?
o.width - o.handleWidth : selectWidth - o.handleWidth)); }    
+        
+        if(this.list.width() > this.newelement.width()){ }
+        
+        //transfer menu click to menu button
+        this.list
+        .click(function(){
+            that.newelement.trigger('click');    // (should be abstracted out)
+            return false;
+        })
+        .keydown(function(event){
+            var ret = true;
+
+            switch (event.keyCode) {
+                case $.ui.keyCode.UP:
+                case $.ui.keyCode.LEFT:
+                    ret = false;
+                    if($(event.target).parents('li:eq(0)').prev().size()>0){
+                        
$(event.target).blur().parents('li:eq(0)').prev().find('a:eq(0)').focus();
+                    }    
+                    break;
+                case $.ui.keyCode.DOWN:
+                case $.ui.keyCode.RIGHT:
+                    ret = false;
+                    if($(event.target).parents('li:eq(0)').next().size()>0){
+                        
$(event.target).blur().parents('li:eq(0)').next().find('a:eq(0)').focus();
+                    }    
+                    break;        
+                case $.ui.keyCode.ENTER:
+                case $.ui.keyCode.SPACE:
+                    ret = false;
+                    that.close();
+                    $(event.target).parents('li:eq(0)').trigger('mouseup');
+                    break;        
+                case $.ui.keyCode.TAB:
+                    ret = true;
+                    that.close();
+                    break;    
+            }
+            return ret;
+        });
+        
+        //selectmenu style
+        if(o.style == 'dropdown'){
+            this.newelement
+                .addClass("ui-selectmenu-dropdown");
+            this.list
+                .addClass("ui-selectmenu-menu-dropdown");    
+        }
+        else {
+            this.newelement
+                .addClass("ui-selectmenu-popup");
+            this.list
+                .addClass("ui-selectmenu-menu-popup");    
+        }
+        
+        this.newelement.prepend('<span class="ui-selectmenu-status">'+
s_options[this._selectedIndex()].text +'</span>');
+        
+        //hide original selectmenu element
+        if(o.hideElement){
+            this.element.hide();
+        }
+        
+        //update value
+        if(o.value){ this.value(o.value); }
+        else { this._refreshValue(); }
+        this._setData("value", this._selectedIndex());
+    },
+    
+    destroy: function() {
+        this.newelement.remove();
+        this.element.show();
+        this._mouseDestroy();
+        //unbind click on label
+        $('label[for='+this.element.attr('id')+']').unbind('click');
+    },
+    
+    _closeOthers: function(){
+        //this needs to be handled better
+        
$('.ui-selectmenu-open').not(this.list).removeClass('ui-selectmenu-open');
+        $('.ui-select').not(this.newelement).trigger('blur');
+    },
+    
+    _genId: function(type){
+        var num = Math.round(Math.random() * 1000);
+        return [this.element.attr('id') + '_' + 'button' + '_' + num,
this.element.attr('id') + '_' + 'menu' + '_' + num];
+    },
+    
+    open: function(){
+        this._refreshPosition();
+        this.newelement.addClass('ui-state-active');
+        this.list
+            .appendTo('body')
+            .addClass('ui-selectmenu-open')
+            .find('li:eq('+ this._selectedIndex() +') a').focus();    
+            
+        if(this.options.style == "dropdown"){
this.newelement.removeClass('ui-corner-all').addClass('ui-corner-top'); }    
+        return this.element;    
+    },
+    
+    close: function(){
+  &