diff options
author | jzaefferer <joern.zaefferer@gmail.com> | 2010-11-19 19:10:16 +0100 |
---|---|---|
committer | jzaefferer <joern.zaefferer@gmail.com> | 2010-11-19 19:10:16 +0100 |
commit | fe5248b6bb26b0ac36de4fbfa37373eeb18a774b (patch) | |
tree | 815f8ad1ba5ea299daca66c17e1104bc8620030f | |
parent | b02b8e3ef585e0ca788687c60e8f15d8c809bd49 (diff) | |
parent | ecc0d0e74da2126eaedf16f8f9ef792e8d86485c (diff) | |
download | jquery-ui-fe5248b6bb26b0ac36de4fbfa37373eeb18a774b.tar.gz jquery-ui-fe5248b6bb26b0ac36de4fbfa37373eeb18a774b.zip |
Merge branch 'menu' into devpreview
Conflicts:
demos/button/splitbutton.html
-rw-r--r-- | demos/button/splitbutton.html | 38 | ||||
-rw-r--r-- | tests/visual/menu/menu.html | 1 | ||||
-rw-r--r-- | ui/jquery.ui.menu.js | 245 |
3 files changed, 156 insertions, 128 deletions
diff --git a/demos/button/splitbutton.html b/demos/button/splitbutton.html index 0d4973090..0e013234b 100644 --- a/demos/button/splitbutton.html +++ b/demos/button/splitbutton.html @@ -8,6 +8,8 @@ <script src="../../ui/jquery.ui.core.js"></script> <script src="../../ui/jquery.ui.widget.js"></script> <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { @@ -23,25 +25,43 @@ primary: "ui-icon-triangle-1-s" } }) - .click(function() { - alert( "Could display a menu to select an action" ); + .click( function() { + var menu = $(this).parent().next().show().position({ + my: "left top", + at: "left bottom", + of: this + }); + $(document).one("click", function() { + menu.hide(); + }); + return false; }) - .parent() - .buttonset(); + .parent() + .buttonset() + .next() + .hide() + .menu(); }); </script> <style> - + .ui-menu { position: absolute; width: 100px; } </style> </head> <body> <div class="demo"> -<div> - <button id="rerun">Run last action</button> - <button id="select">Select an action</button> -</div> + <div> + <div> + <button id="rerun">Run last action</button> + <button id="select">Select an action</button> + </div> + <ul> + <li><a href="#">Open...</a></li> + <li><a href="#">Save</a></li> + <li><a href="#">Delete</a></li> + </ul> + </div> </div><!-- End demo --> diff --git a/tests/visual/menu/menu.html b/tests/visual/menu/menu.html index 1b8bba823..a8d8fa837 100644 --- a/tests/visual/menu/menu.html +++ b/tests/visual/menu/menu.html @@ -8,7 +8,6 @@ <script type="text/javascript" src="../../../ui/jquery.ui.core.js"></script> <script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script> <script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script> - <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script> <script type="text/javascript"> $(function() { $.fn.themeswitcher && $('<div/>').css({ diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index e4f6990d5..c98d2d3d7 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -1,29 +1,32 @@ /* * jQuery UI Menu @VERSION * - * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. + * Copyright 2010, AUTHORS.txt + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license * * http://docs.jquery.com/UI/Menu * * Depends: * jquery.ui.core.js - * jquery.ui.widget.js + * jquery.ui.widget.js */ (function($) { + +var idIncrement = 0; $.widget("ui.menu", { _create: function() { var self = this; + this.menuId = this.element.attr( "id" ) || "ui-menu-" + idIncrement++; this.element - .addClass("ui-menu ui-widget ui-widget-content ui-corner-all") + .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) .attr({ - role: "listbox", - "aria-activedescendant": "ui-active-menuitem" + id: this.menuId, + role: "listbox" }) - .bind("click.menu", function( event ) { - if (self.options.disabled) { + .bind( "click.menu", function( event ) { + if ( self.options.disabled ) { return false; } if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) { @@ -32,17 +35,35 @@ $.widget("ui.menu", { // temporary event.preventDefault(); self.select( event ); + }) + .bind( "mouseover.menu", function( event ) { + if ( self.options.disabled ) { + return; + } + var target = $( event.target ).closest( ".ui-menu-item" ); + if ( target.length && target.parent()[0] === self.element[0] ) { + self.activate( event, target ); + } + }) + .bind("mouseout.menu", function( event ) { + if ( self.options.disabled ) { + return; + } + var target = $( event.target ).closest( ".ui-menu-item" ); + if ( target.length && target.parent()[0] === self.element[0] ) { + self.deactivate( event ); + } }); this.refresh(); - if (!this.options.input) { - this.options.input = this.element.attr("tabIndex", 0); + if ( !this.options.input ) { + this.options.input = this.element.attr( "tabIndex", 0 ); } - this.options.input.bind("keydown.menu", function(event) { - if (self.options.disabled) { + this.options.input.bind( "keydown.menu", function( event ) { + if ( self.options.disabled ) { return; } - switch (event.keyCode) { + switch ( event.keyCode ) { case $.ui.keyCode.PAGE_UP: self.previousPage(); event.preventDefault(); @@ -73,168 +94,156 @@ $.widget("ui.menu", { }, destroy: function() { - $.Widget.prototype.destroy.apply(this, arguments); + $.Widget.prototype.destroy.apply( this, arguments ); this.element - .removeClass("ui-menu ui-widget ui-widget-content ui-corner-all") - .removeAttr("tabIndex") - .removeAttr("role") - .removeAttr("aria-activedescendant"); + .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) + .removeAttr( "tabIndex" ) + .removeAttr( "role" ) + .removeAttr( "aria-activedescendant" ); - this.element.children(".ui-menu-item") - .removeClass("ui-menu-item") - .removeAttr("role") - .children("a") - .removeClass("ui-corner-all") - .removeAttr("tabIndex") - .unbind(".menu"); + this.element.children( ".ui-menu-item" ) + .removeClass( "ui-menu-item" ) + .removeAttr( "role" ) + .children( "a" ) + .removeClass( "ui-corner-all" ) + .removeAttr( "tabIndex" ) + .unbind( ".menu" ); }, refresh: function() { - var self = this; - // don't refresh list items that are already adapted - var items = this.element.children("li:not(.ui-menu-item):has(a)") - .addClass("ui-menu-item") - .attr("role", "menuitem"); + var items = this.element.children( "li:not(.ui-menu-item):has(a)" ) + .addClass( "ui-menu-item" ) + .attr( "role", "menuitem" ); - items.children("a") - .addClass("ui-corner-all") - .attr("tabIndex", -1) - // mouseenter doesn't work with event delegation - .bind("mouseenter.menu", function( event ) { - if (self.options.disabled) { - return; - } - self.activate( event, $(this).parent() ); - }) - .bind("mouseleave.menu", function() { - if (self.options.disabled) { - return; - } - self.deactivate(); - }); + items.children( "a" ) + .addClass( "ui-corner-all" ) + .attr( "tabIndex", -1 ); }, activate: function( event, item ) { + var self = this; this.deactivate(); - if (this._hasScroll()) { - var offset = item.offset().top - this.element.offset().top, - scroll = this.element.attr("scrollTop"), - elementHeight = this.element.height(); - if (offset < 0) { - this.element.attr("scrollTop", scroll + offset); - } else if (offset > elementHeight) { - this.element.attr("scrollTop", scroll + offset - elementHeight + item.height()); + if ( this._hasScroll() ) { + var borderTop = parseFloat( $.curCSS( this.element[0], "borderTopWidth", true) ) || 0, + paddingtop = parseFloat( $.curCSS( this.element[0], "paddingTop", true) ) || 0, + offset = item.offset().top - this.element.offset().top - borderTop - paddingtop, + scroll = this.element.attr( "scrollTop" ), + elementHeight = this.element.height(), + itemHeight = item.height(); + if ( offset < 0 ) { + this.element.attr( "scrollTop", scroll + offset ); + } else if ( offset + itemHeight > elementHeight ) { + this.element.attr( "scrollTop", scroll + offset - elementHeight + itemHeight ); } } - this.active = item.eq(0) - .children("a") - .addClass("ui-state-hover") - .attr("id", "ui-active-menuitem") + this.active = item.first() + .children( "a" ) + .addClass( "ui-state-hover" ) + .attr( "id", function(index, id) { + return (self.itemId = id || self.menuId + "-activedescendant"); + }) .end(); - this._trigger("focus", event, { item: item }); + // need to remove the attribute before adding it for the screenreader to pick up the change + // see http://groups.google.com/group/jquery-a11y/msg/929e0c1e8c5efc8f + this.element.removeAttr("aria-activedescenant").attr("aria-activedescenant", self.itemId); + this._trigger( "focus", event, { item: item } ); }, - deactivate: function() { - if (!this.active) { return; } + deactivate: function(event) { + if (!this.active) { + return; + } - this.active.children("a") - .removeClass("ui-state-hover") - .removeAttr("id"); - this._trigger("blur"); + var self = this; + this.active.children( "a" ).removeClass( "ui-state-hover" ); + // remove only generated id + $( "#" + self.menuId + "-activedescendant" ).removeAttr( "id" ); + this.element.removeAttr( "aria-activedescenant" ); + this._trigger( "blur", event ); this.active = null; }, next: function(event) { - this._move("next", ".ui-menu-item:first", event); + this._move( "next", ".ui-menu-item", "first", event ); }, previous: function(event) { - this._move("prev", ".ui-menu-item:last", event); + this._move( "prev", ".ui-menu-item", "last", event ); }, first: function() { - return this.active && !this.active.prevAll(".ui-menu-item").length; + return this.active && !this.active.prevAll( ".ui-menu-item" ).length; }, last: function() { - return this.active && !this.active.nextAll(".ui-menu-item").length; + return this.active && !this.active.nextAll( ".ui-menu-item" ).length; }, - _move: function(direction, edge, event) { - if (!this.active) { - this.activate(event, this.element.children(edge)); + _move: function(direction, edge, filter, event) { + if ( !this.active ) { + this.activate( event, this.element.children(edge)[filter]() ); return; } - var next = this.active[direction + "All"](".ui-menu-item").eq(0); - if (next.length) { - this.activate(event, next); + var next = this.active[ direction + "All" ]( ".ui-menu-item" ).eq( 0 ); + if ( next.length ) { + this.activate( event, next ); } else { - this.activate(event, this.element.children(edge)); + this.activate( event, this.element.children(edge)[filter]() ); } }, - - // TODO merge with previousPage - nextPage: function(event) { - if (this._hasScroll()) { - // TODO merge with no-scroll-else - if (!this.active || this.last()) { - this.activate(event, this.element.children(":first")); + + nextPage: function( event ) { + if ( this._hasScroll() ) { + if ( !this.active || this.last() ) { + this.activate( event, this.element.children( ".ui-menu-item" ).first() ); return; } var base = this.active.offset().top, height = this.element.height(), - result = this.element.children("li").filter(function() { - var close = $(this).offset().top - base - height + $(this).height(); - // TODO improve approximation - return close < 10 && close > -10; - }); - - // TODO try to catch this earlier when scrollTop indicates the last page anyway - if (!result.length) { - result = this.element.children(":last"); - } - this.activate(event, result); + result; + this.active.nextAll( ".ui-menu-item" ).each( function() { + result = $( this ); + return $( this ).offset().top - base - height < 0; + }); + + this.activate( event, result ); } else { - this.activate(event, this.element.children(!this.active || this.last() ? ":first" : ":last")); + this.activate( event, this.element.children( ".ui-menu-item" ) + [ !this.active || this.last() ? "first" : "last" ]() ); } }, - // TODO merge with nextPage - previousPage: function(event) { - if (this._hasScroll()) { - // TODO merge with no-scroll-else - if (!this.active || this.first()) { - this.activate(event, this.element.children(":last")); + previousPage: function( event ) { + if ( this._hasScroll() ) { + if ( !this.active || this.first() ) { + this.activate( event, this.element.children( ".ui-menu-item" ).last() ); return; } var base = this.active.offset().top, - height = this.element.height(); - result = this.element.children("li").filter(function() { - var close = $(this).offset().top - base + height - $(this).height(); - // TODO improve approximation - return close < 10 && close > -10; - }); - - // TODO try to catch this earlier when scrollTop indicates the last page anyway - if (!result.length) { - result = this.element.children(":first"); - } - this.activate(event, result); + height = this.element.height(), + result; + this.active.prevAll( ".ui-menu-item" ).each( function() { + result = $( this ); + return $(this).offset().top - base + height > 0; + }); + + this.activate( event, result ); } else { - this.activate(event, this.element.children(!this.active || this.first() ? ":last" : ":first")); + this.activate( event, this.element.children( ".ui-menu-item" ) + [ !this.active || this.first() ? ":last" : ":first" ]() ); } }, _hasScroll: function() { - return this.element.height() < this.element.attr("scrollHeight"); + return this.element.height() < this.element.attr( "scrollHeight" ); }, select: function( event ) { - this._trigger("select", event, { item: this.active }); + this._trigger( "select", event, { item: this.active } ); } }); -}(jQuery)); +}( jQuery )); |