diff options
64 files changed, 2462 insertions, 1611 deletions
@@ -7,3 +7,20 @@ If you want to use jQuery UI, go to [jqueryui.com](http://jqueryui.com) to get s If you are interested in helping developing jQuery UI, you are in the right place. To discuss development with team members and the community, visit the [Developing jQuery UI Forum](http://forum.jquery.com/developing-jquery-ui). + +For committers +--- +When looking at pull requests, first check for [proper commit messages](http://wiki.jqueryui.com/w/page/12137724/Bug-Fixing-Guide). + +Unless everything is fine and you can merge directly via GitHub's interface, fetch the remote first: + + git remote add [username] [his-fork.git] -f + +If you want just one commit and edit the commit message: + + git cherry-pick -e [sha-of-commit] + +If it should go to the stable brach, cherry-pick it to stable: + + git checkout 1-8-stable + git cherry-pick -x [sha-of-commit] diff --git a/demos/autocomplete/combobox.html b/demos/autocomplete/combobox.html index ded809e1e..5fb3ffef9 100644 --- a/demos/autocomplete/combobox.html +++ b/demos/autocomplete/combobox.html @@ -16,7 +16,7 @@ <style> .ui-button { margin-left: -1px; } .ui-button-icon-only .ui-button-text { padding: 0.35em; } - .ui-autocomplete-input { margin: 0; padding: 0.48em 0 0.47em 0.45em; } + .ui-autocomplete-input { margin: 0; padding: 0.4em 0 0.4em 0.45em; } </style> <script> (function( $ ) { @@ -26,6 +26,32 @@ select = this.element.hide(), selected = select.children( ":selected" ), value = selected.val() ? selected.text() : ""; + + function removeIfInvalid(element) { + var value = $( element ).val(), + matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( value ) + "$", "i" ), + valid = false; + select.children( "option" ).each(function() { + if ( $( this ).text().match( matcher ) ) { + this.selected = valid = true; + return false; + } + }); + if ( !valid ) { + // remove invalid value, as it didn't match anything + $( element ) + .val( "" ) + .attr( "title", value + " didn't match any item" ) + .tooltip( "open" ); + select.val( "" ); + setTimeout(function() { + input.tooltip( "close" ).attr( "title", "" ); + }, 2500 ); + input.data( "autocomplete" ).term = ""; + return false; + } + } + var input = this.input = $( "<input>" ) .insertAfter( select ) .val( value ) @@ -57,30 +83,8 @@ }); }, change: function( event, ui ) { - if ( !ui.item ) { - var value = $( this ).val(), - matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( value ) + "$", "i" ), - valid = false; - select.children( "option" ).each(function() { - if ( $( this ).text().match( matcher ) ) { - this.selected = valid = true; - return false; - } - }); - if ( !valid ) { - // remove invalid value, as it didn't match anything - $( this ) - .val( "" ) - .attr( "title", value + " didn't match any item" ) - .tooltip( "open" ); - select.val( "" ); - setTimeout(function() { - input.tooltip( "close" ).attr( "title", "" ); - }, 2500 ); - input.data( "autocomplete" ).term = ""; - return false; - } - } + if ( !ui.item ) + return removeIfInvalid( this ); } }) .addClass( "ui-widget ui-widget-content ui-corner-left" ); @@ -109,6 +113,7 @@ // close if already visible if ( input.autocomplete( "widget" ).is( ":visible" ) ) { input.autocomplete( "close" ); + removeIfInvalid( input ); return; } @@ -124,10 +129,9 @@ .tooltip({ position: { of: this.button - } - }) - .tooltip( "widget" ) - .addClass( "ui-state-highlight" ); + }, + tooltipClass: "ui-state-highlight" + }); }, destroy: function() { diff --git a/demos/autocomplete/maxheight.html b/demos/autocomplete/maxheight.html index fc0b4840c..c4f9769c3 100644 --- a/demos/autocomplete/maxheight.html +++ b/demos/autocomplete/maxheight.html @@ -8,6 +8,7 @@ <script src="../../ui/jquery.ui.core.js"></script> <script src="../../ui/jquery.ui.widget.js"></script> <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> <script src="../../ui/jquery.ui.autocomplete.js"></script> <link rel="stylesheet" href="../demos.css"> <style> diff --git a/demos/button/default.html b/demos/button/default.html index 1ea3fa796..969ec6838 100644 --- a/demos/button/default.html +++ b/demos/button/default.html @@ -11,7 +11,7 @@ <link rel="stylesheet" href="../demos.css"> <script> $(function() { - $( "button, input:submit, a", ".demo" ).button(); + $( "input:submit, a, button", ".demo" ).button(); $( "a", ".demo" ).click(function() { return false; }); }); </script> diff --git a/demos/demos.css b/demos/demos.css index f5f2dbe64..cf6da8bd1 100644 --- a/demos/demos.css +++ b/demos/demos.css @@ -129,8 +129,7 @@ eventually we should convert the font sizes to ems -- using px for now to minimi .normal h3.demo-header { font-size:32px; padding:0 0 5px; border-bottom:1px solid #eee; text-transform: capitalize; } .normal h4.demo-subheader { font-size:10px; text-transform: uppercase; color:#999; padding:8px 0 3px; border:0; margin:0; } -.normal a:link, -.normal a:visited { color:#1b75bb; text-decoration:none; } +#demo-notes a, #demo-link a, #demo-source a { color:#1b75bb; text-decoration:none; } .normal a:hover, .normal a:active { color:#0b559b; } diff --git a/demos/index.html b/demos/index.html index 22dbfc5c5..b1cfcdd44 100644 --- a/demos/index.html +++ b/demos/index.html @@ -20,6 +20,8 @@ <script src="../ui/jquery.ui.draggable.js"></script> <script src="../ui/jquery.ui.droppable.js"></script> <script src="../ui/jquery.ui.menu.js"></script> + <script src="../ui/jquery.ui.menubar.js"></script> + <script src="../ui/jquery.ui.popup.js"></script> <script src="../ui/jquery.ui.position.js"></script> <script src="../ui/jquery.ui.progressbar.js"></script> <script src="../ui/jquery.ui.resizable.js"></script> @@ -279,6 +281,8 @@ <dd><a href="datepicker/index.html">Datepicker</a></dd> <dd><a href="dialog/index.html">Dialog</a></dd> <dd><a href="menu/index.html">Menu</a></dd> + <dd><a href="menubar/index.html">Menubar</a></dd> + <dd><a href="popup/index.html">Popup</a></dd> <dd><a href="progressbar/index.html">Progressbar</a></dd> <dd><a href="slider/index.html">Slider</a></dd> <dd><a href="spinner/index.html">Spinner</a></dd> diff --git a/demos/menu/contextmenu.html b/demos/menu/contextmenu.html index 0fb98c140..7513bca28 100644 --- a/demos/menu/contextmenu.html +++ b/demos/menu/contextmenu.html @@ -3,44 +3,28 @@ <head> <meta charset="UTF-8" /> <title>jQuery UI Menu - Contextmenu demo</title> - <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" /> - <script type="text/javascript" src="../../jquery-1.5.1.js"></script> - <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script> - <script type="text/javascript" src="../../ui/jquery.ui.position.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="../../ui/jquery.ui.button.js"></script> - <link type="text/css" href="../demos.css" rel="stylesheet" /> - <script type="text/javascript"> + <link href="../../themes/base/jquery.ui.all.css" rel="stylesheet" /> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.popup.js"></script> + <link href="../demos.css" rel="stylesheet" /> + <script> $(function() { $(".demo button").button({ icons: { primary: "ui-icon-home", secondary: "ui-icon-triangle-1-s" } - }).each(function() { - $(this).next().menu({ - select: function(event, ui) { - $(this).hide(); - $("#log").append("<div>Selected " + ui.item.text() + "</div>"); - } - }).hide(); - }).click(function(event) { - var menu = $(this).next(); - if (menu.is(":visible")) { - menu.hide(); - return false; + }).next().menu({ + select: function(event, ui) { + $(this).hide(); + $("#log").append("<div>Selected " + ui.item.text() + "</div>"); } - menu.menu("blur").show().position({ - my: "left top", - at: "right top", - of: this - }); - $(document).one("click", function() { - menu.hide(); - }); - return false; - }) + }).popup(); }); </script> <style> @@ -53,14 +37,14 @@ <button>Select a city</button> <ul> - <li><a href="#">Amsterdam</a></li> - <li><a href="#">Anaheim</a></li> - <li><a href="#">Cologne</a></li> - <li><a href="#">Frankfurt</a></li> - <li><a href="#">Magdeburg</a></li> - <li><a href="#">Munich</a></li> - <li><a href="#">Utrecht</a></li> - <li><a href="#">Zurich</a></li> + <li><a href="#Amsterdam">Amsterdam</a></li> + <li><a href="#Anaheim">Anaheim</a></li> + <li><a href="#Cologne">Cologne</a></li> + <li><a href="#Frankfurt">Frankfurt</a></li> + <li><a href="#Magdeburg">Magdeburg</a></li> + <li><a href="#Munich">Munich</a></li> + <li><a href="#Utrecht">Utrecht</a></li> + <li><a href="#Zurich">Zurich</a></li> </ul> <div id="log"></div> @@ -69,7 +53,7 @@ <div class="demo-description"> -<p>A simple contextmenu: Click the button, or tab to it and hit space to open the menu. Use the mouse or cursor keys to select an item, click it or hit enter to select it.</p> +<p>TODO update - A simple contextmenu: Click the button, or tab to it and hit space to open the menu. Use the mouse or cursor keys to select an item, click it or hit enter to select it.</p> <p>The keyboard handling is part of the menu. Using the input option to menu is configured to add the key event handlers to the button, as that button gets focused when clicked.</p> diff --git a/demos/menu/default.html b/demos/menu/default.html index 2d9dddf28..40386112f 100644 --- a/demos/menu/default.html +++ b/demos/menu/default.html @@ -4,12 +4,12 @@ <meta charset="UTF-8" /> <title>jQuery UI Menu - Default demo</title> <link type="text/css" href="../../themes/base/jquery.ui.all.css" rel="stylesheet" /> - <script type="text/javascript" src="../../jquery-1.5.1.js"></script> - <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> - <link type="text/css" href="../demos.css" rel="stylesheet" /> - <script type="text/javascript"> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <link href="../demos.css" rel="stylesheet" /> + <script> $(function() { $(".demo ul").menu(); }); @@ -23,21 +23,21 @@ <div class="demo"> <ul> - <li><a href="#">Aberdeen</a></li> - <li><a href="#">Ada</a></li> - <li><a href="#">Adamsville</a></li> - <li><a href="#">Addyston</a></li> - <li><a href="#">Adelphi</a></li> - <li><a href="#">Adena</a></li> - <li><a href="#">Adrian</a></li> - <li><a href="#">Akron</a></li> - <li><a href="#">Albany</a></li> - <li><a href="#">Alexandria</a></li> - <li><a href="#">Alger</a></li> - <li><a href="#">Alledonia</a></li> - <li><a href="#">Alliance</a></li> - <li><a href="#">Alpha</a></li> - <li><a href="#">Alvada</a></li> + <li><a href="#Aberdeen">Aberdeen</a></li> + <li><a href="#Ada">Ada</a></li> + <li><a href="#Adamsville">Adamsville</a></li> + <li><a href="#Addyston">Addyston</a></li> + <li><a href="#Adelphi">Adelphi</a></li> + <li><a href="#Adena">Adena</a></li> + <li><a href="#Adrian">Adrian</a></li> + <li><a href="#Akron">Akron</a></li> + <li><a href="#Albany">Albany</a></li> + <li><a href="#Alexandria">Alexandria</a></li> + <li><a href="#Alger">Alger</a></li> + <li><a href="#Alledonia">Alledonia</a></li> + <li><a href="#Alliance">Alliance</a></li> + <li><a href="#Alpha">Alpha</a></li> + <li><a href="#Alvada">Alvada</a></li> </ul> </div><!-- End demo --> diff --git a/demos/menu/index.html b/demos/menu/index.html index cfc12bc1d..3088f9d7d 100644 --- a/demos/menu/index.html +++ b/demos/menu/index.html @@ -3,7 +3,7 @@ <head> <meta charset="UTF-8" /> <title>jQuery UI Menu Demos</title> - <link type="text/css" href="../demos.css" rel="stylesheet" /> + <link href="../demos.css" rel="stylesheet" /> </head> <body> <div class="demos-nav"> diff --git a/tests/visual/menu/menubar.html b/demos/menubar/default.html index d2185e97c..79662bd9f 100644 --- a/tests/visual/menu/menubar.html +++ b/demos/menubar/default.html @@ -1,27 +1,18 @@ <!DOCTYPE html> <html> <head> - <title>Menu Visual Test: Default</title> - <link rel="stylesheet" href="../visual.css" type="text/css" /> - <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css" type="text/css" title="ui-theme" /> - <script type="text/javascript" src="../../../jquery-1.5.1.js"></script> - <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.position.js"></script> - <script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script> - <script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script> - <script type="text/javascript" src="menubar.js"></script> - <!-- - <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script> - --> - <script type="text/javascript"> + <title>jQuery UI Menubar - Default demo</title> + <link rel="stylesheet" href="../demos.css" type="text/css" /> + <link rel="stylesheet" href="../../themes/base/jquery.ui.all.css" type="text/css" title="ui-theme" /> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <script src="../../ui/jquery.ui.menubar.js"></script> + <script> $(function() { - $.fn.themeswitcher && $('<div/>').css({ - position: "absolute", - right: 10, - top: 10 - }).appendTo(document.body).themeswitcher(); - $("td:has(.menubar)").clone().appendTo("tbody tr:not(:first)"); $("#bar1, .menubar").menubar({ @@ -40,7 +31,6 @@ }); </script> <style type="text/css"> - body { font-size:62.5%; } #bar1, #bar2 { margin: 0 0 4em; } /* style for this page only */ .ui-menu { width: 200px; position: absolute; outline: none; z-index: 9999; } .ui-menu .ui-icon { float: right; } @@ -64,7 +54,7 @@ .ui-menubar-item { float: left; } - + /* table { border-collapse: collapse; } @@ -72,102 +62,105 @@ padding: 0.5em; border: 1px solid black; } + */ </style> </head> <body> +<div class="demo"> <ul id="bar1" class="menubar"> <li> - <a href="#">File</a> + <a href="#File">File</a> <ul> - <li><a href="#">Open...</a></li> + <li><a href="#Open...">Open...</a></li> <li class="ui-state-disabled">Open recent...</li> - <li><a href="#">Save</a></li> - <li><a href="#">Save as...</a></li> - <li><a href="#">Close</a></li> - <li><a href="#">Quit</a></li> + <li><a href="#Save">Save</a></li> + <li><a href="#Save as...">Save as...</a></li> + <li><a href="#Close">Close</a></li> + <li><a href="#Quit">Quit</a></li> </ul> </li> <li> - <a href="#">Edit</a> + <a href="#Edit">Edit</a> <ul> - <li><a href="#">Copy</a></li> - <li><a href="#">Cut</a></li> + <li><a href="#Copy">Copy</a></li> + <li><a href="#Cut">Cut</a></li> <li class="ui-state-disabled">Paste</li> </ul> </li> <li> - <a href="#">View</a> + <a href="#View">View</a> <ul> - <li><a href="#">Fullscreen</a></li> - <li><a href="#">Fit into view</a></li> + <li><a href="#Fullscreen">Fullscreen</a></li> + <li><a href="#Fit into view">Fit into view</a></li> <li> - <a href="#">Encoding</a> + <a href="#Encoding">Encoding</a> <ul> - <li><a href="#">Auto-detect</a></li> - <li><a href="#">UTF-8</a></li> + <li><a href="#Auto-detect">Auto-detect</a></li> + <li><a href="#UTF-8">UTF-8</a></li> <li> - <a href="#">UTF-16</a> + <a href="#UTF-16">UTF-16</a> <ul> - <li><a href="#">Option 1</a></li> - <li><a href="#">Option 2</a></li> - <li><a href="#">Option 3</a></li> - <li><a href="#">Option 4</a></li> + <li><a href="#Option 1">Option 1</a></li> + <li><a href="#Option 2">Option 2</a></li> + <li><a href="#Option 3">Option 3</a></li> + <li><a href="#Option 4">Option 4</a></li> </ul> </li> </ul> </li> - <li><a href="#">Customize...</a></li> + <li><a href="#Customize...">Customize...</a></li> </ul> </li> </ul> <ul id="bar2" class="menubar-icons"> <li> - <a href="#">File</a> + <a href="#File">File</a> <ul> - <li><a href="#">Open...</a></li> + <li><a href="#Open...">Open...</a></li> <li class="ui-state-disabled">Open recent...</li> - <li><a href="#">Save</a></li> - <li><a href="#">Save as...</a></li> - <li><a href="#">Close</a></li> - <li><a href="#">Quit</a></li> + <li><a href="#Save">Save</a></li> + <li><a href="#Save as...">Save as...</a></li> + <li><a href="#Close">Close</a></li> + <li><a href="#Quit">Quit</a></li> </ul> </li> <li> - <a href="#">Edit</a> + <a href="#Edit">Edit</a> <ul> - <li><a href="#">Copy</a></li> - <li><a href="#">Cut</a></li> + <li><a href="#Copy">Copy</a></li> + <li><a href="#Cut">Cut</a></li> <li class="ui-state-disabled">Paste</li> </ul> </li> <li> - <a href="#">View</a> + <a href="#View">View</a> <ul> - <li><a href="#">Fullscreen</a></li> - <li><a href="#">Fit into view</a></li> + <li><a href="#Fullscreen">Fullscreen</a></li> + <li><a href="#Fit into view">Fit into view</a></li> <li> - <a href="#">Encoding</a> + <a href="#Encoding">Encoding</a> <ul> - <li><a href="#">Auto-detect</a></li> - <li><a href="#">UTF-8</a></li> + <li><a href="#Auto-detect">Auto-detect</a></li> + <li><a href="#UTF-8">UTF-8</a></li> <li> - <a href="#">UTF-16</a> + <a href="#UTF-16">UTF-16</a> <ul> - <li><a href="#">Option 1</a></li> - <li><a href="#">Option 2</a></li> - <li><a href="#">Option 3</a></li> - <li><a href="#">Option 4</a></li> + <li><a href="#Option 1">Option 1</a></li> + <li><a href="#Option 2">Option 2</a></li> + <li><a href="#Option 3">Option 3</a></li> + <li><a href="#Option 4">Option 4</a></li> </ul> </li> </ul> </li> - <li><a href="#">Customize...</a></li> + <li><a href="#Customize...">Customize...</a></li> </ul> </li> </ul> +<!-- <table id="movies" class="ui-widget"> <thead> <tr> @@ -185,19 +178,19 @@ <td class="ui-widget-content"> <ul class="menubar"> <li> - <a href="#">Options</a> + <a href="#Options">Options</a> <ul> - <li><a href="#">Order...</a></li> + <li><a href="#Order...">Order...</a></li> <li class="ui-state-disabled">Write a Review...</li> - <li><a href="#">Find Similar Movies...</a></li> + <li><a href="#Find Similar Movies...">Find Similar Movies...</a></li> <li> - <a href="#">Rate</a> + <a href="#Rate">Rate</a> <ul> - <li><a href="#">5 stars</a></li> - <li><a href="#">4 stars</a></li> - <li><a href="#">3 stars</a></li> - <li><a href="#">2 stars</a></li> - <li><a href="#">1 stars</a></li> + <li><a href="#5 stars">5 stars</a></li> + <li><a href="#4 stars">4 stars</a></li> + <li><a href="#3 stars">3 stars</a></li> + <li><a href="#2 stars">2 stars</a></li> + <li><a href="#1 stars">1 stars</a></li> </ul> </li> </ul> @@ -222,11 +215,20 @@ </tr> </tbody> </table> +--> <div class="ui-widget" style="margin-top:2em; font-family:Arial"> Log: - <div id="log" style="height: 400px; width: 300px; overflow: auto;" class="ui-widget-content"></div> + <div id="log" style="height: 100px; width: 300px; overflow: auto;" class="ui-widget-content"></div> </div> +</div> + +<div class="demo-description"> + +<p>TODO</p> + +</div><!-- End demo-description --> + </body> </html> diff --git a/demos/menubar/index.html b/demos/menubar/index.html new file mode 100644 index 000000000..3eebda2d0 --- /dev/null +++ b/demos/menubar/index.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <title>jQuery UI Menubar Demos</title> + <link href="../demos.css" rel="stylesheet" /> +</head> +<body> + <div class="demos-nav"> + <h4>Examples</h4> + <ul> + <li class="demo-config-on"><a href="default.html">Default functionality</a></li> + </ul> + </div> +</body> +</html> diff --git a/demos/popup/default.html b/demos/popup/default.html new file mode 100644 index 000000000..71b3c8dd2 --- /dev/null +++ b/demos/popup/default.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> +<head> + <title>jQuery UI Popup - Default demo</title> + <link rel="stylesheet" href="../demos.css" /> + <link rel="stylesheet" href="../../themes/base/jquery.ui.all.css" title="ui-theme" /> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <script src="../../ui/jquery.ui.popup.js"></script> + <script> + $(function() { + var selected = { + select: function( event, ui ) { + $( "<div/>" ).text( "Selected: " + ui.item.text() ).appendTo( "#log" ); + $(this).popup("close"); + } + }; + + $("#login-form").popup() + .find(":submit").button().click(function(event) { + event.preventDefault(); + }); + }); + </script> + <style type="text/css"> + .ui-popup { position: absolute; z-index: 5000; } + .ui-menu { width: 200px; } + + /* + table { + border-collapse: collapse; + } + th, td { + padding: 0.5em; + border: 1px solid black; + } + */ + + #login-form { + width: 16em; border: 1px solid gray; border-radius: 5px; + padding: 1em; + box-shadow: 3px 3px 5px -1px rgba(0, 0, 0, 0.5); + background: lightgray; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ddd)); + font-size: 1.3em; outline: none; + } + #login-form label { display: inline-block; width: 5em; } + #login-form .submit { margin-left: 5em; } + </style> +</head> +<body> + +<div class="demo"> + <a href="#login-form">Log In</a> + <div id="login-form" class="ui-widget-content" tabIndex="0"> + <form> + <div> + <label>Username</label> + <input type="username" /> + </div> + <div> + <label>Password</label> + <input type="password" /> + </div> + <div> + <input type="submit" class="submit" value="Login" /> + </div> + </form> + </div> +</div> + +<div class="demo-description"> + +<p>A link to a login form that opens as a popup. [Not quite functional, focus handling needs to get better]</p> + +</div><!-- End demo-description --> + + +</body> +</html> diff --git a/demos/popup/index.html b/demos/popup/index.html new file mode 100644 index 000000000..e69365c98 --- /dev/null +++ b/demos/popup/index.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <title>jQuery UI Popup Demos</title> + <link href="../demos.css" rel="stylesheet" /> +</head> +<body> + <div class="demos-nav"> + <h4>Examples</h4> + <ul> + <li class="demo-config-on"><a href="default.html">Default functionality</a></li> + <li><a href="popup-menu.html">Menu's as popup</a></li> + <li><a href="popup-menu-table.html">Menu's as popup in a table</a></li> + </ul> + </div> +</body> +</html> diff --git a/demos/popup/popup-menu-table.html b/demos/popup/popup-menu-table.html new file mode 100644 index 000000000..a06a38434 --- /dev/null +++ b/demos/popup/popup-menu-table.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> +<head> + <title>jQuery UI Popup - Menu as Popup in table demo</title> + <link rel="stylesheet" href="../demos.css" /> + <link rel="stylesheet" href="../../themes/base/jquery.ui.all.css" title="ui-theme" /> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <script src="../../ui/jquery.ui.popup.js"></script> + <script> + $(function() { + var selected = { + select: function( event, ui ) { + $( "<div/>" ).text( "Selected: " + ui.item.text() ).appendTo( "#log" ); + $(this).popup("close"); + } + }; + + $(".demo td:has(.menubar)").clone().appendTo(".demo tbody tr:not(:first)"); + $("table .menubar > ul").menu(selected).popup().prev().button(); + }); + </script> + <style type="text/css"> + .ui-popup { position: absolute; z-index: 5000; } + .ui-menu { width: 200px; } + + .demo table { + border-collapse: collapse; + } + .demo th, .demo td { + padding: 0.5em; + } + </style> +</head> +<body> + +<div class="demo"> + + <table id="movies" class="ui-widget"> + <thead> + <tr> + <th data-field="Name" class="ui-widget-header">Name</th> + <th data-field="ReleaseYear" class="ui-widget-header">Release Year</th> + <th data-field="AverageRating" class="ui-widget-header">Average Rating</th> + <th class="ui-widget-header"></th> + </tr> + </thead> + <tbody> + <tr> + <td class="ui-widget-content">Red Hot Chili Peppers: Funky Monks</td> + <td class="ui-widget-content">1993</td> + <td class="ui-widget-content">3.6</td> + <td class="ui-widget-content"> + <div class="menubar"> + <a href="#">Options</a> + <ul> + <li><a href="#">Order...</a></li> + <li class="ui-state-disabled">Write a Review...</li> + <li><a href="#">Find Similar Movies...</a></li> + <li> + <a href="#">Rate</a> + <ul> + <li><a href="#">5 stars</a></li> + <li><a href="#">4 stars</a></li> + <li><a href="#">3 stars</a></li> + <li><a href="#">2 stars</a></li> + <li><a href="#">1 stars</a></li> + </ul> + </li> + </ul> + </div> + </td> + </tr> + <tr> + <td class="ui-widget-content">Rod Stewart: Storyteller 1984-1991</td> + <td class="ui-widget-content">1991</td> + <td class="ui-widget-content">3.1</td> + </tr> + <tr> + <td class="ui-widget-content">Stevie Ray Vaughan and Double Trouble: Live at the El Mocambo 1983</td> + <td class="ui-widget-content">1991</td> + <td class="ui-widget-content">3.9</td> + </tr> + <tr> + <td class="ui-widget-content">Spike and Mike's Sick & Twisted Festival of Animation</td> + <td class="ui-widget-content">1997</td> + <td class="ui-widget-content">2.6</td> + </tr> + </tbody> + </table> + + <div id="log"></div> +</div> + + +<div class="demo-description"> + +<p>Poup menu in a table. Works okay standalone, not so much in the scrolling demo view. For that to work better, position() would have to take the closest scrolled parent into account for collision detection.</p> + +</div><!-- End demo-description --> + + +</body> +</html> diff --git a/demos/popup/popup-menu.html b/demos/popup/popup-menu.html new file mode 100644 index 000000000..7152d85bc --- /dev/null +++ b/demos/popup/popup-menu.html @@ -0,0 +1,98 @@ +<!DOCTYPE html> +<html> +<head> + <title>jQuery UI Popup - Popup Menu</title> + <link rel="stylesheet" href="../demos.css" /> + <link rel="stylesheet" href="../../themes/base/jquery.ui.all.css" title="ui-theme" /> + <script src="../../jquery-1.5.1.js"></script> + <script src="../../ui/jquery.ui.core.js"></script> + <script src="../../ui/jquery.ui.widget.js"></script> + <script src="../../ui/jquery.ui.position.js"></script> + <script src="../../ui/jquery.ui.button.js"></script> + <script src="../../ui/jquery.ui.menu.js"></script> + <script src="../../ui/jquery.ui.popup.js"></script> + <script> + $(function() { + function log( msg ) { + $( "<div/>" ).text( msg ).appendTo( "#log" ); + } + var selected = { + select: function( event, ui ) { + log( "Selected: " + ui.item.text() ); + $(this).popup("close"); + } + }; + + $("#button1").button() + .next().menu(selected).popup(); + + $( "#rerun" ) + .button() + .click(function() { + log( "Selected: " + $( this ).text() ); + }) + .next() + .button({ + text: false, + icons: { + primary: "ui-icon-triangle-1-s" + } + }) + .next() + .menu(selected) + .popup({ + trigger: $("#select") + }) + .parent() + .buttonset({ + items: "button" + }); + }); + </script> + <style type="text/css"> + .ui-popup { position: absolute; z-index: 5000; } + .ui-menu { width: 200px; } + </style> +</head> +<body> + +<div class="demo"> + + <button id="button1">Show context menu 1</button> + <ul> + <li><a href="#">Amsterdam</a></li> + <li><a href="#">Anaheim</a></li> + <li><a href="#">Cologne</a></li> + <li><a href="#">Frankfurt</a></li> + <li><a href="#">Magdeburg</a></li> + <li><a href="#">Munich</a></li> + <li><a href="#">Utrecht</a></li> + <li><a href="#">Zurich</a></li> + </ul> + + + <div> + <div> + <button id="rerun">Run last action</button> + <button id="select">Select an action</button> + <ul> + <li><a href="#">Open...</a></li> + <li><a href="#">Save</a></li> + <li><a href="#">Delete</a></li> + </ul> + </div> + </div> + + <div id="log"></div> +</div> + + +<div class="demo-description"> + +<p>Make the popup a menu (or the menu a popup) and you get context menus.</p> + +</div><!-- End demo-description --> + + +</body> +</html> diff --git a/demos/resizable/constrain-area.html b/demos/resizable/constrain-area.html index a7003352c..33539f940 100644 --- a/demos/resizable/constrain-area.html +++ b/demos/resizable/constrain-area.html @@ -10,7 +10,7 @@ <script src="../../ui/jquery.ui.mouse.js"></script> <script src="../../ui/jquery.ui.resizable.js"></script> <link rel="stylesheet" href="../demos.css"> - <style\> + <style> #container { width: 300px; height: 300px; } #container h3 { text-align: center; margin: 0; margin-bottom: 10px; } #resizable { background-position: top left; width: 150px; height: 150px; } diff --git a/demos/tabs/manipulation.html b/demos/tabs/manipulation.html index 8b8ddf4e6..4b79ac714 100644 --- a/demos/tabs/manipulation.html +++ b/demos/tabs/manipulation.html @@ -22,21 +22,15 @@ </style> <script> $(function() { - var $tab_title_input = $( "#tab_title"), - $tab_content_input = $( "#tab_content" ); - var tab_counter = 2; - - // tabs init with a custom tab template and an "add" callback filling in the content - var $tabs = $( "#tabs").tabs({ - tabTemplate: "<li><a href='#{href}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>", - add: function( event, ui ) { - var tab_content = $tab_content_input.val() || "Tab " + tab_counter + " content."; - $( ui.panel ).append( "<p>" + tab_content + "</p>" ); - } - }); + var tabTitle = $( "#tab_title" ), + tabContent = $( "#tab_content" ), + tabTemplate = "<li><a href='#{href}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>", + tabCounter = 2; + + var tabs = $( "#tabs" ).tabs(); // modal dialog init: custom buttons and a "close" callback reseting the form inside - var $dialog = $( "#dialog" ).dialog({ + var dialog = $( "#dialog" ).dialog({ autoOpen: false, modal: true, buttons: { @@ -48,40 +42,43 @@ $( this ).dialog( "close" ); } }, - open: function() { - $tab_title_input.focus(); - }, close: function() { - $form[ 0 ].reset(); + form[ 0 ].reset(); } }); // addTab form: calls addTab function on submit and closes the dialog - var $form = $( "form", $dialog ).submit(function() { + var form = dialog.find( "form" ).submit(function( event ) { addTab(); - $dialog.dialog( "close" ); - return false; + dialog.dialog( "close" ); + event.preventDefault(); }); - // actual addTab function: adds new tab using the title input from the form above + // actual addTab function: adds new tab using the input from the form above function addTab() { - var tab_title = $tab_title_input.val() || "Tab " + tab_counter; - $tabs.tabs( "add", "#tabs-" + tab_counter, tab_title ); - tab_counter++; + var label = tabTitle.val() || "Tab " + tabCounter, + id = "tabs-" + tabCounter, + li = $( tabTemplate.replace( /#\{href\}/g, "#" + id ).replace( /#\{label\}/g, label ) ), + tabContentHtml = tabContent.val() || "Tab " + tabCounter + " content."; + + tabs.find( ".ui-tabs-nav" ).append( li ); + tabs.append( "<div id='" + id + "'><p>" + tabContentHtml + "</p></div>" ); + tabs.tabs( "refresh" ); + tabCounter++; } // addTab button: just opens the dialog $( "#add_tab" ) .button() .click(function() { - $dialog.dialog( "open" ); + dialog.dialog( "open" ); }); // close icon: removing the tab on click - // note: closable tabs gonna be an option in the future - see http://dev.jqueryui.com/ticket/3924 $( "#tabs span.ui-icon-close" ).live( "click", function() { - var index = $( "li", $tabs ).index( $( this ).parent() ); - $tabs.tabs( "remove", index ); + $( this ).closest( "li" ).remove(); + $( "#" + $( this ).prev().attr( "aria-controls" ) ).remove(); + tabs.tabs( "refresh" ); }); }); </script> diff --git a/demos/tooltip/video-player.html b/demos/tooltip/video-player.html index 2e2419760..56003ab5a 100644 --- a/demos/tooltip/video-player.html +++ b/demos/tooltip/video-player.html @@ -10,29 +10,54 @@ <script type="text/javascript" src="../../ui/jquery.ui.tooltip.js"></script> <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script> <script type="text/javascript" src="../../ui/jquery.ui.menu.js"></script> - <script type="text/javascript" src="../../tests/visual/menu/popup.js"></script> + <script type="text/javascript" src="../../ui/jquery.ui.popup.js"></script> + <script type="text/javascript" src="../../ui/jquery.effects.core.js"></script> + <script type="text/javascript" src="../../ui/jquery.effects.blind.js"></script> <link type="text/css" href="../demos.css" rel="stylesheet" /> <script type="text/javascript"> $(function() { + function notify( input ) { + var msg = "Selected " + $.trim($(input).text()); + $("<div/>").appendTo(document.body).text(msg).addClass("notification ui-state-default ui-corner-bottom").position({ + my: "center top", + at: "center top", + of: window + }).show({ + effect: "blind" + }).delay(1000).hide({ + effect: "blind", + duration: "slow" + }, function() { + $(this).remove(); + }); + } + $("ul").menu({ + select: function(event, ui) { + // TODO stop button from handling the click + $(this).popup("close"); + // TODO should probably be handled by poup, see ESCAPE key handler + // affects key handling + $(this).prev().focus(); + notify(ui.item); + } + }).popup(); + $("button").each(function() { $(this).button({ icons: { primary: $(this).data("icon") }, text: !!$(this).attr("title") + }).click(function() { + // TODO don't notify if the button is opening a popup + notify(this); }); }); - $(".set").buttonset(); - - $("ul").menu({ - select: function() { - // would also execute some other action here - $(this).popup("close"); - } - }).popup({ - trigger: $(".menu") + $(".set").buttonset({ + items: "button" }); + $(".demo").tooltip({ position: { my: "center top", @@ -57,6 +82,10 @@ background: rgba(20, 20, 20, 1); color: white; } + + .set { display: inline-block; } + + .notification { position: absolute; display: inline-block; font-size: 2em; padding: .5em; box-shadow: 2px 2px 5px -2px rgba(0,0,0,0.5); } </style> </head> <body> @@ -69,23 +98,23 @@ <button data-icon="ui-icon-circle-arrow-n" title="I like this">Like</button> <button data-icon="ui-icon-circle-arrow-s">I dislike this</button> </span> - <span class="set"> + <div class="set"> <button data-icon="ui-icon-circle-plus" title="Add to Watch Later">Add to</button> <button class="menu" data-icon="ui-icon-triangle-1-s">Add to favorites or playlist</button> - </span> + <ul> + <li> + <a href="#">Favorites</a> + </li> + <li> + <a href="#">Watch Later</a> + </li> + <li> + <a href="#">New Playlist...</a> + </li> + </ul> + </div> <button title="Share this video">Share</button> <button data-icon="ui-icon-alert">Flag as inappropiate</button> - <ul> - <li> - <a href="#">Favorites</a> - </li> - <li> - <a href="#">Watch Later</a> - </li> - <li> - <a href="#">New Playlist...</a> - </li> - </ul> </div> </div> diff --git a/external/qunit.css b/external/qunit.css index 78a7d7e3d..b3c6db523 100644 --- a/external/qunit.css +++ b/external/qunit.css @@ -1,7 +1,17 @@ +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } @@ -27,7 +37,7 @@ font-size: 1.5em; line-height: 1em; font-weight: normal; - + border-radius: 15px 15px 0 0; -moz-border-radius: 15px 15px 0 0; -webkit-border-top-right-radius: 15px; @@ -95,13 +105,13 @@ #qunit-tests ol { margin-top: 0.5em; padding: 0.5em; - + background-color: #fff; - + border-radius: 15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; - + box-shadow: inset 0px 2px 13px #999; -moz-box-shadow: inset 0px 2px 13px #999; -webkit-box-shadow: inset 0px 2px 13px #999; @@ -164,7 +174,7 @@ #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } - + #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999999; } diff --git a/external/qunit.js b/external/qunit.js index 870287148..d56936ee2 100644 --- a/external/qunit.js +++ b/external/qunit.js @@ -1,6 +1,6 @@ -/* +/** * QUnit - A JavaScript Unit Testing Framework - * + * * http://docs.jquery.com/QUnit * * Copyright (c) 2011 John Resig, Jörn Zaefferer @@ -78,7 +78,7 @@ Test.prototype = { // allow utility functions to access the current test environment // TODO why?? QUnit.current_testEnvironment = this.testEnvironment; - + try { if ( !config.pollution ) { saveGlobal(); @@ -114,8 +114,8 @@ Test.prototype = { }, teardown: function() { try { - checkPollution(); this.testEnvironment.teardown.call(this.testEnvironment); + checkPollution(); } catch(e) { QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); } @@ -124,7 +124,7 @@ Test.prototype = { if ( this.expected && this.expected != this.assertions.length ) { QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); } - + var good = 0, bad = 0, tests = id("qunit-tests"); @@ -154,9 +154,9 @@ Test.prototype = { // store result when possible if ( QUnit.config.reorder && defined.sessionStorage ) { if (bad) { - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad) + sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); } else { - sessionStorage.removeItem("qunit-" + this.testName); + sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); } } @@ -166,17 +166,17 @@ Test.prototype = { var b = document.createElement("strong"); b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; - + var a = document.createElement("a"); a.innerHTML = "Rerun"; a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - + addEvent(b, "click", function() { var next = b.nextSibling.nextSibling, display = next.style.display; next.style.display = display === "none" ? "block" : "none"; }); - + addEvent(b, "dblclick", function(e) { var target = e && e.target ? e.target : window.event.srcElement; if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { @@ -217,7 +217,7 @@ Test.prototype = { total: this.assertions.length } ); }, - + queue: function() { var test = this; synchronize(function() { @@ -246,7 +246,7 @@ Test.prototype = { synchronize(run); }; } - + }; var QUnit = { @@ -265,7 +265,7 @@ var QUnit = { QUnit.test(testName, expected, callback, true); }, - + test: function(testName, expected, callback, async) { var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg; @@ -286,13 +286,13 @@ var QUnit = { if ( !validTest(config.currentModule + ": " + testName) ) { return; } - + var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); test.module = config.currentModule; test.moduleTestEnvironment = config.currentModuleTestEnviroment; test.queue(); }, - + /** * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. */ @@ -337,7 +337,7 @@ var QUnit = { notEqual: function(actual, expected, message) { QUnit.push(expected != actual, actual, expected, message); }, - + deepEqual: function(actual, expected, message) { QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); }, @@ -356,34 +356,34 @@ var QUnit = { raises: function(block, expected, message) { var actual, ok = false; - + if (typeof expected === 'string') { message = expected; expected = null; } - + try { block(); } catch (e) { actual = e; } - + if (actual) { // we don't want to validate thrown error if (!expected) { ok = true; - // expected is a regexp + // expected is a regexp } else if (QUnit.objectType(expected) === "regexp") { ok = expected.test(actual); - // expected is a constructor + // expected is a constructor } else if (actual instanceof expected) { ok = true; - // expected is a validation function which returns true is validation passed + // expected is a validation function which returns true is validation passed } else if (expected.call({}, actual) === true) { ok = true; } } - + QUnit.ok(ok, message); }, @@ -412,7 +412,7 @@ var QUnit = { process(); } }, - + stop: function(timeout) { config.semaphore++; config.blocking = true; @@ -438,7 +438,7 @@ var config = { // block until document ready blocking: true, - + // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, @@ -519,7 +519,7 @@ extend(QUnit, { if ( result ) { result.parentNode.removeChild( result ); } - + if ( tests ) { result = document.createElement( "p" ); result.id = "qunit-testresult"; @@ -528,23 +528,23 @@ extend(QUnit, { result.innerHTML = 'Running...<br/> '; } }, - + /** * Resets the test setup. Useful for tests that modify the DOM. - * + * * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. */ reset: function() { if ( window.jQuery ) { - jQuery( "#main, #qunit-fixture" ).html( config.fixture ); + jQuery( "#qunit-fixture" ).html( config.fixture ); } else { - var main = id( 'main' ) || id( 'qunit-fixture' ); + var main = id( 'qunit-fixture' ); if ( main ) { main.innerHTML = config.fixture; } } }, - + /** * Trigger an event on an element. * @@ -564,12 +564,12 @@ extend(QUnit, { elem.fireEvent("on"+type); } }, - + // Safe object type checking is: function( type, obj ) { return QUnit.objectType( obj ) == type; }, - + objectType: function( obj ) { if (typeof obj === "undefined") { return "undefined"; @@ -603,7 +603,7 @@ extend(QUnit, { } return undefined; }, - + push: function(result, actual, expected, message) { var details = { result: result, @@ -611,7 +611,7 @@ extend(QUnit, { actual: actual, expected: expected }; - + message = escapeHtml(message) || (result ? "okay" : "failed"); message = '<span class="test-message">' + message + "</span>"; expected = escapeHtml(QUnit.jsDump.parse(expected)); @@ -629,15 +629,15 @@ extend(QUnit, { } } output += "</table>"; - + QUnit.log(details); - + config.current.assertions.push({ result: !!result, message: output }); }, - + url: function( params ) { params = extend( extend( {}, QUnit.urlParams ), params ); var querystring = "?", @@ -648,7 +648,7 @@ extend(QUnit, { } return window.location.pathname + querystring.slice( 0, -1 ); }, - + // Logging callbacks; all receive a single argument with the listed properties // run test/logs.html for any related changes begin: function() {}, @@ -672,7 +672,7 @@ if ( typeof document === "undefined" || document.readyState === "complete" ) { addEvent(window, "load", function() { QUnit.begin({}); - + // Initialize the config, saving the execution queue var oldconfig = extend({}, config); QUnit.init(); @@ -695,7 +695,7 @@ addEvent(window, "load", function() { window.location = QUnit.url( params ); }); } - + var toolbar = id("qunit-testrunner-toolbar"); if ( toolbar ) { var filter = document.createElement("input"); @@ -730,7 +730,7 @@ addEvent(window, "load", function() { toolbar.appendChild( label ); } - var main = id('main') || id('qunit-fixture'); + var main = id('qunit-fixture'); if ( main ) { config.fixture = main.innerHTML; } @@ -774,13 +774,19 @@ function done() { banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); } - if ( tests ) { + if ( tests ) { id( "qunit-testresult" ).innerHTML = html; } + if ( typeof document !== "undefined" && document.title ) { + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = (config.stats.bad ? "\u2716" : "\u2714") + " " + document.title; + } + QUnit.done( { failed: config.stats.bad, - passed: passed, + passed: passed, total: config.stats.all, runtime: runtime } ); @@ -794,7 +800,7 @@ function validTest( name ) { return true; } - not = filter.charAt( 0 ) === "!"; + var not = filter.charAt( 0 ) === "!"; if ( not ) { filter = filter.slice( 1 ); } @@ -869,7 +875,7 @@ function process() { function saveGlobal() { config.pollution = []; - + if ( config.noglobals ) { for ( var key in window ) { config.pollution.push( key ); @@ -880,17 +886,15 @@ function saveGlobal() { function checkPollution( name ) { var old = config.pollution; saveGlobal(); - - var newGlobals = diff( old, config.pollution ); + + var newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - config.current.expected++; } - var deletedGlobals = diff( config.pollution, old ); + var deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - config.current.expected++; } } @@ -968,7 +972,7 @@ QUnit.equiv = function () { } } } - + var callbacks = function () { // for string, boolean, number and null @@ -1022,13 +1026,13 @@ QUnit.equiv = function () { // b could be an object literal here if ( ! (QUnit.objectType(b) === "array")) { return false; - } - + } + len = a.length; if (len !== b.length) { // safe and faster return false; } - + //track reference to avoid circular references parents.push(a); for (i = 0; i < len; i++) { @@ -1061,7 +1065,7 @@ QUnit.equiv = function () { callers.push(a.constructor); //track reference to avoid circular references parents.push(a); - + for (i in a) { // be strict: don't ensures hasOwnProperty and go deep loop = false; for(j=0;j<parents.length;j++){ @@ -1127,7 +1131,7 @@ QUnit.jsDump = (function() { return '"' + str.toString().replace(/"/g, '\\"') + '"'; }; function literal( o ) { - return o + ''; + return o + ''; }; function join( pre, arr, post ) { var s = jsDump.separator(), @@ -1140,21 +1144,21 @@ QUnit.jsDump = (function() { return [ pre, inner + arr, base + post ].join(s); }; function array( arr ) { - var i = arr.length, ret = Array(i); + var i = arr.length, ret = Array(i); this.up(); while ( i-- ) - ret[i] = this.parse( arr[i] ); + ret[i] = this.parse( arr[i] ); this.down(); return join( '[', ret, ']' ); }; - + var reName = /^function (\w+)/; - + var jsDump = { parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - + type = typeof parser; + return type == 'function' ? parser.call( this, obj ) : type == 'string' ? parser : this.parsers.error; @@ -1205,7 +1209,7 @@ QUnit.jsDump = (function() { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them - quote:quote, + quote:quote, literal:literal, join:join, // @@ -1224,7 +1228,7 @@ QUnit.jsDump = (function() { if ( name ) ret += ' ' + name; ret += '('; - + ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); }, @@ -1242,10 +1246,10 @@ QUnit.jsDump = (function() { node:function( node ) { var open = QUnit.jsDump.HTML ? '<' : '<', close = QUnit.jsDump.HTML ? '>' : '>'; - + var tag = node.nodeName.toLowerCase(), ret = open + tag; - + for ( var a in QUnit.jsDump.DOMAttrs ) { var val = node[QUnit.jsDump.DOMAttrs[a]]; if ( val ) @@ -1255,8 +1259,8 @@ QUnit.jsDump = (function() { }, functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function var l = fn.length; - if ( !l ) return ''; - + if ( !l ) return ''; + var args = Array(l); while ( l-- ) args[l] = String.fromCharCode(97+l);//97 is 'a' @@ -1313,34 +1317,34 @@ function getText( elems ) { * * More Info: * http://ejohn.org/projects/javascript-diff-algorithm/ - * + * * Usage: QUnit.diff(expected, actual) - * + * * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" */ QUnit.diff = (function() { function diff(o, n){ var ns = new Object(); var os = new Object(); - + for (var i = 0; i < n.length; i++) { - if (ns[n[i]] == null) + if (ns[n[i]] == null) ns[n[i]] = { rows: new Array(), o: null }; ns[n[i]].rows.push(i); } - + for (var i = 0; i < o.length; i++) { - if (os[o[i]] == null) + if (os[o[i]] == null) os[o[i]] = { rows: new Array(), n: null }; os[o[i]].rows.push(i); } - + for (var i in ns) { if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { n[ns[i].rows[0]] = { @@ -1353,7 +1357,7 @@ QUnit.diff = (function() { }; } } - + for (var i = 0; i < n.length - 1; i++) { if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && n[i + 1] == o[n[i].row + 1]) { @@ -1367,7 +1371,7 @@ QUnit.diff = (function() { }; } } - + for (var i = n.length - 1; i > 0; i--) { if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && n[i - 1] == o[n[i].row - 1]) { @@ -1381,20 +1385,20 @@ QUnit.diff = (function() { }; } } - + return { o: o, n: n }; } - + return function(o, n){ o = o.replace(/\s+$/, ''); n = n.replace(/\s+$/, ''); var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); var str = ""; - + var oSpace = o.match(/\s+/g); if (oSpace == null) { oSpace = [" "]; @@ -1409,7 +1413,7 @@ QUnit.diff = (function() { else { nSpace.push(" "); } - + if (out.n.length == 0) { for (var i = 0; i < out.o.length; i++) { str += '<del>' + out.o[i] + oSpace[i] + "</del>"; @@ -1421,14 +1425,14 @@ QUnit.diff = (function() { str += '<del>' + out.o[n] + oSpace[n] + "</del>"; } } - + for (var i = 0; i < out.n.length; i++) { if (out.n[i].text == null) { str += '<ins>' + out.n[i] + nSpace[i] + "</ins>"; } else { var pre = ""; - + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { pre += '<del>' + out.o[n] + oSpace[n] + "</del>"; } @@ -1436,7 +1440,7 @@ QUnit.diff = (function() { } } } - + return str; }; })(); diff --git a/tests/unit/autocomplete/autocomplete_events.js b/tests/unit/autocomplete/autocomplete_events.js index 35103d89b..c884733d6 100644 --- a/tests/unit/autocomplete/autocomplete_events.js +++ b/tests/unit/autocomplete/autocomplete_events.js @@ -203,4 +203,22 @@ test("cancel select", function() { }, 50); }); +/* TODO previous fix broke more than it fixed, disabling this for now - messed up regular menu select event +test("blur without selection", function() { + expect(1); + var ac = $("#autocomplete").autocomplete({ + delay: 0, + source: data + }); + stop(); + ac.val("j").keydown(); + setTimeout(function() { + $( ".ui-menu-item" ).first().simulate("mouseover"); + ac.simulate("keydown", { keyCode: $.ui.keyCode.TAB }); + same( ac.val(), "j" ); + start(); + }, 50); +}); +*/ + })(jQuery); diff --git a/tests/unit/button/button_core.js b/tests/unit/button/button_core.js index 5b30aa860..692c40320 100644 --- a/tests/unit/button/button_core.js +++ b/tests/unit/button/button_core.js @@ -67,4 +67,18 @@ test("buttonset", function() { ok( set.children("label:eq(2)").is(".ui-button.ui-corner-right:not(.ui-corner-all)") ); }); +test("buttonset (rtl)", function() { + var parent = $("#radio1").parent(); + // Set to rtl + parent.attr("dir", "rtl"); + + var set = $("#radio1").buttonset(); + ok( set.is(".ui-buttonset") ); + same( set.children(".ui-button").length, 3 ); + same( set.children("input:radio.ui-helper-hidden-accessible").length, 3 ); + ok( set.children("label:eq(0)").is(".ui-button.ui-corner-right:not(.ui-corner-all)") ); + ok( set.children("label:eq(1)").is(".ui-button:not(.ui-corner-all)") ); + ok( set.children("label:eq(2)").is(".ui-button.ui-corner-left:not(.ui-corner-all)") ); +}); + })(jQuery); diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 8d078f099..7ef977604 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -102,16 +102,19 @@ test('enableDisable', function() { ok(inp.next('img').css('opacity') == 1, 'Enable/disable image - image now enabled'); inp.datepicker('destroy'); // Inline - var inl = init('#inl'); + var inl = init('#inl', {changeYear: true}); var dp = $('.ui-datepicker-inline', inl); ok(!inl.datepicker('isDisabled'), 'Enable/disable inline - initially marked as enabled'); ok(!dp.children().is('.ui-state-disabled'), 'Enable/disable inline - not visually disabled initially'); + ok(!dp.find('select').attr('disabled'), 'Enable/disable inline - form element enabled initially'); inl.datepicker('disable'); ok(inl.datepicker('isDisabled'), 'Enable/disable inline - now marked as disabled'); ok(dp.children().is('.ui-state-disabled'), 'Enable/disable inline - visually disabled'); + ok(dp.find('select').attr('disabled'), 'Enable/disable inline - form element disabled'); inl.datepicker('enable'); ok(!inl.datepicker('isDisabled'), 'Enable/disable inline - now marked as enabled'); ok(!dp.children().is('.ui-state-disabled'), 'Enable/disable inline - not visiually disabled'); + ok(!dp.find('select').attr('disabled'), 'Enable/disable inline - form element enabled'); inl.datepicker('destroy'); }); diff --git a/tests/unit/datepicker/datepicker_tickets.js b/tests/unit/datepicker/datepicker_tickets.js index 7e6dc5f00..2b9ce05de 100644 --- a/tests/unit/datepicker/datepicker_tickets.js +++ b/tests/unit/datepicker/datepicker_tickets.js @@ -24,4 +24,9 @@ test('beforeShowDay-getDate', function() { inp.datepicker('hide'); }); +test('Ticket 6827: formatDate day of year calculation is wrong during day lights savings time', function(){ + var time = $.datepicker.formatDate("oo", new Date("2010/03/30 12:00:00 CDT")); + equals(time, "089"); +}); + })(jQuery); diff --git a/tests/unit/effects/effects.html b/tests/unit/effects/effects.html new file mode 100644 index 000000000..0879a98b2 --- /dev/null +++ b/tests/unit/effects/effects.html @@ -0,0 +1,73 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Core Test Suite</title> + + <script src="../../../jquery-1.5.1.js"></script> + <script> + $.uiBackCompat = false; + </script> + <script src="../../../ui/jquery.effects.core.js"></script> + <script src="../../../ui/jquery.effects.blind.js"></script> + <script src="../../../ui/jquery.effects.bounce.js"></script> + <script src="../../../ui/jquery.effects.clip.js"></script> + <script src="../../../ui/jquery.effects.drop.js"></script> + <script src="../../../ui/jquery.effects.explode.js"></script> + <script src="../../../ui/jquery.effects.fade.js"></script> + <script src="../../../ui/jquery.effects.fold.js"></script> + <script src="../../../ui/jquery.effects.highlight.js"></script> + <script src="../../../ui/jquery.effects.pulsate.js"></script> + <script src="../../../ui/jquery.effects.scale.js"></script> + <script src="../../../ui/jquery.effects.shake.js"></script> + <script src="../../../ui/jquery.effects.slide.js"></script> + <script src="../../../ui/jquery.effects.transfer.js"></script> + + <link rel="stylesheet" href="../../../external/qunit.css"> + <script src="../../../external/qunit.js"></script> + <script src="../../jquery.simulate.js"></script> + <script src="../testsuite.js"></script> + <script src="effects_core.js"></script> + + <script src="../swarminject.js"></script> + + <style type="text/css"> + .hidden { + display: none; + } + .test { + background: #000; + border: 0; + } + .testAddBorder { + border: 10px solid #000; + } + .testChangeBackground { + background: #fff; + } + .test h2 { + font-size: 10px; + } + .testChildren h2 { + font-size: 20px; + } + </style> +</head> +<body> + +<h1 id="qunit-header">jQuery UI Effects Test Suite</h1> +<h2 id="qunit-banner"></h2> +<div id="qunit-testrunner-toolbar"></div> +<h2 id="qunit-userAgent"></h2> +<ol id="qunit-tests"> +</ol> + +<div id="qunit-fixture"> + <div class="hidden test"></div> + <div class="animateClass test"> + <h2>Child Element Test</h2> + </div> +</div> + +</body> +</html> diff --git a/tests/unit/effects/effects_core.js b/tests/unit/effects/effects_core.js new file mode 100644 index 000000000..8d2e8f8df --- /dev/null +++ b/tests/unit/effects/effects_core.js @@ -0,0 +1,110 @@ +(function($) { + +function present( value, array, message ) { + QUnit.push( jQuery.inArray( value, array ) !== -1 , value, array, message ); +} + +function notPresent( value, array, message ) { + QUnit.push( jQuery.inArray( value, array ) === -1 , value, array, message ); +} + +// minDuration is used for "short" animate tests where we are only concerned about the final +var minDuration = 15, + + // duration is used for "long" animates where we plan on testing properties during animation + duration = 200, + + // mid is used for testing in the "middle" of the "duration" animations + mid = duration / 2; + +module( "effects.core" ); + +$.each( $.effects.effect, function( effect ) { + if ( effect === "transfer" ) { + return; + } + module( "effect."+effect ); + asyncTest( "show/hide", function() { + var hidden = $( "div.hidden" ); + expect( 8 ); + + var count = 0, + test = 0; + + function queueTest( fn ) { + count++; + var point = count; + return function( next ) { + test++; + equal( point, test, "Queue function fired in order" ); + if ( fn ) { + fn(); + } else { + setTimeout( next, minDuration ); + } + }; + } + + hidden.queue( queueTest() ).show( effect, minDuration, queueTest(function() { + equal( hidden.css("display"), "block", "Hidden is shown after .show(\"" +effect+ "\", time)" ); + })).queue( queueTest() ).hide( effect, minDuration, queueTest(function() { + equal( hidden.css("display"), "none", "Back to hidden after .hide(\"" +effect+ "\", time)" ); + })).queue( queueTest(function(next) { + deepEqual( hidden.queue(), ["inprogress"], "Only the inprogress sentinel remains"); + start(); + })); + }); +}); + +module("animateClass"); + +asyncTest( "animateClass works with borderStyle", function() { + var test = $("div.animateClass"), + count = 0; + expect(3); + test.toggleClass("testAddBorder", minDuration, function() { + test.toggleClass("testAddBorder", minDuration, function() { + equal( test.css("borderLeftStyle"), "none", "None border set" ); + start(); + }); + equal( test.css("borderLeftStyle"), "solid", "None border not immedately set" ); + }); + equal( test.css("borderLeftStyle"), "solid", "Solid border immedately set" ); +}); + +asyncTest( "animateClass works with colors", function() { + var test = $("div.animateClass"), + count = 0; + expect(2); + test.toggleClass("testChangeBackground", duration, function() { + present( test.css("backgroundColor"), [ "#ffffff", "rgb(255, 255, 255)" ], "Color is final" ); + start(); + }); + setTimeout(function() { + var color = test.css("backgroundColor"); + notPresent( color, [ "#000000", "#ffffff", "rgb(0, 0, 0)", "rgb(255,255,255)" ], + "Color is not endpoints in middle." ); + }, mid); +}); + +asyncTest( "animateClass works with children", function() { + var test = $("div.animateClass"), + h2 = test.find("h2"); + + expect(4); + test.toggleClass("testChildren", { children: true, duration: duration, complete: function() { + equal( h2.css("fontSize"), "20px", "Text size is final during complete"); + test.toggleClass("testChildren", duration, function() { + equal( h2.css("fontSize"), "10px", "Text size revertted after class removed"); + start(); + }); + setTimeout(function() { + equal( h2.css("fontSize"), "20px", "Text size unchanged with children: undefined" ); + }, mid); + }}); + setTimeout(function() { + notPresent( h2.css("fontSize"), ["10px","20px"], "Font size is neither endpoint when in middle."); + }, mid); +}); + +})(jQuery); diff --git a/tests/unit/index.html b/tests/unit/index.html index a677023dc..5282d3f27 100644 --- a/tests/unit/index.html +++ b/tests/unit/index.html @@ -59,6 +59,11 @@ <li><a href="position/position.html">Position</a></li> </ul> +<h2>Effects</h2> +<ul> + <li><a href="effects/effects.html">Effects</a></li> +</ul> + </body> </html> diff --git a/tests/unit/tabs/spinner.gif b/tests/unit/tabs/spinner.gif Binary files differdeleted file mode 100644 index 822747576..000000000 --- a/tests/unit/tabs/spinner.gif +++ /dev/null diff --git a/tests/unit/tabs/tabs.html b/tests/unit/tabs/tabs.html index 565a0b427..60f5e972e 100644 --- a/tests/unit/tabs/tabs.html +++ b/tests/unit/tabs/tabs.html @@ -24,7 +24,6 @@ <script src="tabs_events.js"></script> <script src="tabs_methods.js"></script> <script src="tabs_options.js"></script> - <script src="tabs_tickets.js"></script> <script> function tabs_state( tabs ) { @@ -82,16 +81,6 @@ same( actual, expected ); } </script> - <script> - // disable this stale testsuite for testswarm only - var url = window.location.search; - url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) ); - if ( url && url.indexOf("http") == 0 ) { - // reset config to kill previous tests; make sure testsuite.js is loaded afterwards to init the testswarm script - QUnit.init(); - test("tabs", function() { ok(true, "disabled tabs testsuite"); }); - } - </script> <script src="../swarminject.js"></script> </head> <body> diff --git a/tests/unit/tabs/tabs_core.js b/tests/unit/tabs/tabs_core.js index c9b16e3a8..98cb617ec 100644 --- a/tests/unit/tabs/tabs_core.js +++ b/tests/unit/tabs/tabs_core.js @@ -63,4 +63,32 @@ test( "accessibility", function() { // TODO: add tests }); +test( "#3627 - Ajax tab with url containing a fragment identifier fails to load", function() { + expect( 1 ); + + var element = $( "#tabs2" ).tabs({ + active: 2, + beforeLoad: function( event, ui ) { + event.preventDefault(); + ok( /test.html$/.test( ui.ajaxSettings.url ), "should ignore fragment identifier" ); + } + }); +}); + +test( "#4033 - IE expands hash to full url and misinterprets tab as ajax", function() { + expect( 2 ); + + var element = $( "<div><ul><li><a href='#tab'>Tab</a></li></ul><div id='tab'></div></div>" ); + element.appendTo( "#main" ); + element.tabs({ + beforeLoad: function( event, ui ) { + event.preventDefault(); + ok( false, 'should not be an ajax tab'); + } + }); + + equals( element.find( ".ui-tabs-nav a" ).attr( "aria-controls" ), "tab", "aria-contorls attribute is correct" ); + tabs_state( element, 1 ); +}); + }( jQuery ) ); diff --git a/tests/unit/tabs/tabs_deprecated.html b/tests/unit/tabs/tabs_deprecated.html index e71fea3d4..ed6518be3 100644 --- a/tests/unit/tabs/tabs_deprecated.html +++ b/tests/unit/tabs/tabs_deprecated.html @@ -22,7 +22,6 @@ <script src="tabs_events.js"></script> <script src="tabs_methods.js"></script> <script src="tabs_options.js"></script> - <script src="tabs_tickets.js"></script> <script src="tabs_deprecated.js"></script> <script> @@ -81,16 +80,6 @@ same( actual, expected ); } </script> - <script> - // disable this stale testsuite for testswarm only - var url = window.location.search; - url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) ); - if ( url && url.indexOf("http") == 0 ) { - // reset config to kill previous tests; make sure testsuite.js is loaded afterwards to init the testswarm script - QUnit.init(); - test("tabs", function() { ok(true, "disabled tabs testsuite"); }); - } - </script> <script src="../swarminject.js"></script> </head> <body> diff --git a/tests/unit/tabs/tabs_deprecated.js b/tests/unit/tabs/tabs_deprecated.js index 90c43037c..1323c774a 100644 --- a/tests/unit/tabs/tabs_deprecated.js +++ b/tests/unit/tabs/tabs_deprecated.js @@ -1,6 +1,6 @@ (function( $ ) { -module("tabs (deprecated): core"); +module( "tabs (deprecated): core" ); test( "panel ids", function() { expect( 2 ); @@ -20,91 +20,149 @@ test( "panel ids", function() { element.tabs( "option", "active", 2 ); }); -module("tabs (deprecated): options"); +module( "tabs (deprecated): options" ); -test('ajaxOptions', function() { - ok(false, "missing test - untested code is broken code."); -}); +asyncTest( "ajaxOptions", function() { + expect( 1 ); -test('cache', function() { - ok(false, "missing test - untested code is broken code."); + var element = $( "#tabs2" ).tabs({ + ajaxOptions: { + converters: { + "text html": function() { + return "test"; + } + } + } + }); + element.one( "tabsload", function( event, ui ) { + equals( $( ui.panel ).html(), "test" ); + start(); + }); + element.tabs( "option", "active", 2 ); }); -test('idPrefix', function() { - ok(false, "missing test - untested code is broken code."); -}); +asyncTest( "cache", function() { + expect( 5 ); -test('tabTemplate', function() { - ok(false, "missing test - untested code is broken code."); + var element = $( "#tabs2" ).tabs({ + cache: true + }); + element.one( "tabsshow", function( event, ui ) { + tabs_state( element, 0, 0, 1, 0, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + ok( true, "tabsload" ); + + setTimeout(function() { + element.tabs( "option", "active", 0 ); + tabs_state( element, 1, 0, 0, 0, 0 ); + + element.one( "tabsshow", function( event, ui ) { + tabs_state( element, 0, 0, 1, 0, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + ok( false, "should be cached" ); + }); + element.tabs( "option", "active", 2 ); + start(); + }, 1 ); + }); + element.tabs( "option", "active", 2 ); + tabs_state( element, 0, 0, 1, 0, 0 ); }); -test('panelTemplate', function() { - ok(false, "missing test - untested code is broken code."); +test( "idPrefix", function() { + expect( 1 ); + + $( "#tabs2" ) + .one( "tabsbeforeload", function( event, ui ) { + ok( /^testing-\d+$/.test( ui.panel.attr( "id" ) ), "generated id" ); + event.preventDefault(); + }) + .tabs({ + idPrefix: "testing-", + active: 2 + }); }); -test('cookie', function() { - expect(6); +test( "tabTemplate + panelTemplate", function() { + // defaults are tested in the add method test + expect( 11 ); - el = $('#tabs1'); - var cookieName = 'tabs_test', cookieObj = { name: cookieName }; - $.cookie(cookieName, null); // blank state - var cookie = function() { - return parseInt($.cookie(cookieName), 10); - }; + var element = $( "#tabs2" ).tabs({ + tabTemplate: "<li class='customTab'><a href='http://example.com/#{href}'>#{label}</a></li>", + panelTemplate: "<div class='customPanel'></div>" + }); + element.one( "tabsadd", function( event, ui ) { + var anchor = $( ui.tab ); + equal( ui.index, 5, "ui.index" ); + equal( anchor.text(), "New", "ui.tab" ); + equal( anchor.attr( "href" ), "http://example.com/#new", "tab href" ); + ok( anchor.parent().hasClass( "customTab" ), "tab custom class" ); + equal( ui.panel.id, "new", "ui.panel" ); + ok( $( ui.panel ).hasClass( "customPanel" ), "panel custom class" ); + }); + element.tabs( "add", "#new", "New" ); + var tab = element.find( ".ui-tabs-nav li" ).last(), + anchor = tab.find( "a" ); + equals( tab.text(), "New", "label" ); + ok( tab.hasClass( "customTab" ), "tab custom class" ); + equals( anchor.attr( "href" ), "http://example.com/#new", "href" ); + equals( anchor.attr( "aria-controls" ), "new", "aria-controls" ); + ok( element.find( "#new" ).hasClass( "customPanel" ), "panel custom class" ); +}); - el.tabs({ cookie: cookieObj }); - equals(cookie(), 0, 'initial cookie value'); +test( "cookie", function() { + expect( 6 ); - el.tabs('destroy'); - el.tabs({ active: 1, cookie: cookieObj }); - equals(cookie(), 1, 'initial cookie value, from active property'); + var element = $( "#tabs1" ), + cookieName = "tabs_test", + cookieObj = { name: cookieName }; + $.cookie( cookieName, null ); + function cookie() { + return parseInt( $.cookie( cookieName ), 10 ); + } - el.tabs('option', 'active', 2); - equals(cookie(), 2, 'cookie value updated after activating'); + element.tabs({ cookie: cookieObj }); + equals( cookie(), 0, "initial cookie value" ); - el.tabs('destroy'); - $.cookie(cookieName, 1); - el.tabs({ cookie: cookieObj }); - equals(cookie(), 1, 'initial cookie value, from existing cookie'); + element.tabs( "destroy" ); + element.tabs({ active: 1, cookie: cookieObj }); + equals( cookie(), 1, "initial cookie value, from active property" ); - el.tabs('destroy'); - el.tabs({ cookie: cookieObj, collapsible: true }); - el.tabs('option', 'active', false); - equals(cookie(), -1, 'cookie value for all tabs unselected'); + element.tabs( "option", "active", 2 ); + equals( cookie(), 2, "cookie value updated after activating" ); - el.tabs('destroy'); - ok($.cookie(cookieName) === null, 'erase cookie after destroy'); + element.tabs( "destroy" ); + $.cookie( cookieName, 1 ); + element.tabs({ cookie: cookieObj }); + equals( cookie(), 1, "initial cookie value, from existing cookie" ); + element.tabs( "destroy" ); + element.tabs({ cookie: cookieObj, collapsible: true }); + element.tabs( "option", "active", false ); + equals( cookie(), -1, "cookie value for all tabs unselected" ); + + element.tabs( "destroy" ); + ok( $.cookie( cookieName ) === null, "erase cookie after destroy" ); }); +asyncTest( "spinner", function() { + expect( 2 ); -test('spinner', function() { - expect(4); - stop(); - - el = $('#tabs2'); - - el.tabs({ - selected: 2, - load: function() { - // spinner: default spinner - setTimeout(function() { - equals($('li:eq(2) > a > span', el).length, 1, "should restore tab markup after spinner is removed"); - equals($('li:eq(2) > a > span', el).html(), '3', "should restore tab label after spinner is removed"); - el.tabs('destroy'); - el.tabs({ - selected: 2, - spinner: '<img src="spinner.gif" alt="">', - load: function() { - // spinner: image - equals($('li:eq(2) > a > span', el).length, 1, "should restore tab markup after spinner is removed"); - equals($('li:eq(2) > a > span', el).html(), '3', "should restore tab label after spinner is removed"); - start(); - } - }); - }, 1); - } + var element = $( "#tabs2" ).tabs(); + + element.one( "tabsbeforeload", function( event, ui ) { + equals( element.find( ".ui-tabs-nav li:eq(2) em" ).length, 1, "beforeload" ); }); + element.one( "tabsload", function( event, ui ) { + // wait until after the load finishes before checking for the spinner to be removed + setTimeout(function() { + equals( element.find( ".ui-tabs-nav li:eq(2) em" ).length, 0, "load" ); + start(); + }, 1 ); + }); + element.tabs( "option", "active", 2 ); }); test( "selected", function() { @@ -162,6 +220,62 @@ test( "selected", function() { module( "tabs (deprecated): events" ); +asyncTest( "load", function() { + expect( 15 ); + + var tab, panelId, panel, + element = $( "#tabs2" ); + + // init + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + strictEqual( ui.tab, tab[ 0 ], "tab" ); + strictEqual( ui.panel, panel[ 0 ], "panel" ); + equals( $( ui.panel ).find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 1, 0, 0 ); + tabsload1(); + }); + element.tabs({ active: 2 }); + + function tabsload1() { + // .option() + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 3 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + strictEqual( ui.tab, tab[ 0 ], "tab" ); + strictEqual( ui.panel, panel[ 0 ], "panel" ); + equals( $( ui.panel ).find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 0, 1, 0 ); + tabsload2(); + }); + element.tabs( "option", "active", 3 ); + } + + function tabsload2() { + // click, change panel content + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 4 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + equals( event.originalEvent.type, "click", "originalEvent" ); + strictEqual( ui.tab, tab[ 0 ], "tab" ); + strictEqual( ui.panel, panel[ 0 ], "panel" ); + equals( $( ui.panel ).find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 0, 0, 1 ); + start(); + }); + element.find( ".ui-tabs-nav a" ).eq( 4 ).click(); + } +}); + test( "enable", function() { expect( 3 ); @@ -193,45 +307,85 @@ test( "disable", function() { element.tabs( "disable", 1 ); }); -test('show', function() { - expect(5); - var uiObj, eventObj; - el = $('#tabs1').tabs({ - show: function(event, ui) { - uiObj = ui; - eventObj = event; - } +test( "show", function() { + expect( 13 ); + + var element = $( "#tabs1" ).tabs({ + active: false, + collapsible: true + }), + tabs = element.find( ".ui-tabs-nav a" ), + panels = element.find( ".ui-tabs-panel" ); + + // from collapsed + element.one( "tabsshow", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + strictEqual( ui.tab, tabs[ 0 ], "ui.tab" ); + strictEqual( ui.panel, panels[ 0 ], "ui.panel" ); + equal( ui.index, 0, "ui.index" ); + tabs_state( element, 1, 0, 0 ); }); - ok(uiObj !== undefined, 'trigger callback after initialization'); - equals(uiObj.tab, $('a', el)[0], 'contain tab as DOM anchor element'); - equals(uiObj.panel, $('div', el)[0], 'contain panel as DOM div element'); - equals(uiObj.index, 0, 'contain index'); + element.tabs( "option", "active", 0 ); + tabs_state( element, 1, 0, 0 ); - el.find( "li:eq(1) a" ).simulate( "click" ); - equals( eventObj.originalEvent.type, "click", "show triggered by click" ); + // switching tabs + element.one( "tabsshow", function( event, ui ) { + equals( event.originalEvent.type, "click", "originalEvent" ); + strictEqual( ui.tab, tabs[ 1 ], "ui.tab" ); + strictEqual( ui.panel, panels[ 1 ], "ui.panel" ); + equal( ui.index, 1, "ui.index" ); + tabs_state( element, 0, 1, 0 ); + }); + tabs.eq( 1 ).click(); + tabs_state( element, 0, 1, 0 ); + // collapsing + element.one( "tabsshow", function( event, ui ) { + ok( false, "collapsing" ); + }); + element.tabs( "option", "active", false ); + tabs_state( element, 0, 0, 0 ); }); -test('select', function() { - expect(7); +test( "select", function() { + expect( 13 ); - var eventObj; - el = $('#tabs1').tabs({ - select: function(event, ui) { - ok(true, 'select triggered after initialization'); - equals(this, el[0], "context of callback"); - equals(event.type, 'tabsselect', 'event type in callback'); - equals(ui.tab, el.find('a')[1], 'contain tab as DOM anchor element'); - equals(ui.panel, el.find('div')[1], 'contain panel as DOM div element'); - equals(ui.index, 1, 'contain index'); - evenObj = event; - } + var element = $( "#tabs1" ).tabs({ + active: false, + collapsible: true + }), + tabs = element.find( ".ui-tabs-nav a" ), + panels = element.find( ".ui-tabs-panel" ); + + // from collapsed + element.one( "tabsselect", function( event, ui ) { + ok( !( "originalEvent" in event ), "originalEvent" ); + strictEqual( ui.tab, tabs[ 0 ], "ui.tab" ); + strictEqual( ui.panel, panels[ 0 ], "ui.panel" ); + equal( ui.index, 0, "ui.index" ); + tabs_state( element, 0, 0, 0 ); }); - el.tabs('select', 1); + element.tabs( "option", "active", 0 ); + tabs_state( element, 1, 0, 0 ); - el.find( "li:eq(1) a" ).simulate( "click" ); - equals( evenObj.originalEvent.type, "click", "select triggered by click" ); + // switching tabs + element.one( "tabsselect", function( event, ui ) { + equals( event.originalEvent.type, "click", "originalEvent" ); + strictEqual( ui.tab, tabs[ 1 ], "ui.tab" ); + strictEqual( ui.panel, panels[ 1 ], "ui.panel" ); + equal( ui.index, 1, "ui.index" ); + tabs_state( element, 1, 0, 0 ); + }); + tabs.eq( 1 ).click(); + tabs_state( element, 0, 1, 0 ); + + // collapsing + element.one( "tabsselect", function( event, ui ) { + ok( false, "collapsing" ); + }); + element.tabs( "option", "active", false ); + tabs_state( element, 0, 0, 0 ); }); module( "tabs (deprecated): methods" ); @@ -405,4 +559,18 @@ test( "url", function() { element.tabs( "option", "active", 3 ); }); +asyncTest( "abort", function() { + expect( 1 ); + + var element = $( "#tabs2" ).tabs(); + element.one( "tabsbeforeload", function( event, ui ) { + ui.jqXHR.error(function( jqXHR, status ) { + equals( status, "abort", "aborted" ); + start(); + }); + }); + element.tabs( "option", "active", 2 ); + element.tabs( "abort" ); +}); + }( jQuery ) ); diff --git a/tests/unit/tabs/tabs_events.js b/tests/unit/tabs/tabs_events.js index 28925a2b9..12c9bb87b 100644 --- a/tests/unit/tabs/tabs_events.js +++ b/tests/unit/tabs/tabs_events.js @@ -138,15 +138,32 @@ test( "activate", function() { }); test( "beforeLoad", function() { - expect( 21 ); + expect( 32 ); var tab, panelId, panel, - element = $( "#tabs2" ).tabs(); + element = $( "#tabs2" ); - // TODO: init -// element.one( "tabsbeforeload", function( event, ui ) { -// }); -// element.tabs({ active: 2 }); + // init + element.one( "tabsbeforeload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + ok( "abort" in ui.jqXHR, "jqXHR" ); + ok( ui.ajaxSettings.url, "data/test.html", "ajaxSettings.url" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equals( ui.panel.html(), "", "panel html" ); + event.preventDefault(); + tabs_state( element, 0, 0, 1, 0, 0 ); + }); + element.tabs({ active: 2 }); + tabs_state( element, 0, 0, 1, 0, 0 ); + equals( panel.html(), "", "panel html after" ); + element.tabs( "destroy" ); // .option() element.one( "tabsbeforeload", function( event, ui ) { @@ -165,6 +182,7 @@ test( "beforeLoad", function() { event.preventDefault(); tabs_state( element, 1, 0, 0, 0, 0 ); }); + element.tabs(); element.tabs( "option", "active", 2 ); tabs_state( element, 0, 0, 1, 0, 0 ); equals( panel.html(), "", "panel html after" ); @@ -191,8 +209,68 @@ test( "beforeLoad", function() { equals( panel.html(), "<p>testing</p>", "panel html after" ); }); -test( "load", function() { - ok( false, "missing test - untested code is broken code." ); -}); +if ( $.uiBackCompat === false ) { + asyncTest( "load", function() { + expect( 21 ); + + var tab, panelId, panel, + element = $( "#tabs2" ); + + // init + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 2 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equals( ui.panel.find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 1, 0, 0 ); + tabsload1(); + }); + element.tabs({ active: 2 }); + + function tabsload1() { + // .option() + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 3 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equals( ui.panel.find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 0, 1, 0 ); + tabsload2(); + }); + element.tabs( "option", "active", 3 ); + } + + function tabsload2() { + // click, change panel content + element.one( "tabsload", function( event, ui ) { + tab = element.find( ".ui-tabs-nav a" ).eq( 4 ); + panelId = tab.attr( "aria-controls" ); + panel = $( "#" + panelId ); + + equals( event.originalEvent.type, "click", "originalEvent" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + equals( ui.panel.find( "p" ).length, 1, "panel html" ); + tabs_state( element, 0, 0, 0, 0, 1 ); + start(); + }); + element.find( ".ui-tabs-nav a" ).eq( 4 ).click(); + } + }); +} }( jQuery ) ); diff --git a/tests/unit/tabs/tabs_methods.js b/tests/unit/tabs/tabs_methods.js index a6e60a4b3..ec21e2de7 100644 --- a/tests/unit/tabs/tabs_methods.js +++ b/tests/unit/tabs/tabs_methods.js @@ -2,19 +2,10 @@ module( "tabs: methods" ); -test('destroy', function() { - expect(6); - - el = $('#tabs1').tabs({ collapsible: true }); - $('li:eq(2)', el).simulate('mouseover').find('a').focus(); - el.tabs('destroy'); - - ok( el.is(':not(.ui-tabs, .ui-widget, .ui-widget-content, .ui-corner-all, .ui-tabs-collapsible)'), 'remove classes from container'); - ok( $('ul', el).is(':not(.ui-tabs-nav, .ui-helper-reset, .ui-helper-clearfix, .ui-widget-header, .ui-corner-all)'), 'remove classes from list' ); - ok( $('div:eq(1)', el).is(':not(.ui-tabs-panel, .ui-widget-content, .ui-corner-bottom)'), 'remove classes to panel' ); - ok( $('li:eq(0)', el).is(':not(.ui-tabs-active, .ui-state-active, .ui-corner-top)'), 'remove classes from active li'); - ok( $('li:eq(1)', el).is(':not(.ui-state-default, .ui-corner-top)'), 'remove classes from inactive li'); - ok( $('li:eq(2)', el).is(':not(.ui-state-hover, .ui-state-focus)'), 'remove classes from mouseovered or focused li'); +test( "destroy", function() { + domEqual( "#tabs1", function() { + $( "#tabs1" ).tabs().tabs( "destroy" ); + }); }); test( "enable", function() { @@ -154,8 +145,93 @@ test( "refresh", function() { tabs_disabled( element, false ); }); -test('load', function() { - ok(false, "missing test - untested code is broken code."); +asyncTest( "load", function() { + expect( 30 ); + + var element = $( "#tabs2" ).tabs(); + + // load content of inactive tab + // useful for preloading content with custom caching + element.one( "tabsbeforeload", function( event, ui ) { + var tab = element.find( ".ui-tabs-nav a" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + tabs_state( element, 1, 0, 0, 0, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + // TODO: remove wrapping in 2.0 + var uiTab = $( ui.tab ), + uiPanel = $( ui.panel ); + + var tab = element.find( ".ui-tabs-nav a" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( uiTab.size(), 1, "tab size" ); + strictEqual( uiTab[ 0 ], tab[ 0 ], "tab" ); + equals( uiPanel.size(), 1, "panel size" ); + strictEqual( uiPanel[ 0 ], panel[ 0 ], "panel" ); + equals( uiPanel.find( "p" ).length, 1, "panel html" ); + tabs_state( element, 1, 0, 0, 0, 0 ); + setTimeout( tabsload1, 1 ); + }); + element.tabs( "load", 3 ); + tabs_state( element, 1, 0, 0, 0, 0 ); + + function tabsload1() { + // no need to test details of event (tested in events tests) + element.one( "tabsbeforeload", function() { + ok( true, "tabsbeforeload invoked" ); + }); + element.one( "tabsload", function() { + ok( true, "tabsload invoked" ); + setTimeout( tabsload2, 1 ); + }); + element.tabs( "option", "active", 3 ); + tabs_state( element, 0, 0, 0, 1, 0 ); + } + + function tabsload2() { + // reload content of active tab + element.one( "tabsbeforeload", function( event, ui ) { + var tab = element.find( ".ui-tabs-nav a" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( ui.tab.size(), 1, "tab size" ); + strictEqual( ui.tab[ 0 ], tab[ 0 ], "tab" ); + equals( ui.panel.size(), 1, "panel size" ); + strictEqual( ui.panel[ 0 ], panel[ 0 ], "panel" ); + tabs_state( element, 0, 0, 0, 1, 0 ); + }); + element.one( "tabsload", function( event, ui ) { + // TODO: remove wrapping in 2.0 + var uiTab = $( ui.tab ), + uiPanel = $( ui.panel ); + + var tab = element.find( ".ui-tabs-nav a" ).eq( 3 ), + panelId = tab.attr( "aria-controls" ), + panel = $( "#" + panelId ); + + ok( !( "originalEvent" in event ), "originalEvent" ); + equals( uiTab.size(), 1, "tab size" ); + strictEqual( uiTab[ 0 ], tab[ 0 ], "tab" ); + equals( uiPanel.size(), 1, "panel size" ); + strictEqual( uiPanel[ 0 ], panel[ 0 ], "panel" ); + tabs_state( element, 0, 0, 0, 1, 0 ); + start(); + }); + element.tabs( "load", 3 ); + tabs_state( element, 0, 0, 0, 1, 0 ); + } }); }( jQuery ) ); diff --git a/tests/unit/tabs/tabs_options.js b/tests/unit/tabs/tabs_options.js index 691186276..8d61c47ef 100644 --- a/tests/unit/tabs/tabs_options.js +++ b/tests/unit/tabs/tabs_options.js @@ -133,28 +133,81 @@ test( "{ collapsible: true }", function() { tabs_state( element, 0, 0, 0 ); }); -test('disabled', function() { - expect(4); +test( "disabled", function() { + expect( 10 ); - el = $('#tabs1').tabs(); - same(el.tabs('option', 'disabled'), false, "should not disable any tab by default"); + // fully enabled by default + var element = $( "#tabs1" ).tabs(); + tabs_disabled( element, false ); + + // disable single tab + element.tabs( "option", "disabled", [ 1 ] ); + tabs_disabled( element, [ 1 ] ); - el.tabs('option', 'disabled', [ 1 ]); - same(el.tabs('option', 'disabled'), [ 1 ], "should set property"); // everything else is being tested in methods module... + // disabled active tab + element.tabs( "option", "disabled", [ 0, 1 ] ); + tabs_disabled( element, [ 0, 1 ] ); - el.tabs('option', 'disabled', [ 0, 1 ]); - same(el.tabs('option', 'disabled'), [ 0, 1 ], "should disable given tabs, even selected one"); // ... + // disable all tabs + element.tabs( "option", "disabled", [ 0, 1, 2 ] ); + tabs_disabled( element, true ); - el.tabs('option', 'disabled', [ ]); - same(el.tabs('option', 'disabled'), false, "should not disable any tab"); // ... + // enable all tabs + element.tabs( "option", "disabled", [] ); + tabs_disabled( element, false ); }); -test('event', function() { - ok(false, "missing test - untested code is broken code."); +test( "{ event: null }", function() { + expect( 5 ); + + var element = $( "#tabs1" ).tabs({ + event: null + }); + tabs_state( element, 1, 0, 0 ); + + element.tabs( "option", "active", 1 ); + equal( element.tabs( "option", "active" ), 1 ); + tabs_state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-tabs-nav a" ).eq( 2 ).click(); + equal( element.tabs( "option", "active" ), 1 ); + tabs_state( element, 0, 1, 0 ); }); -test('fx', function() { - ok(false, "missing test - untested code is broken code."); +test( "{ event: custom }", function() { + expect( 11 ); + + var element = $( "#tabs1" ).tabs({ + event: "custom1 custom2" + }); + tabs_state( element, 1, 0, 0 ); + + element.find( ".ui-tabs-nav a" ).eq( 1 ).trigger( "custom1" ); + equal( element.tabs( "option", "active" ), 1 ); + tabs_state( element, 0, 1, 0 ); + + // ensure default click handler isn't bound + element.find( ".ui-tabs-nav a" ).eq( 2 ).trigger( "click" ); + equal( element.tabs( "option", "active" ), 1 ); + tabs_state( element, 0, 1, 0 ); + + element.find( ".ui-tabs-nav a" ).eq( 2 ).trigger( "custom2" ); + equal( element.tabs( "option", "active" ), 2 ); + tabs_state( element, 0, 0, 1 ); + + element.tabs( "option", "event", "custom3" ); + + // ensure old event handlers are unbound + element.find( ".ui-tabs-nav a" ).eq( 1 ).trigger( "custom1" ); + equal( element.tabs( "option", "active" ), 2 ); + tabs_state( element, 0, 0, 1 ); + + element.find( ".ui-tabs-nav a" ).eq( 1 ).trigger( "custom3" ); + equal( element.tabs( "option", "active" ), 1 ); + tabs_state( element, 0, 1, 0 ); }); -})(jQuery); +// TODO: add animation tests + +}( jQuery ) ); diff --git a/tests/unit/tabs/tabs_tickets.js b/tests/unit/tabs/tabs_tickets.js deleted file mode 100644 index d7a59cb5b..000000000 --- a/tests/unit/tabs/tabs_tickets.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * tabs_tickets.js - */ -(function($) { - -module("tabs: tickets"); - -test('#3627 - Ajax tab with url containing a fragment identifier fails to load', function() { - // http://dev.jqueryui.com/ticket/3627 - expect(1); - - el = $('#tabs2').tabs({ - active: 2, - beforeLoad: function( event, ui ) { - event.preventDefault(); - ok(/test.html$/.test( ui.ajaxSettings.url ), 'should ignore fragment identifier'); - } - }); -}); - -test('#4033 - IE expands hash to full url and misinterprets tab as ajax', function() { - // http://dev.jqueryui.com/ticket/4033 - expect(1); - - el = $('<div><ul><li><a href="#tab">Tab</a></li></ul><div id="tab"></div></div>'); - el.appendTo('#main'); - el.tabs({ - beforeLoad: function( event, ui ) { - event.preventDefault(); - ok( false, 'should not be an ajax tab'); - } - }); - - equals($('a', el).attr('aria-controls'), 'tab', 'aria-contorls attribute is correct'); -}); - -})(jQuery); diff --git a/tests/unit/widget/widget.html b/tests/unit/widget/widget.html index e74abb317..183537612 100644 --- a/tests/unit/widget/widget.html +++ b/tests/unit/widget/widget.html @@ -14,6 +14,7 @@ <script src="../testsuite.js"></script> <script src="widget_core.js"></script> + <script src="widget_extend.js"></script> <script src="../swarminject.js"></script> </head> diff --git a/tests/unit/widget/widget_extend.js b/tests/unit/widget/widget_extend.js new file mode 100644 index 000000000..fb78ecfb7 --- /dev/null +++ b/tests/unit/widget/widget_extend.js @@ -0,0 +1,104 @@ +test( "$.widget.extend()", function() { + expect( 26 ); + + var settings = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + options = { xnumber2: 1, xstring2: "x", xxx: "newstring" }, + optionsCopy = { xnumber2: 1, xstring2: "x", xxx: "newstring" }, + merged = { xnumber1: 5, xnumber2: 1, xstring1: "peter", xstring2: "x", xxx: "newstring" }, + deep1 = { foo: { bar: true } }, + deep1copy = { foo: { bar: true } }, + deep2 = { foo: { baz: true }, foo2: document }, + deep2copy = { foo: { baz: true }, foo2: document }, + deepmerged = { foo: { bar: true, baz: true }, foo2: document }, + arr = [1, 2, 3], + nestedarray = { arr: arr }, + ret; + + $.widget.extend( settings, options ); + deepEqual( settings, merged, "Check if extended: settings must be extended" ); + deepEqual( options, optionsCopy, "Check if not modified: options must not be modified" ); + + $.widget.extend( deep1, deep2 ); + deepEqual( deep1.foo, deepmerged.foo, "Check if foo: settings must be extended" ); + deepEqual( deep2.foo, deep2copy.foo, "Check if not deep2: options must not be modified" ); + equal( deep1.foo2, document, "Make sure that a deep clone was not attempted on the document" ); + + strictEqual( $.widget.extend({}, nestedarray).arr, arr, "Don't clone arrays" ); + ok( $.isPlainObject( $.widget.extend({ arr: arr }, { arr: {} }).arr ), "Cloned object heve to be an plain object" ); + + var empty = {}; + var optionsWithLength = { foo: { length: -1 } }; + $.widget.extend( empty, optionsWithLength ); + deepEqual( empty.foo, optionsWithLength.foo, "The length property must copy correctly" ); + + empty = {}; + var optionsWithDate = { foo: { date: new Date } }; + $.widget.extend( empty, optionsWithDate ); + deepEqual( empty.foo, optionsWithDate.foo, "Dates copy correctly" ); + + var myKlass = function() {}; + var customObject = new myKlass(); + var optionsWithCustomObject = { foo: { date: customObject } }; + empty = {}; + $.widget.extend( empty, optionsWithCustomObject ); + strictEqual( empty.foo.date, customObject, "Custom objects copy correctly (no methods)" ); + + // Makes the class a little more realistic + myKlass.prototype = { someMethod: function(){} }; + empty = {}; + $.widget.extend( empty, optionsWithCustomObject ); + strictEqual( empty.foo.date, customObject, "Custom objects copy correctly" ); + + ret = $.widget.extend({ foo: 4 }, { foo: new Number(5) } ); + equal( ret.foo, 5, "Wrapped numbers copy correctly" ); + + var nullUndef; + nullUndef = $.widget.extend( {}, options, { xnumber2: null } ); + strictEqual( nullUndef.xnumber2, null, "Check to make sure null values are copied"); + + nullUndef = $.widget.extend( {}, options, { xnumber2: undefined } ); + strictEqual( nullUndef.xnumber2, options.xnumber2, "Check to make sure undefined values are not copied"); + + nullUndef = $.widget.extend( {}, options, { xnumber0: null } ); + strictEqual( nullUndef.xnumber0, null, "Check to make sure null values are inserted"); + + var target = {}; + var recursive = { foo:target, bar:5 }; + $.widget.extend( target, recursive ); + deepEqual( target, { foo: {}, bar: 5 }, "Check to make sure a recursive obj doesn't go never-ending loop by not copying it over" ); + + ret = $.widget.extend( { foo: [] }, { foo: [0] } ); // 1907 + equal( ret.foo.length, 1, "Check to make sure a value with coersion 'false' copies over when necessary to fix #1907" ); + + ret = $.widget.extend( { foo: "1,2,3" }, { foo: [1, 2, 3] } ); + strictEqual( typeof ret.foo, "object", "Check to make sure values equal with coersion (but not actually equal) overwrite correctly" ); + + ret = $.widget.extend( { foo:"bar" }, { foo:null } ); + strictEqual( typeof ret.foo, "object", "Make sure a null value doesn't crash with deep extend, for #1908" ); + + var obj = { foo:null }; + $.widget.extend( obj, { foo:"notnull" } ); + equal( obj.foo, "notnull", "Make sure a null value can be overwritten" ); + + var defaults = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + defaultsCopy = { xnumber1: 5, xnumber2: 7, xstring1: "peter", xstring2: "pan" }, + options1 = { xnumber2: 1, xstring2: "x" }, + options1Copy = { xnumber2: 1, xstring2: "x" }, + options2 = { xstring2: "xx", xxx: "newstringx" }, + options2Copy = { xstring2: "xx", xxx: "newstringx" }, + merged2 = { xnumber1: 5, xnumber2: 1, xstring1: "peter", xstring2: "xx", xxx: "newstringx" }; + + var settings = $.widget.extend( {}, defaults, options1, options2 ); + deepEqual( settings, merged2, "Check if extended: settings must be extended" ); + deepEqual( defaults, defaultsCopy, "Check if not modified: options1 must not be modified" ); + deepEqual( options1, options1Copy, "Check if not modified: options1 must not be modified" ); + deepEqual( options2, options2Copy, "Check if not modified: options2 must not be modified" ); + + var input = { + key: [ 1, 2, 3 ] + } + var output = $.widget.extend( {}, input ); + deepEqual( input, output, "don't clone arrays" ); + input.key[0] = 10; + deepEqual( input, output, "don't clone arrays" ); +}); diff --git a/tests/visual/effects.all.html b/tests/visual/effects.all.html index 1fc35f700..fed35de8a 100644 --- a/tests/visual/effects.all.html +++ b/tests/visual/effects.all.html @@ -146,6 +146,18 @@ </li> <li> + <div class="effect" id="size"> + <p>Size Default Show/Hide</p> + </div> + </li> + + <li> + <div class="effect" id="sizeToggle"> + <p>Size Toggle</p> + </div> + </li> + + <li> <div class="effect" id="slideDown"> <p>Slide down</p> </div> diff --git a/tests/visual/effects.all.js b/tests/visual/effects.all.js index 3ac8968b3..a28c41a89 100644 --- a/tests/visual/effects.all.js +++ b/tests/visual/effects.all.js @@ -14,12 +14,14 @@ $(function() { $(el).bind("click", function() { - $(this).addClass("current").hide(n, o, duration, function() { - var self = this; - window.setTimeout(function() { - $(self).show(n, o, duration, function() { $(this).removeClass("current"); }); - }, wait); - }); + $(this).addClass("current") + // delaying the initial animation makes sure that the queue stays in tact + .delay( 10 ) + .hide( n, o, duration ) + .delay( wait ) + .show( n, o, duration, function() { + $( this ).removeClass("current"); + }); }); }; @@ -28,10 +30,10 @@ $(function() { var el = $(this); el.addClass("current").hide(duration, function() { setTimeout(function() { - el.show(duration, function() { el.removeClass("current") }); + el.show(duration, function() { el.removeClass("current"); }); }, wait); - }) - }) + }); + }); effect("#blindLeft", "blind", { direction: "left" }); effect("#blindUp", "blind", { direction: "up" }); @@ -61,6 +63,16 @@ $(function() { effect("#puff", "puff", { times: 2 }); effect("#scale", "scale", {}); + effect("#size", "size", {}); + $("#sizeToggle").bind("click", function() { + var opts = { to: { width: 300, height: 300 }}; + $(this).addClass('current') + .toggle("size", opts, duration) + .delay(wait) + .toggle("size", opts, duration, function() { + $(this).removeClass("current"); + }); + }); $("#shake").bind("click", function() { $(this).addClass("current").effect("shake", {}, 100, function() { $(this).removeClass("current"); }); }); @@ -82,13 +94,13 @@ $(function() { $("#removeClass").click(function() { $(this).addClass("current").removeClass(function() { window.console && console.log(arguments); - return "current" + return "current"; }, duration); }); $("#toggleClass").click(function() { $(this).toggleClass(function() { window.console && console.log(arguments); - return "current" + return "current"; }, duration); }); }); diff --git a/tests/visual/menu/contextmenu.html b/tests/visual/menu/contextmenu.html deleted file mode 100644 index 63d9877a0..000000000 --- a/tests/visual/menu/contextmenu.html +++ /dev/null @@ -1,191 +0,0 @@ -<!doctype html> -<html> -<head> - <title>Menu Visual Test: Default</title> - <link rel="stylesheet" href="../visual.css" type="text/css" /> - <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css" type="text/css" title="ui-theme" /> - <script type="text/javascript" src="../../../jquery-1.5.1.js"></script> - <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.position.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({ - position: "absolute", - right: 10, - top: 10 - }).appendTo(document.body).themeswitcher(); - - // TODO close other menus when opening a new one - $("button").each(function() { - $(this).next().menu({ - /* top-alignment - position: function(item) { - return { - my: "left top", - at: "right top", - of: item.parent() - } - }, - */ - select: function(event, ui) { - $(this).hide().prev().focus(); - $("#log").append("<div>Selected " + ui.item.text() + "</div>"); - } - }).hide(); - - // equal height - //menu.find("ul").height(menu.height()); - }).click(function(event) { - // required to prevent the click handler below from handling this event - event.stopPropagation(); - var menu = $("#menu" + this.id).menu("blur").show().position({ - my: "left top", - at: "right top", - of: event.pageX > 0 ? event : this - }).focus(); - $(document).one("click", function() { - menu.menu("closeAll").menu("widget").hide(); - }) - }).next().keydown(function(event) { - var menu = $(this).data("menu"); - if (menu.widget().is(":hidden")) - return; - event.stopPropagation(); - switch (event.keyCode) { - case $.ui.keyCode.TAB: - menu.closeAll(); - menu.widget().hide(); - $(this).prev().focus() - break; - case $.ui.keyCode.ESCAPE: - menu.closeAll(); - menu.widget().hide(); - $(this).prev().focus() - break; - default: - - } - }); - }); - </script> - <style> - body { font-size:62.5%; } - .ui-menu { width: 200px; position: absolute; } - #menu3 { height: 200px; overflow: auto; } - </style> -</head> -<body> - -<button id="1">Show context menu 1</button> -<ul id="menu1"> - <li><a href="#">Amsterdam</a></li> - <li><a href="#">Anaheim</a></li> - <li><a href="#">Cologne</a></li> - <li><a href="#">Frankfurt</a></li> - <li><a href="#">Magdeburg</a></li> - <li><a href="#">Munich</a></li> - <li><a href="#">Utrecht</a></li> - <li><a href="#">Zurich</a></li> -</ul> - -<button id="2">Show context menu 2</button> -<ul id="menu2"> - <li> - <a id="a1" href="#">Amsterdam</a> - <ul> - <li><a id="b1" href="#">Aberdeen</a></li> - <li><a id="b2" href="#">Ada</a></li> - <li> - <a href="#">Adamsville</a> - <ul> - <li><a href="#">Anaheim</a></li> - <li> - <a href="#">Cologne</a> - <ul> - <li><a href="#">Mberdeen</a></li> - <li><a href="#">Mda</a></li> - <li><a href="#">Mdamsville</a></li> - <li><a href="#">Mddyston</a></li> - <li><a href="#">Mmesville</a></li> - </ul> - </li> - <li><a href="#">Frankfurt</a></li> - </ul> - </li> - <li><a href="#">Addyston</a></li> - <li><a href="#">Amesville</a></li> - </ul> - </li> - <li><a id="a2" href="#">Anaheim</a></li> - <li><a id="a3" href="#">Cologne</a></li> - <li><a href="#">Frankfurt</a></li> - <li> - <a href="#">Magdeburg</a> - <ul> - <li><a href="#">Mberdeen</a></li> - <li><a href="#">Mda</a></li> - <li><a href="#">Mdamsville</a></li> - <li><a href="#">Mddyston</a></li> - <li><a href="#">Mmesville</a></li> - </ul> - </li> - <li><a href="#">Munich</a></li> - <li><a href="#">Utrecht</a></li> - <li><a href="#">Zurich</a></li> -</ul> - - -<div class="ui-widget" style="margin-top:2em; font-family:Arial"> - Log: - <div id="log" style="height: 400px; width: 300px; overflow: auto;" class="ui-widget-content"></div> -</div> - -<button id="3">Show context menu 3</button> -<ul id="menu3"> - <li><a href="#">Aberdeen</a></li> - <li><a href="#">Ada</a></li> - <li><a href="#">Adamsville</a></li> - <li><a href="#">Addyston</a></li> - <li><a href="#">Adelphi</a></li> - <li><a href="#">Adena</a></li> - <li><a href="#">Adrian</a></li> - <li><a href="#">Akron</a></li> - <li><a href="#">Albany</a></li> - <li><a href="#">Alexandria</a></li> - <li><a href="#">Alger</a></li> - <li><a href="#">Alledonia</a></li> - <li><a href="#">Alliance</a></li> - <li><a href="#">Alpha</a></li> - <li><a href="#">Alvada</a></li> - <li><a href="#">Alvordton</a></li> - <li><a href="#">Amanda</a></li> - <li><a href="#">Amelia</a></li> - <li><a href="#">Amesville</a></li> - <li><a href="#">Aberdeen</a></li> - <li><a href="#">Ada</a></li> - <li><a href="#">Adamsville</a></li> - <li><a href="#">Addyston</a></li> - <li><a href="#">Adelphi</a></li> - <li><a href="#">Adena</a></li> - <li><a href="#">Adrian</a></li> - <li><a href="#">Akron</a></li> - <li><a href="#">Albany</a></li> - <li><a href="#">Alexandria</a></li> - <li><a href="#">Alger</a></li> - <li><a href="#">Alledonia</a></li> - <li><a href="#">Alliance</a></li> - <li><a href="#">Alpha</a></li> - <li><a href="#">Alvada</a></li> - <li><a href="#">Alvordton</a></li> - <li><a href="#">Amanda</a></li> - <li><a href="#">Amelia</a></li> - <li><a href="#">Amesville</a></li> -</ul> - -</body> -</html> diff --git a/tests/visual/menu/popup.html b/tests/visual/menu/popup.html deleted file mode 100644 index 2a712b00a..000000000 --- a/tests/visual/menu/popup.html +++ /dev/null @@ -1,176 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>Visual Test for Popup Utility</title> - <link rel="stylesheet" href="../visual.css" type="text/css" /> - <link rel="stylesheet" href="../../../themes/base/jquery.ui.all.css" type="text/css" title="ui-theme" /> - <script type="text/javascript" src="../../../jquery-1.5.1.js"></script> - <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.position.js"></script> - <script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script> - <script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script> - <script type="text/javascript" src="popup.js"></script> - <!-- - <script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script> - --> - <script type="text/javascript"> - $(function() { - $.fn.themeswitcher && $('<div/>').css({ - position: "absolute", - right: 10, - top: 10 - }).appendTo(document.body).themeswitcher(); - - var selected = { - select: function( event, ui ) { - $( "<div/>" ).text( "Selected: " + ui.item.text() ).appendTo( "#log" ); - $(this).popup("close"); - } - }; - - $("#members-popup").popup(); - - $("#button1").button(); - $("#menu1").menu(selected).popup(); - - $( "#rerun" ) - .button() - .click(function() { - alert( "Running the last action" ); - }) - .next() - .button({ - text: false, - icons: { - primary: "ui-icon-triangle-1-s" - } - }) - .parent() - .buttonset() - .next() - .menu(selected) - .popup({ - trigger: $("#select") - }); - - $("td:has(.menubar)").clone().appendTo("tbody tr:not(:first)"); - $("table .menubar > ul").menu(selected).popup().prev().button(); - }); - </script> - <style type="text/css"> - body { font-size:62.5%; } - - .ui-popup { position: absolute; z-index: 5000; } - .ui-menu { width: 200px; } - - table { - border-collapse: collapse; - } - th, td { - padding: 0.5em; - border: 1px solid black; - } - - #members-popup { - width: 200px; height: 150px; border: 1px solid gray; border-radius: 5px; - box-shadow: 3px 3px 5px -1px rgba(0, 0, 0, 0.5); - background: lightgray; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ddd)); - font-size: 120%; text-align: center; line-height: 150px; outline: none; - } - </style> -</head> -<body> - -<div> - <a href="#members-popup">Project members</a> - <div id="members-popup" tabIndex="0"> - some form controls in here - </div> -</div> - -<button id="button1">Show context menu 1</button> -<ul id="menu1"> - <li><a href="#">Amsterdam</a></li> - <li><a href="#">Anaheim</a></li> - <li><a href="#">Cologne</a></li> - <li><a href="#">Frankfurt</a></li> - <li><a href="#">Magdeburg</a></li> - <li><a href="#">Munich</a></li> - <li><a href="#">Utrecht</a></li> - <li><a href="#">Zurich</a></li> -</ul> - - -<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> - -<table id="movies" class="ui-widget"> - <thead> - <tr> - <th data-field="Name" class="ui-widget-header">Name</th> - <th data-field="ReleaseYear" class="ui-widget-header">Release Year</th> - <th data-field="AverageRating" class="ui-widget-header">Average Rating</th> - <th class="ui-widget-header"></th> - </tr> - </thead> - <tbody> - <tr> - <td class="ui-widget-content">Red Hot Chili Peppers: Funky Monks</td> - <td class="ui-widget-content">1993</td> - <td class="ui-widget-content">3.6</td> - <td class="ui-widget-content"> - <div class="menubar"> - <a href="#">Options</a> - <ul> - <li><a href="#">Order...</a></li> - <li class="ui-state-disabled">Write a Review...</li> - <li><a href="#">Find Similar Movies...</a></li> - <li> - <a href="#">Rate</a> - <ul> - <li><a href="#">5 stars</a></li> - <li><a href="#">4 stars</a></li> - <li><a href="#">3 stars</a></li> - <li><a href="#">2 stars</a></li> - <li><a href="#">1 stars</a></li> - </ul> - </li> - </ul> - </div> - </td> - </tr> - <tr> - <td class="ui-widget-content">Rod Stewart: Storyteller 1984-1991</td> - <td class="ui-widget-content">1991</td> - <td class="ui-widget-content">3.1</td> - </tr> - <tr> - <td class="ui-widget-content">Stevie Ray Vaughan and Double Trouble: Live at the El Mocambo 1983</td> - <td class="ui-widget-content">1991</td> - <td class="ui-widget-content">3.9</td> - </tr> - <tr> - <td class="ui-widget-content">Spike and Mike's Sick & Twisted Festival of Animation</td> - <td class="ui-widget-content">1997</td> - <td class="ui-widget-content">2.6</td> - </tr> - </tbody> -</table> - -<div class="ui-widget" style="margin-top:2em; font-family:Arial"> - Log: - <div id="log" style="height: 400px; width: 300px; overflow: auto;" class="ui-widget-content"></div> -</div> - -</body> -</html> diff --git a/themes/base/jquery.ui.tabs.css b/themes/base/jquery.ui.tabs.css index d4c039f30..b6429cf72 100644 --- a/themes/base/jquery.ui.tabs.css +++ b/themes/base/jquery.ui.tabs.css @@ -9,9 +9,9 @@ */ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } -.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 0; margin: 1px .2em 0 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } -.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: -1px; padding-bottom: 1px; } .ui-tabs .ui-tabs-nav li.ui-tabs-active a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-tabs-loading a { cursor: text; } .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } diff --git a/themes/base/jquery.ui.theme.css b/themes/base/jquery.ui.theme.css index 7d4398465..81f6051f2 100644 --- a/themes/base/jquery.ui.theme.css +++ b/themes/base/jquery.ui.theme.css @@ -237,15 +237,10 @@ ----------------------------------*/ /* Corner radius */ -.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; } -.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } -.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } -.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } -.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } -.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } -.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } -.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } -.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; } /* Overlays */ .ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; } diff --git a/ui/jquery.effects.bounce.js b/ui/jquery.effects.bounce.js index 8da0feb76..9e1117ce9 100644 --- a/ui/jquery.effects.bounce.js +++ b/ui/jquery.effects.bounce.js @@ -12,89 +12,106 @@ */ (function( $, undefined ) { -var rshowhide = /show|hide/; - $.effects.effect.bounce = function(o) { - return this.queue(function() { - - // Create element + return this.queue( function( next ) { var el = $( this ), - props = [ 'position', 'top', 'bottom', 'left', 'right' ], + props = [ "position", "top", "bottom", "left", "right" ], + // defaults: - mode = $.effects.setMode( el, o.mode || 'effect' ), - direction = o.direction || 'up', - distance = o.distance || 20, - times = o.times || 5, - speed = (o.duration || 250), + mode = $.effects.setMode( el, o.mode || "effect" ), + hide = mode === "hide", + show = mode === "show", + direction = o.direction || "up", + distance = o.distance, + times = o.times || 5, + + // number of internal animations + anims = times * 2 + ( show || hide ? 1 : 0 ), + speed = o.duration / anims, + easing = o.easing, + // utility: - ref = ( direction == 'up' || direction == 'down' ) ? 'top' : 'left', - motion = ( direction == 'up' || direction == 'left' ), // true is positive - i, animation, animation1, animation2; - + ref = ( direction === "up" || direction === "down" ) ? "top" : "left", + motion = ( direction === "up" || direction === "left" ), + i, + upAnim, + downAnim, + + // we will need to re-assemble the queue to stack our animations in place + queue = el.queue(), + queuelen = queue.length; + // Avoid touching opacity to prevent clearType and PNG issues in IE - if ( rshowhide.test( mode ) ) { - props.push( 'opacity' ); + if ( show || hide ) { + props.push( "opacity" ); } $.effects.save( el, props ); el.show(); $.effects.createWrapper( el ); // Create Wrapper + // default distance for the BIGGEST bounce is the outer Distance / 3 if ( !distance ) { - distance = el[ ref == 'top' ? 'outerHeight' : 'outerWidth' ]({ margin:true }) / 3; + distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3; } - if ( mode == 'show' ) el.css( 'opacity', 0 ).css( ref, motion ? -distance : distance ); // Shift - if ( mode == 'hide' ) distance = distance / (times * 2); - if ( mode != 'hide' ) times--; - - // Animate - if ( mode == 'show' ) { - animation = { - opacity: 1 - }; - animation[ ref ] = ( motion ? '+=' : '-=' ) + distance; - el.animate( animation, speed / 2, o.easing); - distance = distance / 2; - times--; - }; - - // Bounces - for (i = 0; i < times; i++) { - animation1 = {}; - animation2 = {}; - animation1[ ref ] = ( motion ? '-=' : '+=' ) + distance; - animation2[ ref ] = ( motion ? '+=' : '-=' ) + distance; - el.animate( animation1, speed / 2, o.easing ).animate( animation2, speed / 2, o.easing ); - distance = ( mode == 'hide' ) ? distance * 2 : distance / 2; + + if ( show ) { + downAnim = { opacity: 1 }; + downAnim[ ref ] = 0; + + // if we are showing, force opacity 0 and set the initial position + // then do the "first" animation + el.css( "opacity", 0 ) + .css( ref, motion ? -distance*2 : distance*2 ) + .animate( downAnim, speed, easing ); } - // Last Bounce - if ( mode == 'hide' ) { - animation = { - opacity: 0 - }; - animation[ ref ] = ( motion ? '-=' : '+=' ) + distance; - el.animate( animation, speed / 2, o.easing, function(){ + // start at the smallest distance if we are hiding + if ( hide ) { + distance = distance / Math.pow( 2, times - 1 ); + } + + downAnim = {}; + downAnim[ ref ] = 0; + // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here + for ( i = 0; i < times; i++ ) { + upAnim = {}; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + el.animate( upAnim, speed, easing ) + .animate( downAnim, speed, easing ); + + distance = hide ? distance * 2 : distance / 2; + } + + // Last Bounce when Hiding + if ( hide ) { + upAnim = { opacity: 0 }; + upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance; + + el.animate( upAnim, speed, easing ); + } + + el.queue( function( next ) { + if ( hide ) { el.hide(); - $.effects.restore( el, props ); - $.effects.removeWrapper( el ); - $.isFunction( o.complete ) && o.complete.apply( this, arguments ); - }); - } else { - animation1 = {}; - animation2 = {}; - animation1[ ref ] = ( motion ? '-=' : '+=' ) + distance; - animation2[ ref ] = ( motion ? '+=' : '-=' ) + distance; - el - .animate( animation1, speed / 2, o.easing ) - .animate( animation2, speed / 2, o.easing, function() { - $.effects.restore( el, props ); - $.effects.removeWrapper( el ); - $.isFunction( o.complete ) && o.complete.apply( this, arguments ); - }); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + if ( o.complete ) { + o.complete.apply( el[ 0 ] ); + } + next(); + }); + + // inject all the animations we just queued to be first in line (after "inprogress") + if ( queuelen > 1) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); } - el.dequeue(); + next(); + }); }; diff --git a/ui/jquery.effects.core.js b/ui/jquery.effects.core.js index 581fb0861..638119579 100644 --- a/ui/jquery.effects.core.js +++ b/ui/jquery.effects.core.js @@ -20,8 +20,8 @@ $.effects = { /******************************************************************************/ // override the animation for color styles -$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', - 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'], +$.each(["backgroundColor", "borderBottomColor", "borderLeftColor", + "borderRightColor", "borderTopColor", "borderColor", "color", "outlineColor"], function(i, attr) { $.fx.step[attr] = function(fx) { if (!fx.colorInit) { @@ -30,10 +30,10 @@ function(i, attr) { fx.colorInit = true; } - fx.elem.style[attr] = 'rgb(' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')'; + fx.elem.style[attr] = "rgb(" + + Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + "," + + Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + "," + + Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ")"; }; }); @@ -46,7 +46,7 @@ function getRGB(color) { var result; // Check if we're already dealing with an array of colors - if ( color && color.constructor == Array && color.length == 3 ) + if ( color && color.constructor === Array && color.length === 3 ) return color; // Look for rgb(num,num,num) @@ -67,7 +67,7 @@ function getRGB(color) { // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) - return colors['transparent']; + return colors["transparent"]; // Otherwise, we're most likely dealing with a named color return colors[$.trim(color).toLowerCase()]; @@ -80,7 +80,7 @@ function getColor(elem, attr) { color = $.curCSS(elem, attr); // Keep going until we find an element that has color, or we hit the body - if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") ) + if ( color != "" && color !== "transparent" || $.nodeName(elem, "body") ) break; attr = "backgroundColor"; @@ -146,7 +146,7 @@ var colors = { /****************************** CLASS ANIMATIONS ******************************/ /******************************************************************************/ -var classAnimationActions = [ 'add', 'remove', 'toggle' ], +var classAnimationActions = [ "add", "remove", "toggle" ], shorthandStyles = { border: 1, borderBottom: 1, @@ -157,7 +157,18 @@ var classAnimationActions = [ 'add', 'remove', 'toggle' ], borderWidth: 1, margin: 1, padding: 1 + }, + // prefix used for storing data on .data() + dataSpace = "ec.storage."; + +$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function(_, prop) { + $.fx.step[ prop ] = function( fx ) { + if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { + jQuery.style( fx.elem, prop, fx.end ); + fx.setAttr = true; + } }; +}); function getElementStyles() { var style = document.defaultView @@ -173,55 +184,34 @@ function getElementStyles() { len = style.length; while ( len-- ) { key = style[ len ]; - if ( typeof style[ key ] == 'string' ) { - camelCase = key.replace( /\-(\w)/g, function( all, letter ) { - return letter.toUpperCase(); - }); - newStyle[ camelCase ] = style[ key ]; + if ( typeof style[ key ] === "string" ) { + newStyle[ $.camelCase( key ) ] = style[ key ]; } } } else { for ( key in style ) { - if ( typeof style[ key ] === 'string' ) { + if ( typeof style[ key ] === "string" ) { newStyle[ key ] = style[ key ]; } } } - + return newStyle; } -function filterStyles( styles ) { - var name, value; - for ( name in styles ) { - value = styles[ name ]; - if ( - // ignore null and undefined values - value == null || - // ignore functions (when does this occur?) - $.isFunction( value ) || - // shorthand styles that need to be expanded - name in shorthandStyles || - // ignore scrollbars (break in IE) - ( /scrollbar/ ).test( name ) || - - // only colors or values that can be converted to numbers - ( !( /color/i ).test( name ) && isNaN( parseFloat( value ) ) ) - ) { - delete styles[ name ]; - } - } - - return styles; -} function styleDifference( oldStyle, newStyle ) { - var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459 - name; + var diff = {}, + name, value; for ( name in newStyle ) { - if ( oldStyle[ name ] != newStyle[ name ] ) { - diff[ name ] = newStyle[ name ]; + value = newStyle[ name ]; + if ( oldStyle[ name ] != value ) { + if ( !shorthandStyles[ name ] ) { + if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { + diff[ name ] = value; + } + } } } @@ -229,48 +219,77 @@ function styleDifference( oldStyle, newStyle ) { } $.effects.animateClass = function( value, duration, easing, callback ) { - if ( $.isFunction( easing ) ) { - callback = easing; - easing = null; - } - - return this.queue(function() { - var that = $( this ), - originalStyleAttr = that.attr( 'style' ) || ' ', - originalStyle = filterStyles( getElementStyles.call( this ) ), - newStyle, - className = that.attr( 'class' ); + var o = $.speed( duration, easing, callback ); + + return this.queue( function() { + var animated = $( this ), + baseClass = animated.attr( "class" ), + finalClass, + allAnimations = o.children ? animated.find( "*" ).andSelf() : animated; + + // map the animated objects to store the original styles. + allAnimations = allAnimations.map(function() { + var el = $( this ); + return { + el: el, + originalStyleAttr: el.attr( "style" ) || " ", + start: getElementStyles.call( this ) + }; + }); + // apply class change $.each( classAnimationActions, function(i, action) { if ( value[ action ] ) { - that[ action + 'Class' ]( value[ action ] ); + animated[ action + "Class" ]( value[ action ] ); } }); - newStyle = filterStyles( getElementStyles.call( this ) ); - that.attr( 'class', className ); - - that.animate( styleDifference( originalStyle, newStyle ), { - queue: false, - duration: duration, - easing: easing, - complete: function() { - $.each( classAnimationActions, function( i, action ) { - if ( value[ action ] ) { - that[ action + 'Class' ]( value[ action ] ); - } - }); - // work around bug in IE by clearing the cssText before setting it - if ( typeof that.attr( 'style' ) == 'object' ) { - that.attr( 'style' ).cssText = ''; - that.attr( 'style' ).cssText = originalStyleAttr; - } else { - that.attr( 'style', originalStyleAttr ); + finalClass = animated.attr( "class" ); + + // map all animated objects again - calculate new styles and diff + allAnimations = allAnimations.map(function() { + this.end = getElementStyles.call( this.el[ 0 ] ); + this.diff = styleDifference( this.start, this.end ); + return this; + }); + + // apply original class + animated.attr( "class", baseClass ); + + // map all animated objects again - this time collecting a promise + allAnimations = allAnimations.map(function() { + var styleInfo = this, + dfd = $.Deferred(); + + this.el.animate( this.diff, { + duration: o.duration, + easing: o.easing, + queue: false, + complete: function() { + dfd.resolve( styleInfo ); } - if ( callback ) { - callback.apply( this, arguments ); + }); + return dfd.promise(); + }); + + // once all animations have completed: + $.when.apply( $, allAnimations.get() ).done(function() { + + // set the final class + animated.attr( "class", finalClass ); + + // for each animated element + $.each( arguments, function() { + if ( typeof this.el.attr( "style" ) === "object" ) { + this.el.attr( "style" ).cssText = ""; + this.el.attr( "style" ).cssText = this.originalStyleAttr; + } else { + this.el.attr( "style", this.originalStyleAttr ); } - $.dequeue( this ); - } + }); + + // this is guarnteed to be there if you use jQuery.speed() + // it also handles dequeuing the next anim... + o.complete.call( animated[ 0 ] ); }); }); }; @@ -278,21 +297,21 @@ $.effects.animateClass = function( value, duration, easing, callback ) { $.fn.extend({ _addClass: $.fn.addClass, addClass: function( classNames, speed, easing, callback ) { - return speed ? + return speed ? $.effects.animateClass.apply( this, [{ add: classNames }, speed, easing, callback ]) : this._addClass(classNames); }, _removeClass: $.fn.removeClass, removeClass: function( classNames, speed, easing, callback ) { - return speed ? + return speed ? $.effects.animateClass.apply( this, [{ remove: classNames }, speed, easing, callback ]) : this._removeClass(classNames); }, _toggleClass: $.fn.toggleClass, toggleClass: function( classNames, force, speed, easing, callback ) { - if ( typeof force == "boolean" || force === undefined ) { + if ( typeof force === "boolean" || force === undefined ) { if ( !speed ) { // without speed parameter; return this._toggleClass( classNames, force ); @@ -300,15 +319,15 @@ $.fn.extend({ return $.effects.animateClass.apply( this, [( force ? { add:classNames } : { remove:classNames }), speed, easing, callback ]); } } else { - // without switch parameter; + // without force parameter; return $.effects.animateClass.apply( this, [{ toggle: classNames }, force, speed, easing ]); } }, switchClass: function( remove, add, speed, easing, callback) { - return $.effects.animateClass.apply( this, [{ - add: add, - remove: remove + return $.effects.animateClass.apply( this, [{ + add: add, + remove: remove }, speed, easing, callback ]); } }); @@ -326,7 +345,7 @@ $.extend( $.effects, { save: function( element, set ) { for( var i=0; i < set.length; i++ ) { if ( set[ i ] !== null ) { - element.data( "ec.storage." + set[ i ], element[ 0 ].style[ set[ i ] ] ); + element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); } } }, @@ -335,32 +354,32 @@ $.extend( $.effects, { restore: function( element, set ) { for( var i=0; i < set.length; i++ ) { if ( set[ i ] !== null ) { - element.css( set[ i ], element.data( "ec.storage." + set[ i ] ) ); + element.css( set[ i ], element.data( dataSpace + set[ i ] ) ); } } }, setMode: function( el, mode ) { - if (mode == 'toggle') { - mode = el.is( ':hidden' ) ? 'show' : 'hide'; + if (mode === "toggle") { + mode = el.is( ":hidden" ) ? "show" : "hide"; } return mode; }, // Translates a [top,left] array into a baseline value // this should be a little more flexible in the future to handle a string & hash - getBaseline: function( origin, original ) { + getBaseline: function( origin, original ) { var y, x; switch ( origin[ 0 ] ) { - case 'top': y = 0; break; - case 'middle': y = 0.5; break; - case 'bottom': y = 1; break; + case "top": y = 0; break; + case "middle": y = 0.5; break; + case "bottom": y = 1; break; default: y = origin[ 0 ] / original.height; }; switch ( origin[ 1 ] ) { - case 'left': x = 0; break; - case 'center': x = 0.5; break; - case 'right': x = 1; break; + case "left": x = 0; break; + case "center": x = 0.5; break; + case "right": x = 1; break; default: x = origin[ 1 ] / original.width; }; return { @@ -373,7 +392,7 @@ $.extend( $.effects, { createWrapper: function( element ) { // if the element is already wrapped, return it - if ( element.parent().is( '.ui-effects-wrapper' )) { + if ( element.parent().is( ".ui-effects-wrapper" )) { return element.parent(); } @@ -381,14 +400,14 @@ $.extend( $.effects, { var props = { width: element.outerWidth(true), height: element.outerHeight(true), - 'float': element.css( 'float' ) + "float": element.css( "float" ) }, - wrapper = $( '<div></div>' ) - .addClass( 'ui-effects-wrapper' ) + wrapper = $( "<div></div>" ) + .addClass( "ui-effects-wrapper" ) .css({ - fontSize: '100%', - background: 'transparent', - border: 'none', + fontSize: "100%", + background: "transparent", + border: "none", margin: 0, padding: 0 }); @@ -397,26 +416,26 @@ $.extend( $.effects, { wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element // transfer positioning properties to the wrapper - if ( element.css( 'position' ) == 'static' ) { - wrapper.css({ position: 'relative' }); - element.css({ position: 'relative' }); + if ( element.css( "position" ) === "static" ) { + wrapper.css({ position: "relative" }); + element.css({ position: "relative" }); } else { $.extend( props, { - position: element.css( 'position' ), - zIndex: element.css( 'z-index' ) + position: element.css( "position" ), + zIndex: element.css( "z-index" ) }); - $.each([ 'top', 'left', 'bottom', 'right' ], function(i, pos) { + $.each([ "top", "left", "bottom", "right" ], function(i, pos) { props[ pos ] = element.css( pos ); if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { - props[ pos ] = 'auto'; + props[ pos ] = "auto"; } }); element.css({ - position: 'relative', + position: "relative", top: 0, left: 0, - right: 'auto', - bottom: 'auto' + right: "auto", + bottom: "auto" }); } @@ -424,7 +443,7 @@ $.extend( $.effects, { }, removeWrapper: function( element ) { - if ( element.parent().is( '.ui-effects-wrapper' ) ) + if ( element.parent().is( ".ui-effects-wrapper" ) ) return element.parent().replaceWith( element ); return element; }, @@ -463,7 +482,7 @@ function _normalizeArguments( effect, options, speed, callback ) { } // catch (effect, speed, ?) - if ( $.type( options ) == 'number' || $.fx.speeds[ options ]) { + if ( $.type( options ) === "number" || $.fx.speeds[ options ]) { callback = speed; speed = options; options = {}; @@ -479,9 +498,9 @@ function _normalizeArguments( effect, options, speed, callback ) { if ( options ) { $.extend( effect, options ); } - + speed = speed || options.duration; - effect.duration = $.fx.off ? 0 : typeof speed == 'number' + effect.duration = $.fx.off ? 0 : typeof speed === "number" ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default; effect.complete = callback || options.complete; @@ -494,7 +513,7 @@ function standardSpeed( speed ) { if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { return true; } - + // invalid strings - treat as "normal" speed if ( typeof speed === "string" && !$.effects.effect[ speed ] ) { // TODO: remove in 2.0 (#7115) @@ -503,7 +522,7 @@ function standardSpeed( speed ) { } return true; } - + return false; } @@ -549,7 +568,7 @@ $.fn.extend({ return this._show.apply( this, arguments ); } else { var args = _normalizeArguments.apply( this, arguments ); - args.mode = 'show'; + args.mode = "show"; return this.effect.call( this, args ); } }, @@ -560,7 +579,7 @@ $.fn.extend({ return this._hide.apply( this, arguments ); } else { var args = _normalizeArguments.apply( this, arguments ); - args.mode = 'hide'; + args.mode = "hide"; return this.effect.call( this, args ); } }, @@ -572,7 +591,7 @@ $.fn.extend({ return this.__toggle.apply( this, arguments ); } else { var args = _normalizeArguments.apply( this, arguments ); - args.mode = 'toggle'; + args.mode = "toggle"; return this.effect.call( this, args ); } }, @@ -582,7 +601,7 @@ $.fn.extend({ var style = this.css( key ), val = []; - $.each( [ 'em', 'px', '%', 'pt' ], function( i, unit ) { + $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { if ( style.indexOf( unit ) > 0 ) val = [ parseFloat( style ), unit ]; }); @@ -637,7 +656,7 @@ $.fn.extend({ $.easing.jswing = $.easing.swing; $.extend( $.easing, { - def: 'easeOutQuad', + def: "easeOutQuad", swing: function ( x, t, b, c, d ) { return $.easing[ $.easing.def ]( x, t, b, c, d ); }, @@ -718,9 +737,9 @@ $.extend( $.easing, { a = c; if ( t == 0 ) return b; if ( ( t /= d ) == 1 ) return b+c; - if ( a < Math.abs( c ) ) { - a = c; - s = p / 4; + if ( a < Math.abs( c ) ) { + a = c; + s = p / 4; } else { s = p / ( 2 * Math.PI ) * Math.asin( c / a ); } @@ -733,8 +752,8 @@ $.extend( $.easing, { if ( t == 0 ) return b; if ( ( t /= d ) == 1 ) return b+c; if ( a < Math.abs( c ) ) { - a = c; - s = p / 4; + a = c; + s = p / 4; } else { s = p / ( 2 * Math.PI ) * Math.asin( c / a ); } @@ -746,7 +765,7 @@ $.extend( $.easing, { a = c; if ( t == 0 ) return b; if ( ( t /= d / 2 ) == 2 ) return b+c; - if ( a < Math.abs( c ) ) { + if ( a < Math.abs( c ) ) { a = c; s = p / 4; } else { diff --git a/ui/jquery.effects.fade.js b/ui/jquery.effects.fade.js index 5fa0319c0..ff1cba5f8 100644 --- a/ui/jquery.effects.fade.js +++ b/ui/jquery.effects.fade.js @@ -13,19 +13,26 @@ (function( $, undefined ) { $.effects.effect.fade = function( o ) { - return this.queue( function() { + return this.queue( function( next ) { var el = $( this ), - mode = $.effects.setMode( el, o.mode || 'hide' ); + mode = $.effects.setMode( el, o.mode || 'toggle' ), + hide = mode === "hide"; + el.show(); el.animate({ - opacity: mode + opacity: hide ? 0 : 1 }, { queue: false, duration: o.duration, easing: o.easing, complete: function() { - $.isFunction( o.complete ) && o.complete.apply( this, arguments ); - el.dequeue(); + if ( hide ) { + el.hide(); + } + if ( o.complete ) { + o.complete.call( this ); + } + next(); } }); }); diff --git a/ui/jquery.effects.pulsate.js b/ui/jquery.effects.pulsate.js index b168b6ef5..a0ddf51fd 100644 --- a/ui/jquery.effects.pulsate.js +++ b/ui/jquery.effects.pulsate.js @@ -13,39 +13,54 @@ (function( $, undefined ) { $.effects.effect.pulsate = function( o ) { - return this.queue( function() { + return this.queue( function( next ) { var elem = $( this ), - mode = $.effects.setMode( elem, o.mode || 'show' ), - times = ( ( o.times || 5 ) * 2 ) - 1, - duration = o.duration / 2, - isVisible = elem.is( ':visible' ), + mode = $.effects.setMode( elem, o.mode || "show" ), + show = mode === "show", + hide = mode === "hide", + showhide = ( show || mode === "hide" ), + + // showing or hiding leaves of the "last" animation + anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ), + duration = o.duration / anims, animateTo = 0, + queue = elem.queue(), + queuelen = queue.length, i; - if ( !isVisible ) { - elem.css('opacity', 0).show(); + if ( show || !elem.is(':visible')) { + elem.css( "opacity", 0 ).show(); animateTo = 1; } - if ( ( mode == 'hide' && isVisible ) || ( mode == 'show' && !isVisible ) ) { - times--; - } - - for ( i = 0; i < times; i++ ) { - elem.animate({ - opacity: animateTo + // anims - 1 opacity "toggles" + for ( i = 1; i < anims; i++ ) { + elem.animate({ + opacity: animateTo }, duration, o.easing ); - animateTo = ( animateTo + 1 ) % 2; + animateTo = 1 - animateTo; } - elem.animate({ - opacity: animateTo - }, duration, o.easing, function() { - if (animateTo == 0) { + elem.animate({ + opacity: animateTo + }, duration, o.easing); + + elem.queue( function( next ) { + if ( hide ) { elem.hide(); } - (o.complete && o.complete.apply(this, arguments)); - }).dequeue(); + if ( o.complete ) { + o.complete.apply( this ); + } + next(); + }); + + // We just queued up "anims" animations, we need to put them next in the queue + if ( queuelen > 1) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); + } + next(); }); }; diff --git a/ui/jquery.effects.scale.js b/ui/jquery.effects.scale.js index 8f25ca9a8..843aa2241 100644 --- a/ui/jquery.effects.scale.js +++ b/ui/jquery.effects.scale.js @@ -25,6 +25,7 @@ $.effects.effect.puff = function( o ) { $.extend(o, { effect: 'scale', + queue: false, fade: true, mode: mode, percent: mode == 'hide' ? percent : 100, @@ -36,13 +37,13 @@ $.effects.effect.puff = function( o ) { } }); - elem.effect( o ).dequeue(); + elem.effect( o ); }); }; $.effects.effect.scale = function( o ) { - return this.queue( function() { + return this[ o.queue === false ? "each" : "queue" ]( function() { // Create element var el = $( this ), @@ -62,6 +63,7 @@ $.effects.effect.scale = function( o ) { // We are going to pass this effect to the size effect: options.effect = "size"; + options.queue = false; // Set default origin and restore for show/hide if ( mode != 'effect' ) { @@ -87,14 +89,14 @@ $.effects.effect.scale = function( o ) { }; // Animate - el.effect(options).dequeue(); + el.effect(options); }); }; $.effects.effect.size = function( o ) { - return this.queue( function() { + return this[ o.queue === false ? "each" : "queue" ]( function() { // Create element var el = $( this ), props = [ 'position', 'top', 'bottom', 'left', 'right', 'width', 'height', 'overflow', 'opacity' ], diff --git a/ui/jquery.effects.shake.js b/ui/jquery.effects.shake.js index a1ba1577c..550329ca4 100644 --- a/ui/jquery.effects.shake.js +++ b/ui/jquery.effects.shake.js @@ -17,46 +17,60 @@ $.effects.effect.shake = function( o ) { return this.queue( function() { var el = $( this ), - props = [ 'position', 'top', 'bottom', 'left', 'right' ], - mode = $.effects.setMode( el, o.mode || 'effect' ), - direction = o.direction || 'left', + props = [ "position", "top", "bottom", "left", "right" ], + mode = $.effects.setMode( el, o.mode || "effect" ), + direction = o.direction || "left", distance = o.distance || 20, times = o.times || 3, - speed = o.duration || 140, - ref = (direction == 'up' || direction == 'down') ? 'top' : 'left', - motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg', + anims = times * 2 + 1, + speed = o.duration, + ref = (direction == "up" || direction == "down") ? "top" : "left", + motion = (direction == "up" || direction == "left") ? "pos" : "neg", animation = {}, animation1 = {}, animation2 = {}, - i; + i, - // Adjust - $.effects.save( el, props ); - el.show(); - $.effects.createWrapper( el ); // Create Wrapper + // we will need to re-assemble the queue to stack our animations in place + queue = el.queue(), + queuelen = queue.length; + + + $.effects.save( el, props ); + el.show(); + $.effects.createWrapper( el ); // Animation - animation[ ref ] = ( motion == 'pos' ? '-=' : '+=' ) + distance; - animation1[ ref ] = ( motion == 'pos' ? '+=' : '-=' ) + distance * 2; - animation2[ ref ] = ( motion == 'pos' ? '-=' : '+=' ) + distance * 2; + animation[ ref ] = ( motion == "pos" ? "-=" : "+=" ) + distance; + animation1[ ref ] = ( motion == "pos" ? "+=" : "-=" ) + distance * 2; + animation2[ ref ] = ( motion == "pos" ? "-=" : "+=" ) + distance * 2; // Animate el.animate( animation, speed, o.easing ); // Shakes - for ( i = 1; i < times; i++ ) { + for ( i = 1; i < times; i++ ) { el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing ); }; el .animate( animation1, speed, o.easing ) - .animate( animation, speed / 2, o.easing, function() { + .animate( animation, speed / 2, o.easing ) + .queue( function( next ) { + if ( mode === "hide" ) { + el.hide(); + } + $.effects.restore( el, props ); + $.effects.removeWrapper( el ); + $.isFunction( o.complete ) && o.complete.apply( this, arguments ); + next(); + }); - // Last shake - $.effects.restore( el, props ); - $.effects.removeWrapper( el ); - $.isFunction( o.complete ) && o.complete.apply( this, arguments ); - }) - .dequeue(); + // inject all the animations we just queued to be first in line (after "inprogress") + if ( queuelen > 1) { + queue.splice.apply( queue, + [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) ); + } + el.dequeue(); }); }; diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index 526eb3869..0e62ccfdb 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -62,6 +62,7 @@ $.widget( "ui.autocomplete", { }) .bind( "keydown.autocomplete", function( event ) { if ( self.options.disabled || self.element.attr( "readonly" ) ) { + suppressKeyPress = true; return; } @@ -69,17 +70,21 @@ $.widget( "ui.autocomplete", { var keyCode = $.ui.keyCode; switch( event.keyCode ) { case keyCode.PAGE_UP: + suppressKeyPress = true; self._move( "previousPage", event ); break; case keyCode.PAGE_DOWN: + suppressKeyPress = true; self._move( "nextPage", event ); break; case keyCode.UP: + suppressKeyPress = true; self._move( "previous", event ); // prevent moving cursor to beginning of text field in some browsers event.preventDefault(); break; case keyCode.DOWN: + suppressKeyPress = true; self._move( "next", event ); // prevent moving cursor to end of text field in some browsers event.preventDefault(); @@ -121,7 +126,29 @@ $.widget( "ui.autocomplete", { if ( suppressKeyPress ) { suppressKeyPress = false; event.preventDefault(); + return; } + + // replicate some key handlers to allow them to repeat in Firefox and Opera + var keyCode = $.ui.keyCode; + switch( event.keyCode ) { + case keyCode.PAGE_UP: + self._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + self._move( "nextPage", event ); + break; + case keyCode.UP: + self._move( "previous", event ); + // prevent moving cursor to beginning of text field in some browsers + event.preventDefault(); + break; + case keyCode.DOWN: + self._move( "next", event ); + // prevent moving cursor to end of text field in some browsers + event.preventDefault(); + break; + } }) .bind( "focus.autocomplete", function() { if ( self.options.disabled ) { diff --git a/ui/jquery.ui.button.js b/ui/jquery.ui.button.js index 031ac2091..e5eedc3e5 100644 --- a/ui/jquery.ui.button.js +++ b/ui/jquery.ui.button.js @@ -13,7 +13,7 @@ */ (function( $, undefined ) { -var lastActive, +var lastActive, startXPos, startYPos, clickDragged, baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", stateClasses = "ui-state-hover ui-state-active ", typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", @@ -112,13 +112,36 @@ $.widget( "ui.button", { if ( toggleButton ) { this.element.bind( "change.button", function() { + if ( clickDragged ) { + return; + } self.refresh(); }); + // if mouse moves between mousedown and mouseup (drag) set clickDragged flag + // prevents issue where button state changes but checkbox/radio checked state + // does not in Firefox (see ticket #6970) + this.buttonElement + .bind( "mousedown.button", function( event ) { + if ( options.disabled ) { + return; + } + clickDragged = false; + startXPos = event.pageX; + startYPos = event.pageY; + }) + .bind( "mouseup.button", function( event ) { + if ( options.disabled ) { + return; + } + if ( startXPos !== event.pageX || startYPos !== event.pageY ) { + clickDragged = true; + } + }); } if ( this.type === "checkbox" ) { this.buttonElement.bind( "click.button", function() { - if ( options.disabled ) { + if ( options.disabled || clickDragged ) { return false; } $( this ).toggleClass( "ui-state-active" ); @@ -126,7 +149,7 @@ $.widget( "ui.button", { }); } else if ( this.type === "radio" ) { this.buttonElement.bind( "click.button", function() { - if ( options.disabled ) { + if ( options.disabled || clickDragged ) { return false; } $( this ).addClass( "ui-state-active" ); @@ -185,6 +208,7 @@ $.widget( "ui.button", { // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can // be overridden by individual plugins this._setOption( "disabled", options.disabled ); + this._resetButton(); }, _determineButtonType: function() { @@ -250,6 +274,7 @@ $.widget( "ui.button", { } else { this.element.removeAttr( "disabled" ); } + return; } this._resetButton(); }, @@ -352,6 +377,8 @@ $.widget( "ui.buttonset", { }, refresh: function() { + var ltr = this.element.css( "direction" ) === "ltr"; + this.buttons = this.element.find( this.options.items ) .filter( ":ui-button" ) .button( "refresh" ) @@ -364,10 +391,10 @@ $.widget( "ui.buttonset", { }) .removeClass( "ui-corner-all ui-corner-left ui-corner-right" ) .filter( ":first" ) - .addClass( "ui-corner-left" ) + .addClass( ltr ? "ui-corner-left" : "ui-corner-right" ) .end() .filter( ":last" ) - .addClass( "ui-corner-right" ) + .addClass( ltr ? "ui-corner-right" : "ui-corner-left" ) .end() .end(); }, diff --git a/ui/jquery.ui.datepicker.js b/ui/jquery.ui.datepicker.js index 7861cd5a9..021041202 100644 --- a/ui/jquery.ui.datepicker.js +++ b/ui/jquery.ui.datepicker.js @@ -364,6 +364,8 @@ $.extend(Datepicker.prototype, { else if (nodeName == 'div' || nodeName == 'span') { var inline = $target.children('.' + this._inlineClass); inline.children().removeClass('ui-state-disabled'); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + removeAttr("disabled"); } this._disabledInputs = $.map(this._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry @@ -387,6 +389,8 @@ $.extend(Datepicker.prototype, { else if (nodeName == 'div' || nodeName == 'span') { var inline = $target.children('.' + this._inlineClass); inline.children().addClass('ui-state-disabled'); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + attr("disabled", "disabled"); } this._disabledInputs = $.map(this._disabledInputs, function(value) { return (value == target ? null : value); }); // delete entry @@ -455,7 +459,8 @@ $.extend(Datepicker.prototype, { inst.settings.maxDate = this._formatDate(inst, maxDate); this._attachments($(target), inst); this._autoSize(inst); - this._setDateDatepicker(target, date); + this._setDate(inst, date); + this._updateAlternate(inst); this._updateDatepicker(inst); } }, @@ -705,7 +710,7 @@ $.extend(Datepicker.prototype, { var origyearshtml = inst.yearshtml; setTimeout(function(){ //assure that inst.yearshtml didn't change. - if( origyearshtml === inst.yearshtml ){ + if( origyearshtml === inst.yearshtml && inst.yearshtml ){ inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml); } origyearshtml = inst.yearshtml = null; @@ -1188,7 +1193,7 @@ $.extend(Datepicker.prototype, { break; case 'o': output += formatNumber('o', - (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3); + Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); break; case 'm': output += formatNumber('m', date.getMonth() + 1, 2); @@ -1609,14 +1614,9 @@ $.extend(Datepicker.prototype, { '>' + year + '</option>'; } inst.yearshtml += '</select>'; - //when showing there is no need for later update - if( ! $.browser.mozilla ){ - html += inst.yearshtml; - inst.yearshtml = null; - } else { - // will be replaced later with inst.yearshtml - html += '<select class="ui-datepicker-year"><option value="' + drawYear + '" selected="selected">' + drawYear + '</option></select>'; - } + + html += inst.yearshtml; + inst.yearshtml = null; } } html += this._get(inst, 'yearSuffix'); diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js index 8423d1164..496118761 100644 --- a/ui/jquery.ui.dialog.js +++ b/ui/jquery.ui.dialog.js @@ -281,10 +281,10 @@ $.widget("ui.dialog", { options = self.options, uiDialog = self.uiDialog; - self.overlay = options.modal ? new $.ui.dialog.overlay( self ) : null; self._size(); self._position( options.position ); uiDialog.show( options.show ); + self.overlay = options.modal ? new $.ui.dialog.overlay( self ) : null; self.moveToTop( true ); // prevent tabbing out of modal dialogs @@ -755,8 +755,8 @@ $.extend( $.ui.dialog.overlay, { height: function() { var scrollHeight, offsetHeight; - // handle IE 6 - if ( $.browser.msie && $.browser.version < 7 ) { + // handle IE + if ( $.browser.msie ) { scrollHeight = Math.max( document.documentElement.scrollHeight, document.body.scrollHeight diff --git a/ui/jquery.ui.draggable.js b/ui/jquery.ui.draggable.js index 774ea4380..c16833eb0 100644 --- a/ui/jquery.ui.draggable.js +++ b/ui/jquery.ui.draggable.js @@ -261,7 +261,7 @@ $.widget("ui.draggable", $.ui.mouse, { _createHelper: function(event) { var o = this.options; - var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element); + var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element); if(!helper.parents('body').length) helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); @@ -355,8 +355,8 @@ $.widget("ui.draggable", $.ui.mouse, { var o = this.options; if(o.containment == 'parent') o.containment = this.helper[0].parentNode; if(o.containment == 'document' || o.containment == 'window') this.containment = [ - (o.containment == 'document' ? 0 : $(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left, - (o.containment == 'document' ? 0 : $(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top, + o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left, + o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top, (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top ]; @@ -436,10 +436,11 @@ $.widget("ui.draggable", $.ui.mouse, { } if(o.grid) { - var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; + //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) + var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; + var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; } @@ -580,7 +581,7 @@ $.ui.plugin.add("draggable", "connectToSortable", { //Now we fake the start of dragging for the sortable instance, //by cloning the list group item, appending it to the sortable and using it as inst.currentItem //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) - this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true); + this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true); this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it this.instance.options.helper = function() { return ui.helper[0]; }; diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 0841018af..41b2e7a1a 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -45,8 +45,6 @@ $.widget("ui.menu", { if ( !item.length ) { return; } - // temporary - event.preventDefault(); // it's possible to click an item without hovering it (#7085) if ( !self.active || ( self.active[ 0 ] !== item[ 0 ] ) ) { self.focus( event, item ); @@ -235,15 +233,15 @@ $.widget("ui.menu", { 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" ), + paddingTop = parseFloat( $.curCSS( this.element[0], "paddingTop", true) ) || 0, + offset = item.offset().top - this.element.offset().top - borderTop - paddingTop, + scroll = this.element.scrollTop(), elementHeight = this.element.height(), itemHeight = item.height(); if ( offset < 0 ) { - this.element.attr( "scrollTop", scroll + offset ); + this.element.scrollTop( scroll + offset ); } else if ( offset + itemHeight > elementHeight ) { - this.element.attr( "scrollTop", scroll + offset - elementHeight + itemHeight ); + this.element.scrollTop( scroll + offset - elementHeight + itemHeight ); } } @@ -408,7 +406,8 @@ $.widget("ui.menu", { }, _hasScroll: function() { - return this.element.height() < this.element.attr( "scrollHeight" ); + // TODO: just use .prop() when we drop support for jQuery <1.6 + return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]( "scrollHeight" ); }, select: function( event ) { diff --git a/tests/visual/menu/menubar.js b/ui/jquery.ui.menubar.js index b9abacb9b..2879d079c 100644 --- a/tests/visual/menu/menubar.js +++ b/ui/jquery.ui.menubar.js @@ -1,7 +1,17 @@ /* - * jQuery UI menubar - * - * TODO move to jquery.ui.menubar.js + * jQuery UI Menubar @VERSION + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menubar + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + * jquery.ui.menu.js */ (function( $ ) { diff --git a/ui/jquery.ui.mouse.js b/ui/jquery.ui.mouse.js index bfe4a7578..0bd38db85 100644 --- a/ui/jquery.ui.mouse.js +++ b/ui/jquery.ui.mouse.js @@ -12,6 +12,11 @@ */ (function( $, undefined ) { +var mouseHandled = false; +$(document).mousedown(function(e) { + mouseHandled = false; +}); + $.widget("ui.mouse", { options: { cancel: ':input,option', @@ -44,9 +49,7 @@ $.widget("ui.mouse", { _mouseDown: function(event) { // don't let more than one widget handle mouseStart - // TODO: figure out why we have to use originalEvent - event.originalEvent = event.originalEvent || {}; - if (event.originalEvent.mouseHandled) { return; } + if(mouseHandled) {return}; // we may have missed mouseup (out of window) (this._mouseStarted && this._mouseUp(event)); @@ -92,7 +95,8 @@ $.widget("ui.mouse", { .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); event.preventDefault(); - event.originalEvent.mouseHandled = true; + + mouseHandled = true; return true; }, diff --git a/tests/visual/menu/popup.js b/ui/jquery.ui.popup.js index 08088c994..10361a35c 100644 --- a/tests/visual/menu/popup.js +++ b/ui/jquery.ui.popup.js @@ -1,5 +1,16 @@ /* - * jQuery UI popup utility + * jQuery UI Popup @VERSION + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Popup + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js */ (function($) { @@ -38,10 +49,21 @@ $.widget( "ui.popup", { this._bind(this.options.trigger, { keydown: function( event ) { - // prevent space-to-open to scroll the page - if (event.keyCode == $.ui.keyCode.SPACE) { + // prevent space-to-open to scroll the page, only hapens for anchor ui.button + if ( this.options.trigger.is( "a:ui-button" ) && event.keyCode == $.ui.keyCode.SPACE) { event.preventDefault() } + // TODO handle SPACE to open popup? only when not handled by ui.button + if ( event.keyCode == $.ui.keyCode.SPACE && this.options.trigger.is("a:not(:ui-button)") ) { + this.options.trigger.trigger( "click", event ); + } + // translate keydown to click + // opens popup and let's tooltip hide itself + if ( event.keyCode == $.ui.keyCode.DOWN ) { + // prevent scrolling + event.preventDefault(); + this.options.trigger.trigger( "click", event ); + } }, click: function( event ) { event.preventDefault(); @@ -50,6 +72,7 @@ $.widget( "ui.popup", { return; } var that = this; + clearTimeout( this.closeTimer ); setTimeout(function() { that.open( event ); }, 1); @@ -57,17 +80,25 @@ $.widget( "ui.popup", { }); this._bind(this.element, { - // TODO also triggered when open and clicking the trigger again - // figure out how to close in that case, while still closing on regular blur - //blur: "close" + // TODO use focusout so that element itself doesn't need to be focussable + blur: function( event ) { + var that = this; + // use a timer to allow click to clear it and letting that + // handle the closing instead of opening again + that.closeTimer = setTimeout( function() { + that.close( event ); + }, 100); + } }); this._bind({ // TODO only triggerd on element if it can receive focus // bind to document instead? + // either element itself or a child should be focusable keyup: function( event ) { if (event.keyCode == $.ui.keyCode.ESCAPE && this.element.is( ":visible" )) { this.close( event ); + // TODO move this to close()? would allow menu.select to call popup.close, and get focus back to trigger this.options.trigger.focus(); } } @@ -111,8 +142,13 @@ $.widget( "ui.popup", { .attr( "aria-hidden", false ) .attr( "aria-expanded", true ) .position( position ) + // TODO find a focussable child, otherwise put focus on element, add tabIndex=0 if not focussable .focus(); + if (this.element.is(":ui-menu")) { + this.element.menu("focus", event, this.element.children( "li" ).first() ); + } + // take trigger out of tab order to allow shift-tab to skip trigger this.options.trigger.attr("tabindex", -1); diff --git a/ui/jquery.ui.resizable.js b/ui/jquery.ui.resizable.js index 1e1706a9d..e0579ef84 100644 --- a/ui/jquery.ui.resizable.js +++ b/ui/jquery.ui.resizable.js @@ -176,10 +176,12 @@ $.widget("ui.resizable", $.ui.mouse, { $(this.element) .addClass("ui-resizable-autohide") .hover(function() { + if (o.disabled) return; $(this).removeClass("ui-resizable-autohide"); self._handles.show(); }, function(){ + if (o.disabled) return; if (!self.resizing) { $(this).addClass("ui-resizable-autohide"); self._handles.hide(); diff --git a/ui/jquery.ui.slider.js b/ui/jquery.ui.slider.js index eb6545f62..f0b01d7c0 100644 --- a/ui/jquery.ui.slider.js +++ b/ui/jquery.ui.slider.js @@ -36,7 +36,11 @@ $.widget( "ui.slider", $.ui.mouse, { _create: function() { var self = this, - o = this.options; + o = this.options, + existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), + handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>", + handleCount = ( o.values && o.values.length ) || 1, + handles = []; this._keySliding = false; this._mouseSliding = false; @@ -50,57 +54,35 @@ $.widget( "ui.slider", $.ui.mouse, { " ui-slider-" + this.orientation + " ui-widget" + " ui-widget-content" + - " ui-corner-all" ); - - if ( o.disabled ) { - this.element.addClass( "ui-slider-disabled ui-disabled" ); - } + " ui-corner-all" + + ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) ); this.range = $([]); if ( o.range ) { if ( o.range === true ) { - this.range = $( "<div></div>" ); if ( !o.values ) { o.values = [ this._valueMin(), this._valueMin() ]; } if ( o.values.length && o.values.length !== 2 ) { o.values = [ o.values[0], o.values[0] ]; } - } else { - this.range = $( "<div></div>" ); - } - - this.range - .appendTo( this.element ) - .addClass( "ui-slider-range" ); - - if ( o.range === "min" || o.range === "max" ) { - this.range.addClass( "ui-slider-range-" + o.range ); } - // note: this isn't the most fittingly semantic framework class for this element, - // but worked best visually with a variety of themes - this.range.addClass( "ui-widget-header" ); - } - - if ( $( ".ui-slider-handle", this.element ).length === 0 ) { - $( "<a href='#'></a>" ) + this.range = $( "<div></div>" ) .appendTo( this.element ) - .addClass( "ui-slider-handle" ); + .addClass( "ui-slider-range" + + // note: this isn't the most fittingly semantic framework class for this element, + // but worked best visually with a variety of themes + " ui-widget-header" + + ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) ); } - if ( o.values && o.values.length ) { - while ( $(".ui-slider-handle", this.element).length < o.values.length ) { - $( "<a href='#'></a>" ) - .appendTo( this.element ) - .addClass( "ui-slider-handle" ); - } + for ( var i = existingHandles.length; i < handleCount; i += 1 ) { + handles.push( handle ); } - this.handles = $( ".ui-slider-handle", this.element ) - .addClass( "ui-state-default" + - " ui-corner-all" ); + this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) ); this.handle = this.handles.eq( 0 ); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index b8cca7dcd..2709175b5 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -11,11 +11,11 @@ * jquery.ui.core.js * jquery.ui.widget.js */ -(function($) { +(function( $ ) { -$.widget('ui.spinner', { +$.widget( "ui.spinner", { defaultElement: "<input>", - widgetEventPrefix: "spin", + widgetEventPrefix: "spin", options: { incremental: true, max: null, @@ -25,218 +25,227 @@ $.widget('ui.spinner', { step: null, value: null }, - + _create: function() { this._draw(); this._markupOptions(); this._mousewheel(); this._aria(); }, - + _markupOptions: function() { - var _this = this; + var that = this; $.each({ min: -Number.MAX_VALUE, max: Number.MAX_VALUE, step: 1 - }, function(attr, defaultValue) { - if (_this.options[attr] === null) { - var value = _this.element.attr(attr); - _this.options[attr] = typeof value == "string" && value.length > 0 ? _this._parse(value) : defaultValue; + }, function( attr, defaultValue ) { + if ( that.options[ attr ] === null ) { + var value = that.element.attr( attr ); + that.options[ attr ] = typeof value === "string" && value.length > 0 ? + that._parse( value ) : + defaultValue; } }); - this.value(this.options.value !== null ? this.options.value : this.element.val() || 0); + this.value( this.options.value !== null ? this.options.value : this.element.val() || 0 ); }, - + _draw: function() { var self = this, options = self.options; var uiSpinner = this.uiSpinner = self.element - .addClass('ui-spinner-input') - .attr('autocomplete', 'off') - .wrap(self._uiSpinnerHtml()) + .addClass( "ui-spinner-input" ) + .attr( "autocomplete", "off" ) + .wrap( self._uiSpinnerHtml() ) .parent() // add buttons - .append(self._buttonHtml()) + .append( self._buttonHtml() ) // add behaviours + // TODO: user ._hoverable .hover(function() { - if (!options.disabled) { - $(this).addClass('ui-state-hover'); + if ( !options.disabled ) { + $( this ).addClass( "ui-state-hover" ); } self.hovered = true; }, function() { - $(this).removeClass('ui-state-hover'); + $( this ).removeClass( "ui-state-hover" ); self.hovered = false; }); + // TODO: use ._bind() this.element .attr( "role", "spinbutton" ) - .bind('keydown.spinner', function(event) { - if (self.options.disabled) { + .bind( "keydown.spinner", function( event ) { + if ( options.disabled ) { return; } - if (self._start(event)) { - return self._keydown(event); + if ( self._start( event ) ) { + return self._keydown( event ); } return true; }) - .bind('keyup.spinner', function(event) { - if (self.options.disabled) { + .bind( "keyup.spinner", function( event ) { + if ( options.disabled ) { return; } - if (self.spinning) { - self._stop(event); - self._change(event); + if ( self.spinning ) { + self._stop( event ); + self._change( event ); } }) - .bind('focus.spinner', function() { - uiSpinner.addClass('ui-state-active'); + .bind( "focus.spinner", function() { + uiSpinner.addClass( "ui-state-active" ); self.focused = true; }) - .bind('blur.spinner', function(event) { - self.value(self.element.val()); - if (!self.hovered) { - uiSpinner.removeClass('ui-state-active'); - } + .bind( "blur.spinner", function( event ) { + self.value( self.element.val() ); + if ( !self.hovered ) { + uiSpinner.removeClass( "ui-state-active" ); + } self.focused = false; }); // button bindings - this.buttons = uiSpinner.find('.ui-spinner-button') - .attr("tabIndex", -1) + this.buttons = uiSpinner.find( ".ui-spinner-button" ) + .attr( "tabIndex", -1 ) .button() - .removeClass("ui-corner-all") - .bind('mousedown', function(event) { - if (self.options.disabled) { + .removeClass( "ui-corner-all" ) + .bind( "mousedown", function( event ) { + if ( options.disabled ) { return; } - if (self._start(event) === false) { + if ( self._start( event ) === false ) { return false; } - self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + self._repeat( null, $( this ).hasClass( "ui-spinner-up" ) ? 1 : -1, event ); }) - .bind('mouseup', function(event) { - if (self.options.disabled) { + .bind( "mouseup", function( event ) { + if ( options.disabled ) { return; } - if (self.spinning) { - self._stop(event); - self._change(event); + if ( self.spinning ) { + self._stop( event ); + self._change( event ); } }) - .bind("mouseenter", function() { - if (self.options.disabled) { + .bind( "mouseenter", function() { + if ( self.options.disabled ) { return; } // button will add ui-state-active if mouse was down while mouseleave and kept down - if ($(this).hasClass("ui-state-active")) { - if (self._start(event) === false) { + if ( $( this ).hasClass( "ui-state-active" ) ) { + if ( self._start( event ) === false ) { return false; } - self._repeat(null, $(this).hasClass('ui-spinner-up') ? 1 : -1, event); + self._repeat( null, $( this ).hasClass( "ui-spinner-up" ) ? 1 : -1, event ); } }) - .bind("mouseleave", function() { - if (self.spinning) { - self._stop(event); - self._change(event); + .bind( "mouseleave", function() { + if ( self.spinning ) { + self._stop( event ); + self._change( event ); } }); - + // disable spinner if element was already disabled - if (options.disabled) { + if ( options.disabled ) { this.disable(); } }, - - _keydown: function(event) { - var o = this.options, - KEYS = $.ui.keyCode; - - switch (event.keyCode) { - case KEYS.UP: - this._repeat(null, 1, event); + + _keydown: function( event ) { + var options = this.options, + keyCode = $.ui.keyCode; + + switch ( event.keyCode ) { + case keyCode.UP: + this._repeat( null, 1, event ); return false; - case KEYS.DOWN: - this._repeat(null, -1, event); + case keyCode.DOWN: + this._repeat( null, -1, event ); return false; - case KEYS.PAGE_UP: - this._repeat(null, this.options.page, event); + case keyCode.PAGE_UP: + this._repeat( null, options.page, event ); return false; - case KEYS.PAGE_DOWN: - this._repeat(null, -this.options.page, event); + case keyCode.PAGE_DOWN: + this._repeat( null, -options.page, event ); return false; - - case KEYS.ENTER: - this.value(this.element.val()); + case keyCode.ENTER: + this.value( this.element.val() ); } - + return true; }, - + _mousewheel: function() { // need the delta normalization that mousewheel plugin provides - if (!$.fn.mousewheel) { + if ( !$.fn.mousewheel ) { return; } var self = this; - this.element.bind("mousewheel.spinner", function(event, delta) { - if (self.options.disabled || !delta) { + this.element.bind( "mousewheel.spinner", function( event, delta ) { + if ( self.options.disabled || !delta ) { return; } - if (!self.spinning && !self._start(event)) { + if ( !self.spinning && !self._start( event ) ) { return false; } - self._spin((delta > 0 ? 1 : -1) * self.options.step, event); - clearTimeout(self.timeout); + self._spin( (delta > 0 ? 1 : -1) * self.options.step, event ); + clearTimeout( self.timeout ); self.timeout = setTimeout(function() { - if (self.spinning) { - self._stop(event); - self._change(event); + if ( self.spinning ) { + self._stop( event ); + self._change( event ); } }, 100); event.preventDefault(); }); }, - + _uiSpinnerHtml: function() { - return '<span class="ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all"></span>'; + return "<span class='ui-spinner ui-state-default ui-widget ui-widget-content ui-corner-all'></span>"; }, - + _buttonHtml: function() { - return '<a class="ui-spinner-button ui-spinner-up ui-corner-tr"><span class="ui-icon ui-icon-triangle-1-n">▲</span></a>' + - '<a class="ui-spinner-button ui-spinner-down ui-corner-br"><span class="ui-icon ui-icon-triangle-1-s">▼</span></a>'; + return "" + + "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" + + "<span class='ui-icon ui-icon-triangle-1-n'>▲</span>" + + "</a>" + + "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" + + "<span class='ui-icon ui-icon-triangle-1-s'>▼</span>" + + "</a>"; }, - - _start: function(event) { - if (!this.spinning && this._trigger('start', event) !== false) { - if (!this.counter) { - this.counter = 1; - } - this.spinning = true; - return true; + + _start: function( event ) { + if ( !this.spinning && this._trigger( "start", event ) === false ) { + return false; + } + + if ( !this.counter ) { + this.counter = 1; } - return false; + this.spinning = true; + return true; }, - - _repeat: function(i, steps, event) { + + _repeat: function( i, steps, event ) { var self = this; i = i || 500; - clearTimeout(this.timer); + clearTimeout( this.timer ); this.timer = setTimeout(function() { - self._repeat(40, steps, event); - }, i); - - self._spin(steps * self.options.step, event); + self._repeat( 40, steps, event ); + }, i ); + + self._spin( steps * self.options.step, event ); }, - - _spin: function(step, event) { - if (!this.counter) { + + _spin: function( step, event ) { + if ( !this.counter ) { this.counter = 1; } - + // TODO refactor, maybe figure out some non-linear math var newVal = this.value() + step * (this.options.incremental && this.counter > 20 @@ -246,49 +255,51 @@ $.widget('ui.spinner', { : 10 : 2 : 1); - - if (this._trigger('spin', event, { value: newVal }) !== false) { - this.value(newVal); - this.counter++; + + if ( this._trigger( "spin", event, { value: newVal } ) !== false) { + this.value( newVal ); + this.counter++; } }, - - _stop: function(event) { + + _stop: function( event ) { this.counter = 0; - if (this.timer) { - window.clearTimeout(this.timer); + if ( this.timer ) { + clearTimeout( this.timer ); } this.element.focus(); this.spinning = false; - this._trigger('stop', event); + this._trigger( "stop", event ); }, - - _change: function(event) { - this._trigger('change', event); + + _change: function( event ) { + this._trigger( "change", event ); }, - - _setOption: function(key, value) { - if (key == 'value') { - value = this._parse(value); - if (value < this.options.min) { + + _setOption: function( key, value ) { + if ( key === "value") { + value = this._parse( value ); + if ( value < this.options.min ) { value = this.options.min; } - if (value > this.options.max) { + if ( value > this.options.max ) { value = this.options.max; } } - if (key == 'disabled') { - if (value) { - this.element.attr("disabled", true); - this.buttons.button("disable"); + + if ( key === "disabled" ) { + if ( value ) { + this.element.attr( "disabled", true ); + this.buttons.button( "disable" ); } else { - this.element.removeAttr("disabled"); - this.buttons.button("enable"); + this.element.removeAttr( "disabled" ); + this.buttons.button( "enable" ); } } + this._super( "_setOption", key, value ); }, - + _setOptions: function( options ) { this._super( "_setOptions", options ); if ( "value" in options ) { @@ -296,62 +307,62 @@ $.widget('ui.spinner', { } this._aria(); }, - + _aria: function() { - this.element - .attr('aria-valuemin', this.options.min) - .attr('aria-valuemax', this.options.max) - .attr('aria-valuenow', this.options.value); + this.element.attr({ + "aria-valuemin": this.options.min, + "aria-valuemax": this.options.max, + "aria-valuenow": this.options.value + }); }, - - _parse: function(val) { - var input = val; - if (typeof val == 'string') { - val = $.global && this.options.numberformat ? $.global.parseFloat(val) : +val; + + _parse: function( val ) { + if ( typeof val === "string" ) { + val = $.global && this.options.numberformat ? $.global.parseFloat( val ) : +val; } - return isNaN(val) ? null : val; + return isNaN( val ) ? null : val; }, - - _format: function(num) { - this.element.val( $.global && this.options.numberformat ? $.global.format(num, this.options.numberformat) : num ); + + _format: function( num ) { + this.element.val( $.global && this.options.numberformat ? $.global.format( num, this.options.numberformat ) : num ); }, - + destroy: function() { this.element - .removeClass('ui-spinner-input') - .removeAttr('disabled') - .removeAttr('autocomplete') - .removeAttr('role') - .removeAttr('aria-valuemin') - .removeAttr('aria-valuemax') - .removeAttr('aria-valuenow'); + .removeClass( "ui-spinner-input" ) + .removeAttr( "disabled" ) + .removeAttr( "autocomplete" ) + .removeAttr( "role" ) + .removeAttr( "aria-valuemin" ) + .removeAttr( "aria-valuemax" ) + .removeAttr( "aria-valuenow" ); this._super( "destroy" ); - this.uiSpinner.replaceWith(this.element); + this.uiSpinner.replaceWith( this.element ); }, - - stepUp: function(steps) { - this._spin((steps || 1) * this.options.step); + + stepUp: function( steps ) { + this._spin( (steps || 1) * this.options.step ); }, - - stepDown: function(steps) { - this._spin((steps || 1) * -this.options.step); + + stepDown: function( steps ) { + this._spin( (steps || 1) * -this.options.step ); }, - - pageUp: function(pages) { - this.stepUp((pages || 1) * this.options.page); + + pageUp: function( pages ) { + this.stepUp( (pages || 1) * this.options.page ); }, - - pageDown: function(pages) { - this.stepDown((pages || 1) * this.options.page); + + pageDown: function( pages ) { + this.stepDown( (pages || 1) * this.options.page ); }, - - value: function(newVal) { - if (!arguments.length) { - return this._parse(this.element.val()); + + value: function( newVal ) { + if ( !arguments.length ) { + return this._parse( this.element.val() ); } - this.option('value', newVal); + this.option( "value", newVal ); }, - + widget: function() { return this.uiSpinner; } @@ -359,4 +370,4 @@ $.widget('ui.spinner', { $.ui.spinner.version = "@VERSION"; -})(jQuery); +}( jQuery ) ); diff --git a/ui/jquery.ui.tabs.js b/ui/jquery.ui.tabs.js index 58c181670..239805b14 100644 --- a/ui/jquery.ui.tabs.js +++ b/ui/jquery.ui.tabs.js @@ -13,17 +13,11 @@ */ (function( $, undefined ) { -var tabId = 0, - listId = 0; - +var tabId = 0; function getNextTabId() { return ++tabId; } -function getNextListId() { - return ++listId; -} - $.widget( "ui.tabs", { options: { active: null, @@ -108,15 +102,7 @@ $.widget( "ui.tabs", { var panel = that._getPanelForTab( this.active ); panel.show(); - this.lis.eq( options.active ).addClass( "ui-tabs-active ui-state-active" ); - - // TODO: we need to remove this or add it to accordion - // seems to be expected behavior that the activate callback is fired - that.element.queue( "tabs", function() { - that._trigger( "activate", null, that._ui( that.active[ 0 ], panel[ 0 ] ) ); - }); - this.load( options.active ); } else { this.active = $(); @@ -158,15 +144,7 @@ $.widget( "ui.tabs", { _sanitizeSelector: function( hash ) { // we need this because an id may contain a ":" - return hash ? hash.replace( /:/g, "\\:" ) : ""; - }, - - _ui: function( tab, panel ) { - return { - tab: tab, - panel: panel, - index: this.anchors.index( tab ) - }; + return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@[\]^`{|}~]/g, "\\$&" ) : ""; }, refresh: function() { @@ -308,61 +286,13 @@ $.widget( "ui.tabs", { } }, - // Reset certain styles left over from animation - // and prevent IE's ClearType bug... + // TODO: remove once jQuery core properly removes filters - see #4621 _resetStyle: function ( $el, fx ) { - $el.css( "display", "" ); if ( !$.support.opacity && fx.opacity ) { $el[ 0 ].style.removeAttribute( "filter" ); } }, - _showTab: function( event, eventData ) { - var that = this; - - $( eventData.newTab ).closest( "li" ).addClass( "ui-tabs-active ui-state-active" ); - - if ( that.showFx ) { - that.running = true; - eventData.newPanel - // TODO: why are we hiding? old code? - .hide() - .animate( that.showFx, that.showFx.duration || "normal", function() { - that._resetStyle( $( this ), that.showFx ); - that.running = false; - that._trigger( "activate", event, eventData ); - }); - } else { - eventData.newPanel.show(); - that._trigger( "activate", event, eventData ); - } - }, - - // TODO: combine with _showTab() - _hideTab: function( event, eventData ) { - var that = this; - - if ( that.hideFx ) { - that.running = true; - eventData.oldPanel.animate( that.hideFx, that.hideFx.duration || "normal", function() { - that.running = false; - eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); - that._resetStyle( $( this ), that.hideFx ); - that.element.dequeue( "tabs" ); - if ( !eventData.newPanel.length ) { - that._trigger( "activate", event, eventData ); - } - }); - } else { - eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); - eventData.oldPanel.hide(); - that.element.dequeue( "tabs" ); - if ( !eventData.newPanel.length ) { - that._trigger( "activate", event, eventData ); - } - } - }, - _setupEvents: function( event ) { // attach tab event handler, unbind to avoid duplicates from former tabifying... this.anchors.unbind( ".tabs" ); @@ -417,52 +347,60 @@ $.widget( "ui.tabs", { that.xhr.abort(); } - // if tab may be closed - if ( options.collapsible ) { - if ( collapsing ) { - options.active = false; + if ( !toHide.length && !toShow.length ) { + throw "jQuery UI Tabs: Mismatching fragment identifier."; + } + + if ( toShow.length ) { - that.element.queue( "tabs", function() { - that._hideTab( event, eventData ); - }).dequeue( "tabs" ); + // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171 + that.load( that.anchors.index( clicked ), event ); - clicked[ 0 ].blur(); - return; - } else if ( !toHide.length ) { - that.element.queue( "tabs", function() { - that._showTab( event, eventData ); - }); + clicked[ 0 ].blur(); + } + that._toggle( event, eventData ); + }, + + // handles show/hide for selecting tabs + _toggle: function( event, eventData ) { + var that = this, + options = that.options, + toShow = eventData.newPanel, + toHide = eventData.oldPanel; - // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171 - that.load( that.anchors.index( clicked ), event ); + that.running = true; - clicked[ 0 ].blur(); - return; - } + function complete() { + that.running = false; + that._trigger( "activate", event, eventData ); } - // show new tab - if ( toShow.length ) { - if ( toHide.length ) { - that.element.queue( "tabs", function() { - that._hideTab( event, eventData ); - }); - } - that.element.queue( "tabs", function() { - that._showTab( event, eventData ); - }); + function show() { + eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" ); - that.load( that.anchors.index( clicked ), event ); - } else { - throw "jQuery UI Tabs: Mismatching fragment identifier."; + if ( toShow.length && that.showFx ) { + toShow + .animate( that.showFx, that.showFx.duration || "normal", function() { + that._resetStyle( $( this ), that.showFx ); + complete(); + }); + } else { + toShow.show(); + complete(); + } } - // Prevent IE from keeping other link focussed when using the back button - // and remove dotted border from clicked link. This is controlled via CSS - // in modern browsers; blur() removes focus from address bar in Firefox - // which can become a usability - if ( $.browser.msie ) { - clicked[ 0 ].blur(); + // start out by hiding, then showing, then completing + if ( toHide.length && that.hideFx ) { + toHide.animate( that.hideFx, that.hideFx.duration || "normal", function() { + eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); + that._resetStyle( $( this ), that.hideFx ); + show(); + }); + } else { + eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); + toHide.hide(); + show(); } }, @@ -585,24 +523,19 @@ $.widget( "ui.tabs", { load: function( index, event ) { index = this._getIndex( index ); var self = this, - o = this.options, - a = this.anchors.eq( index )[ 0 ], - panel = self._getPanelForTab( a ), + options = this.options, + anchor = this.anchors.eq( index ), + panel = self._getPanelForTab( anchor ), // TODO until #3808 is fixed strip fragment identifier from url // (IE fails to load from such url) - url = $( a ).attr( "href" ).replace( /#.*$/, "" ), + url = anchor.attr( "href" ).replace( /#.*$/, "" ), eventData = { - tab: $( a ), + tab: anchor, panel: panel }; - if ( this.xhr ) { - this.xhr.abort(); - } - // not remote if ( !url ) { - this.element.dequeue( "tabs" ); return; } @@ -615,35 +548,26 @@ $.widget( "ui.tabs", { }); if ( this.xhr ) { - // load remote from here on this.lis.eq( index ).addClass( "ui-tabs-loading" ); this.xhr .success(function( response ) { panel.html( response ); + self._trigger( "load", event, eventData ); }) .complete(function( jqXHR, status ) { if ( status === "abort" ) { - // stop possibly running animations - self.element.queue( [] ); self.panels.stop( false, true ); - - // "tabs" queue must not contain more than two elements, - // which are the callbacks for the latest clicked tab... - self.element.queue( "tabs", self.element.queue( "tabs" ).splice( -2, 2 ) ); - - delete this.xhr; } self.lis.eq( index ).removeClass( "ui-tabs-loading" ); - self._trigger( "load", event, eventData ); + if ( jqXHR === self.xhr ) { + delete self.xhr; + } }); } - // last, so that load event is fired before show... - self.element.dequeue( "tabs" ); - return this; }, @@ -660,6 +584,15 @@ $.extend( $.ui.tabs, { // DEPRECATED if ( $.uiBackCompat !== false ) { + // helper method for a lot of the back compat extensions + $.ui.tabs.prototype._ui = function( tab, panel ) { + return { + tab: tab, + panel: panel, + index: this.anchors.index( tab ) + }; + }; + // url method (function( $, prototype ) { prototype.url = function( index, url ) { @@ -685,7 +618,7 @@ if ( $.uiBackCompat !== false ) { var self = this; - this.element.bind( "tabsbeforeload", function( event, ui ) { + this.element.bind( "tabsbeforeload.tabs", function( event, ui ) { // tab is already cached if ( $.data( ui.tab[ 0 ], "cache.tabs" ) ) { event.preventDefault(); @@ -705,7 +638,7 @@ if ( $.uiBackCompat !== false ) { } }); - ui.jqXHR.success( function() { + ui.jqXHR.success(function() { if ( self.options.cache ) { $.data( ui.tab[ 0 ], "cache.tabs", true ); } @@ -743,34 +676,28 @@ if ( $.uiBackCompat !== false ) { }( jQuery, jQuery.ui.tabs.prototype ) ); // spinner - (function( $, prototype ) { - $.extend( prototype.options, { + $.widget( "ui.tabs", $.ui.tabs, { + options: { spinner: "<em>Loading…</em>" - }); - - var _create = prototype._create; - prototype._create = function() { - _create.call( this ); - var self = this; - - this.element.bind( "tabsbeforeload", function( event, ui ) { - if ( self.options.spinner ) { - var span = $( "span", ui.tab ); - if ( span.length ) { - span.data( "label.tabs", span.html() ).html( self.options.spinner ); + }, + _create: function() { + this._super( "_create" ); + this._bind({ + tabsbeforeload: function( event, ui ) { + if ( !this.options.spinner ) { + return; } + + var span = ui.tab.find( "span" ), + html = span.html(); + span.html( this.options.spinner ); + ui.jqXHR.complete(function() { + span.html( html ); + }); } - ui.jqXHR.complete( function() { - if ( self.options.spinner ) { - var span = $( "span", ui.tab ); - if ( span.length ) { - span.html( span.data( "label.tabs" ) ).removeData( "label.tabs" ); - } - } - }); }); - }; - }( jQuery, jQuery.ui.tabs.prototype ) ); + } + }); // enable/disable events (function( $, prototype ) { @@ -839,20 +766,30 @@ if ( $.uiBackCompat !== false ) { li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true ); li.find( "a" ).attr( "aria-controls", id ); + var doInsertAfter = index >= this.lis.length; + // try to find an existing element before creating a new one var panel = this.element.find( "#" + id ); if ( !panel.length ) { panel = this._createPanel( id ); + if ( doInsertAfter ) { + if ( index > 0 ) { + panel.insertAfter( this.panels.eq( -1 ) ); + } else { + panel.appendTo( this.element ); + } + } else { + panel.insertBefore( this.panels[ index ] ); + } } panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ).hide(); - if ( index >= this.lis.length ) { + if ( doInsertAfter ) { li.appendTo( this.list ); - panel.appendTo( this.list[ 0 ].parentNode ); } else { li.insertBefore( this.lis[ index ] ); - panel.insertBefore( this.panels[ index ] ); } + options.disabled = $.map( options.disabled, function( n ) { return n >= index ? ++n : n; }); @@ -975,21 +912,33 @@ if ( $.uiBackCompat !== false ) { show: null, select: null }); - var _trigger = prototype._trigger; + var _create = prototype._create, + _trigger = prototype._trigger; + prototype._create = function() { + _create.call( this ); + if ( this.options.active !== false ) { + this._trigger( "show", null, this._ui( + this.active[ 0 ], this._getPanelForTab( this.active )[ 0 ] ) ); + } + }; prototype._trigger = function( type, event, data ) { var ret = _trigger.apply( this, arguments ); if ( !ret ) { return false; } - if ( type === "beforeActivate" ) { + if ( type === "beforeActivate" && data.newTab.length ) { ret = _trigger.call( this, "select", event, { tab: data.newTab[ 0], panel: data.newPanel[ 0 ], index: data.newTab.closest( "li" ).index() }); - } else if ( type === "activate" ) { - ret = _trigger.call( this, "show", event, data ); + } else if ( type === "activate" && data.newTab.length ) { + ret = _trigger.call( this, "show", event, { + tab: data.newTab[ 0 ], + panel: data.newPanel[ 0 ], + index: data.newTab.closest( "li" ).index() + }); } }; }( jQuery, jQuery.ui.tabs.prototype ) ); @@ -1010,57 +959,66 @@ if ( $.uiBackCompat !== false ) { }( jQuery, jQuery.ui.tabs.prototype ) ); // cookie option - (function( $, prototype ) { - $.extend( prototype.options, { + var listId = 0; + function getNextListId() { + return ++listId; + } + $.widget( "ui.tabs", $.ui.tabs, { + options: { cookie: null // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true } - }); - - var _create = prototype._create, - _refresh = prototype._refresh, - _eventHandler = prototype._eventHandler, - _destroy = prototype._destroy; - - prototype._create = function() { - var o = this.options; - if ( o.active === undefined ) { - if ( typeof o.active !== "number" && o.cookie ) { - o.active = parseInt( this._cookie(), 10 ); + }, + _create: function() { + var options = this.options, + active; + if ( options.active == null && options.cookie ) { + active = parseInt( this._cookie(), 10 ); + if ( active === -1 ) { + active = false; } + options.active = active; } - _create.call( this ); - }; - - prototype._cookie = function() { - var cookie = this.cookie || - ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() ); - return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) ); - }; - - prototype._refresh = function() { - _refresh.call( this ); - - // set or update cookie after init and add/remove respectively + this._super( "_create" ); + }, + _cookie: function( active ) { + var cookie = [ this.cookie || + ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() ) ]; + if ( arguments.length ) { + cookie.push( active === false ? -1 : active ); + cookie.push( this.options.cookie ); + } + return $.cookie.apply( null, cookie ); + }, + _refresh: function() { + this._super( "_refresh" ); if ( this.options.cookie ) { this._cookie( this.options.active, this.options.cookie ); } - }; - - prototype._eventHandler = function( event ) { - _eventHandler.apply( this, arguments ); - + }, + _eventHandler: function( event ) { + this._superApply( "_eventHandler", arguments ); if ( this.options.cookie ) { this._cookie( this.options.active, this.options.cookie ); } - }; - - prototype._destroy = function() { - _destroy.call( this ); - + }, + _destroy: function() { + this._super( "_destroy" ); if ( this.options.cookie ) { this._cookie( null, this.options.cookie ); } - }; - }( jQuery, jQuery.ui.tabs.prototype ) ); + } + }); + + // load event + $.widget( "ui.tabs", $.ui.tabs, { + _trigger: function( type, event, data ) { + var _data = $.extend( {}, data ); + if ( type === "load" ) { + _data.panel = _data.panel[ 0 ]; + _data.tab = _data.tab[ 0 ]; + } + return this._super( "_trigger", type, event, _data ); + } + }); } })( jQuery ); diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index a74e6b77b..4167fd4e5 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -55,7 +55,7 @@ $.widget = function( name, base, prototype ) { // we need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from - basePrototype.options = $.extend( true, {}, basePrototype.options ); + basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { if ( $.isFunction( value ) ) { prototype[ prop ] = (function() { @@ -83,7 +83,7 @@ $.widget = function( name, base, prototype ) { }()); } }); - $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + $[ namespace ][ name ].prototype = $.widget.extend( basePrototype, { namespace: namespace, widgetName: name, widgetEventPrefix: name, @@ -93,6 +93,23 @@ $.widget = function( name, base, prototype ) { $.widget.bridge( name, $[ namespace ][ name ] ); }; +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if (input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + target[ key ] = $.isPlainObject( value ) ? $.widget.extend( {}, target[ key ], value ) : value; + } + } + } + return target; +}; + $.widget.bridge = function( name, object ) { $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", @@ -101,7 +118,7 @@ $.widget.bridge = function( name, object ) { // allow multiple hashes to be passed on init options = !isMethodCall && args.length ? - $.extend.apply( null, [ true, options ].concat(args) ) : + $.widget.extend.apply( null, [ options ].concat(args) ) : options; if ( isMethodCall ) { @@ -163,7 +180,7 @@ $.Widget.prototype = { _createWidget: function( options, element ) { element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); - this.options = $.extend( true, {}, + this.options = $.widget.extend( {}, this.options, this._getCreateOptions(), options ); @@ -218,7 +235,7 @@ $.Widget.prototype = { if ( arguments.length === 0 ) { // don't return a reference to the internal hash - return $.extend( {}, this.options ); + return $.widget.extend( {}, this.options ); } if ( typeof key === "string" ) { @@ -230,7 +247,7 @@ $.Widget.prototype = { parts = key.split( "." ); key = parts.shift(); if ( parts.length ) { - curOption = options[ key ] = $.extend( true, {}, this.options[ key ] ); + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); for ( i = 0; i < parts.length - 1; i++ ) { curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; curOption = curOption[ parts[ i ] ]; |