diff options
-rw-r--r-- | demos/menubar/default.html | 67 | ||||
-rw-r--r-- | demos/widget/default.html | 35 | ||||
-rw-r--r-- | tests/unit/effects/effects.html | 11 | ||||
-rw-r--r-- | tests/unit/effects/effects_core.js | 15 | ||||
-rw-r--r-- | tests/unit/position/position.html | 61 | ||||
-rw-r--r-- | tests/unit/position/position_core.js | 5 | ||||
-rw-r--r-- | tests/unit/position/position_core_within.js | 441 | ||||
-rw-r--r-- | tests/unit/widget/widget_core.js | 19 | ||||
-rw-r--r-- | tests/visual/position/position.html | 2 | ||||
-rw-r--r-- | tests/visual/position/position_within.html | 189 | ||||
-rw-r--r-- | ui/jquery.effects.blind.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.bounce.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.core.js | 8 | ||||
-rw-r--r-- | ui/jquery.effects.drop.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.fold.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.scale.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.shake.js | 2 | ||||
-rw-r--r-- | ui/jquery.effects.slide.js | 16 | ||||
-rw-r--r-- | ui/jquery.ui.menubar.js | 17 | ||||
-rw-r--r-- | ui/jquery.ui.position.js | 114 | ||||
-rw-r--r-- | ui/jquery.ui.widget.js | 14 |
21 files changed, 862 insertions, 164 deletions
diff --git a/demos/menubar/default.html b/demos/menubar/default.html index 79662bd9f..8f15be516 100644 --- a/demos/menubar/default.html +++ b/demos/menubar/default.html @@ -13,9 +13,10 @@ <script src="../../ui/jquery.ui.menubar.js"></script> <script> $(function() { - $("td:has(.menubar)").clone().appendTo("tbody tr:not(:first)"); - - $("#bar1, .menubar").menubar({ + $("#bar1").menubar({ + position: { + within: $("#demo-frame").add(window).first() + }, select: function(event, ui) { $("<div/>").text("Selected: " + ui.item.text()).appendTo("#log"); } @@ -24,6 +25,9 @@ $(".menubar-icons").menubar({ menuIcon: true, buttons: true, + position: { + within: $("#demo-frame").add(window).first() + }, select: function(event, ui) { $("<div/>").text("Selected: " + ui.item.text()).appendTo("#log"); } @@ -160,63 +164,6 @@ </li> </ul> -<!-- -<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"> - <ul class="menubar"> - <li> - <a href="#Options">Options</a> - <ul> - <li><a href="#Order...">Order...</a></li> - <li class="ui-state-disabled">Write a Review...</li> - <li><a href="#Find Similar Movies...">Find Similar Movies...</a></li> - <li> - <a href="#Rate">Rate</a> - <ul> - <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> - </li> - </ul> - </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: 100px; width: 300px; overflow: auto;" class="ui-widget-content"></div> diff --git a/demos/widget/default.html b/demos/widget/default.html index 410318e0a..c2b520983 100644 --- a/demos/widget/default.html +++ b/demos/widget/default.html @@ -10,7 +10,7 @@ <script src="../../ui/jquery.ui.position.js"></script> <link rel="stylesheet" href="../demos.css"> <style> - .colorize { + .custom-colorize { font-size: 25px; width: 75px; height: 75px; @@ -28,20 +28,21 @@ blue: 0, // callbacks + change: null, random: null }, // the constructor _create: function() { this.element - // add a class for themeing - .addClass("colorize") + // add a class for theming + .addClass( "custom-colorize" ) // prevent double click to select text .disableSelection(); // bind click events to random method this._bind({ - // _bind won"t call random when widget is disabled + // _bind won't call random when widget is disabled click: "random" }); this._refresh(); @@ -68,7 +69,7 @@ blue: Math.floor( Math.random() * 256 ) }; - // trigger an event, check if it"s canceled + // trigger an event, check if it's canceled if ( this._trigger( "random", event, colors ) !== false ) { this.option( colors ); } @@ -78,24 +79,26 @@ // revert other modifications here _destroy: function() { this.element - .removeClass( "colorize" ) + .removeClass( "custom-colorize" ) .enableSelection() .css( "background-color", "transparent" ); }, - _setOption: function( key, value ) { - // prevent invalid color values - if ( /red|green|blue/.test(key) && value < 0 || value > 255 ) { - return; - } - this._super( "_setOptions", options ); - }, - + // _setOptions is called with a hash of all options that are changing // always refresh when changing options _setOptions: function() { - // _super handles keeping the right this-context + // _super and _superApply handle keeping the right this-context this._superApply( "_setOptions", arguments ); this._refresh(); + }, + + // _setOption is called for each individual option that is changing + _setOption: function( key, value ) { + // prevent invalid color values + if ( /red|green|blue/.test(key) && (value < 0 || value > 255) ) { + return; + } + this._super( "_setOption", key, value ); } }); @@ -131,7 +134,7 @@ red: 0, green: 0, blue: 0 - } ); + }); }); }); </script> diff --git a/tests/unit/effects/effects.html b/tests/unit/effects/effects.html index 84fecd9cc..6956ebcda 100644 --- a/tests/unit/effects/effects.html +++ b/tests/unit/effects/effects.html @@ -54,6 +54,14 @@ .testChildren h2 { font-size: 20px; } + + .relWidth { + width: 50%; + } + + .relHeight { + height: 50%; + } </style> </head> <body> @@ -70,6 +78,9 @@ <div class="animateClass test"> <h2>Child Element Test</h2> </div> + <div class="relWidth relHeight testAddBorder"> + <h2>Slide with relative width</d2> + </div> </div> </body> diff --git a/tests/unit/effects/effects_core.js b/tests/unit/effects/effects_core.js index ed9fbf9ba..4c685ebb6 100644 --- a/tests/unit/effects/effects_core.js +++ b/tests/unit/effects/effects_core.js @@ -54,6 +54,21 @@ $.each( $.effects.effect, function( effect ) { start(); })); }); + + asyncTest( "relative width & height - properties are preserved", function() { + var test = $("div.relWidth.relHeight"), + width = test.width(), height = test.height(), + cssWidth = test[0].style.width, cssHeight = test[0].style.height; + + expect( 4 ); + test.toggle( effect, minDuration, function() { + equal( test[0].style.width, cssWidth, "Inline CSS Width has been reset after animation ended" ); + equal( test[0].style.height, cssHeight, "Inline CSS Height has been rest after animation ended" ); + start(); + }); + equal( test.width(), width, "Width is the same px after animation started" ); + equal( test.height(), height, "Height is the same px after animation started" ); + }); }); module("animateClass"); diff --git a/tests/unit/position/position.html b/tests/unit/position/position.html index 4f2e87e08..d71bae497 100644 --- a/tests/unit/position/position.html +++ b/tests/unit/position/position.html @@ -16,47 +16,54 @@ <script src="../testsuite.js"></script> <script src="position_core.js"></script> + <script src="position_core_within.js"></script> <script src="../swarminject.js"></script> </head> <body> - -<h1 id="qunit-header">jQuery UI Position 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 style="position:relative; z-index:2;"> + <h1 id="qunit-header">jQuery UI Position 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> <!-- elements smaller than 10px have a line-height set on them to avoid a bug in IE6 .height() returns the greater of the height and line-height --> -<div id="qunit-fixture" style="top: 0; left: 0;"> - <div id="el1" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> - <div id="el2" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> - <div id="parent" style="position: absolute; width: 6px; height: 6px; top: 4px; left: 4px; line-height: 6px;"></div> -</div> - -<div style="position: absolute; top: 0px; left: 0px"> - <div id="elx" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> - <div id="parentx" style="position: absolute; width: 20px; height: 20px; top: 40px; left: 40px;"></div> +<div id="qunit-fixture" style="top: 0; left: 0; z-index:1"> + <div id="within-container"> + <div id="el1" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> + <div id="el2" style="position: absolute; width: 6px; height: 6px; line-height: 6px;"></div> + <div id="parent" style="position: absolute; width: 6px; height: 6px; top: 4px; left: 4px; line-height: 6px;"></div> + + <div style="position: absolute; top: 0px; left: 0px"> + <div id="elx" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> + <div id="parentx" style="position: absolute; width: 20px; height: 20px; top: 40px; left: 40px;"></div> + </div> + + <div style="position: absolute; top: 200px; left: 100px;"> + <div id="el-offset-100-200" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> + <div style="position: absolute; top: 100px; left: 50px;"> + <div id="el-two-offset-150-300" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> + <div id="el-fixed" style="position: fixed; top: 200px; left: 200px;"></div> + </div> + </div> + + <div style="position: absolute; height: 5000px; width: 5000px;"></div> + + <div id="bug-5280" style="height: 30px; width: 201px;"> + <div style="width: 50px; height: 10px;"></div> + </div> + </div> </div> -<div style="position: absolute; top: 200px; left: 100px;"> - <div id="el-offset-100-200" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> - <div style="position: absolute; top: 100px; left: 50px;"> - <div id="el-two-offset-150-300" style="position: absolute; width: 10px; height: 10px; line-height: 10px;"></div> - <div id="el-fixed" style="position: fixed; top: 200px; left: 200px;"></div> - </div> -</div> -<div style="position: absolute; height: 5000px; width: 5000px;"></div> -<div id="bug-5280" style="height: 30px; width: 201px;"> - <div style="width: 50px; height: 10px;"></div> -</div> </body> </html> diff --git a/tests/unit/position/position_core.js b/tests/unit/position/position_core.js index dbbda8a3b..bd8e58612 100644 --- a/tests/unit/position/position_core.js +++ b/tests/unit/position/position_core.js @@ -64,7 +64,6 @@ test( "positions", function() { center: 3, right: 6, top: 0, - center: 3, bottom: 6 }; var start = { left: 4, top: 4 }; @@ -316,6 +315,7 @@ test( "collision: fit, window scrolled", function() { if ( scrollTopSupport() ) { var win = $( window ); win.scrollTop( 300 ).scrollLeft( 200 ); + collisionTest({ collision: "fit", at: "left-100 top-100" @@ -324,6 +324,7 @@ test( "collision: fit, window scrolled", function() { collision: "fit", at: "right+100 bottom+100" }, { top: 300 + win.height() - 10, left: 200 + win.width() - 10 }, "right bottom" ); + win.scrollTop( 0 ).scrollLeft( 0 ); } }); @@ -351,7 +352,7 @@ test( "collision: flip, with offset", function() { collisionTest2({ collision: "flip", - at: "left-2 top-3", + at: "left-2 top-3" }, { top: $( window ).height() + 3, left: $( window ).width() + 2 }, "right bottom, negative offset" ); }); diff --git a/tests/unit/position/position_core_within.js b/tests/unit/position/position_core_within.js new file mode 100644 index 000000000..567c17192 --- /dev/null +++ b/tests/unit/position/position_core_within.js @@ -0,0 +1,441 @@ +(function( $ ) { + +function scrollTopSupport() { + $( window ).scrollTop( 1 ); + return $( window ).scrollTop() === 1; +} + +module( "position - within", { + setup: function(){ + $("#within-container").css({"width": "500px", "height": "500px", "top": "20px", "left": "20px", "position": "relative"}).show(); + } +}); + +var addTop = -20, + addLeft = -20; + +$.fn.addOffsets = function() { + var elOffset = this.offset(), + offset = $("#within-container").offset(); + + elOffset.top -= offset.top; + elOffset.left -= offset.left; + + return {top: elOffset.top - offset.top, left: elOffset.left - offset.left }; +}; + +test( "my, at, of", function() { + var within = $("#within-container"); + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 40, left: addLeft + 40 }, "left top, left top" ); + + $( "#elx" ).position({ + my: "left top", + at: "left bottom", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 60, left: addLeft + 40 }, "left top, left bottom" ); + + $( "#elx" ).position({ + my: "left", + at: "bottom", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 55, left: addLeft + 50 }, "left, bottom" ); + + $( "#elx" ).position({ + my: "left foo", + at: "bar baz", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 45, left: addLeft +50 }, "left foo, bar baz" ); +}); + +test( "multiple elements", function() { + var elements = $( "#el1, #el2" ); + var result = elements.position({ + my: "left top", + at: "left bottom", + of: "#parent", + collision: "none", + within: $("#within-container") + }); + + same( result, elements ); + var expected = { top: addTop + 10, left: addLeft + 4 }; + elements.each(function() { + same( $( this ).addOffsets(), expected ); + }); +}); + +test( "positions", function() { + var definitions = []; + var offsets = { + left: 0, + center: 3, + right: 6, + top: 0, + bottom: 6 + }; + var start = { left: 4, top: 4 }; + $.each( [ 0, 1 ], function( my ) { + $.each( [ "top", "center", "bottom" ], function( vindex, vertical ) { + $.each( [ "left", "center", "right" ], function( hindex, horizontal ) { + definitions.push({ + my: my ? horizontal + " " + vertical : "left top", + at: !my ? horizontal + " " + vertical : "left top", + result: { + top: addTop + (my ? start.top - offsets[ vertical ] : start.top + offsets[ vertical ]), + left: addLeft + (my ? start.left - offsets[ horizontal ] : start.left + offsets[ horizontal ]) + } + }); + }); + }); + }); + var el = $( "#el1" ); + $.each( definitions, function( index, definition ) { + el.position({ + my: definition.my, + at: definition.at, + of: "#parent", + collision: "none", + within: $("#within-container") + }); + same( el.addOffsets(), definition.result, + "Position via " + QUnit.jsDump.parse({ my:definition.my, at:definition.at }) ); + }); +}); + +test( "of", function() { + var within = $("#within-container"); + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 40, left: addLeft + 40 }, "selector" ); + + $( "#elx" ).position({ + my: "left top", + at: "left bottom", + of: $( "#parentx"), + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 60, left: addLeft + 40 }, "jQuery object" ); + + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: $( "#parentx" )[ 0 ], + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 40, left: addLeft + 40 }, "DOM element" ); + + var event = $.extend( $.Event( "someEvent" ), { pageX: 200, pageY: 300 } ); + $( "#elx" ).position({ + my: "left top", + at: "left top", + of: event, + collision: "none", + within: within + }); + same( $( "#elx" ).offset(), { + top: 300, + left: 200 + }, "event - left top, left top" ); + + event = $.extend( $.Event( "someEvent" ), { pageX: 400, pageY: 600 } ); + $( "#elx" ).position({ + my: "left top", + at: "right bottom", + of: event, + collision: "none", + within: within + }); + same( $( "#elx" ).offset(), { + top: 600, + left: 400 + }, "event - left top, right bottom" ); +}); + +test( "within:offsets", function() { + var within = $("#within-container"); + + $( "#elx" ).position({ + my: "left top", + at: "left+10 bottom+10", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 70, left: addLeft + 50 }, "offsets in at" ); + + $( "#elx" ).position({ + my: "left+10 top-10", + at: "left bottom", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 50, left: addLeft + 50 }, "offsets in my" ); + + $( "#elx" ).position({ + my: "left top", + at: "left+50% bottom-10%", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 58, left: addLeft + 50 }, "percentage offsets in at" ); + + $( "#elx" ).position({ + my: "left-30% top+50%", + at: "left bottom", + of: "#parentx", + collision: "none", + within: within + }); + same( $( "#elx" ).addOffsets(), { top: addTop + 65, left: addLeft + 37 }, "percentage offsets in my" ); +}); + +test( "using", function() { + expect( 6 ); + + var within = $("#within-container"); + + var count = 0, + elems = $( "#el1, #el2" ), + expectedPosition = { top: addTop + 40, left: addLeft + 40 }, + originalPosition = elems.position({ + my: "right bottom", + at: "rigt bottom", + of: "#parentx", + collision: "none", + within: within + }).addOffsets(); + + elems.position({ + my: "left top", + at: "left top", + of: "#parentx", + using: function( position ) { + position.top -= within.offset().top; + position.left -= within.offset().left; + same( this, elems[ count ], "correct context for call #" + count ); + same( position, expectedPosition, "correct position for call #" + count ); + count++; + }, + within: within + }); + + elems.each(function() { + same( $( this ).addOffsets(), originalPosition, "elements not moved" ); + }); +}); + +function collisionTest( config, result, msg ) { + var within = $("#within-container"); + + var elem = $( "#elx" ).position( $.extend({ + my: "left top", + at: "right bottom", + of: within[0], + within: within + }, config ) ); + + same( elem.addOffsets(), result, msg ); +} + +function collisionTest2( config, result, msg ) { + collisionTest( $.extend({ + my: "right bottom", + at: "left top" + }, config ), result, msg ); +} + +test( "collision: fit, no offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "fit" + }, { top: addTop + within.height() - 10 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 10 - $.position.getScrollInfo( within ).width }, "right bottom" ); + + collisionTest2({ + collision: "fit" + }, { top: addTop + 0, left: addLeft + 0 }, "left top" ); +}); + + +test( "collision: fit, with offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "fit", + at: "right+2 bottom+3" + }, { top: addTop + within.height() - 10 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 10 - $.position.getScrollInfo( within ).width }, "right bottom"); + + collisionTest2({ + collision: "fit", + at: "left+2 top+3" + }, { top: addTop + 0, left: addLeft + 0 }, "left top, positive offset" ); + + collisionTest2({ + collision: "fit", + at: "left-2 top-3" + }, { top: addTop + 0, left: addLeft + 0 }, "left top, negative offset" ); +}); + +test( "collision: fit, within scrolled", function() { + if ( scrollTopSupport() ) { + var within = $("#within-container").css({"width": "1000px", "height": "800px", "overflow": "auto"}); + within.scrollTop( 300 ).scrollLeft( 150 ); + + collisionTest({ + collision: "fit", + at: "left-100 top-100" + }, { top: addTop, left: addLeft }, "top left" ); + collisionTest2({ + collision: "fit", + at: "right+100 bottom+100" + }, { top: addTop + within.height() - 10 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 10 - $.position.getScrollInfo( within ).width }, "right bottom" ); + within.scrollTop( 0 ).scrollLeft( 0 ); + } +}); + +test( "collision: flip, no offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "flip" + }, { top: addTop + -10, left: addLeft + -10 }, "left top" ); + + collisionTest2({ + collision: "flip" + }, { top: addTop + within.height(), left: addLeft + within.width() }, "right bottom" ); +}); + +test( "collision: flip, with offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "flip", + at: "right+2 bottom+3" + }, { top: addTop + -13, left: addLeft + -12 }, "left top, with offset added" ); + + collisionTest2({ + collision: "flip", + at: "left+2 top+3" + }, { top: addTop + within.height() - 3, left: addLeft + within.width() - 2 }, "bottom, positive offset" ); + + collisionTest2({ + collision: "flip", + at: "left-2 top-3" + }, { top: addTop + within.height() + 3, left: addLeft + within.width() + 2 }, "right bottom, negative offset" ); +}); + +test( "collision: none, no offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "none" + }, { top: addTop + within.height(), left: addLeft + within.width() }, "left top" ); + + collisionTest2({ + collision: "none" + }, { top: addTop + -10, left: addLeft + -10 }, "moved to the right bottom" ); +}); + +test( "collision: none, with offset", function() { + var within = $("#within-container"); + + collisionTest({ + collision: "none", + at: "right+2 bottom+3" + }, { top: addTop + within.height() + 3, left: addLeft + within.width() + 2 }, "right bottom, with offset added" ); + + collisionTest2({ + collision: "none", + at: "left+2 top+3" + }, { top: addTop + -7, left: addLeft + -8 }, "left top, positive offset" ); + + collisionTest2({ + collision: "none", + at: "left-2 top-3" + }, { top: addTop + -13, left: addLeft + -12 }, "left top, negative offset" ); +}); + +test( "collision: fit, with margin", function() { + var within = $("#within-container"); + + $( "#elx" ).css( "margin", 10 ); + + collisionTest({ + collision: "fit" + }, { top: addTop + within.height() - 20 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 20 - $.position.getScrollInfo( within ).width }, "right bottom" ); + + collisionTest2({ + collision: "fit" + }, { top: addTop + 10, left: addLeft + 10 }, "left top" ); + + $( "#elx" ).css({ + "margin-left": 5, + "margin-top": 5 + }); + + collisionTest({ + collision: "fit" + }, { top: addTop + within.height() - 20 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 20 - $.position.getScrollInfo( within ).width }, "right bottom" ); + + collisionTest2({ + collision: "fit" + }, { top: addTop + 5, left: addLeft + 5 }, "left top" ); + + $( "#elx" ).css({ + "margin-right": 15, + "margin-bottom": 15 + }); + + collisionTest({ + collision: "fit" + }, { top: addTop + within.height() - 25 - $.position.getScrollInfo( within ).height, left: addLeft + within.width() - 25 - $.position.getScrollInfo( within ).width }, "right bottom" ); + + collisionTest2({ + collision: "fit" + }, { top: addTop + 5, left: addLeft + 5 }, "left top" ); +}); + +test( "collision: flip, with margin", function() { + var within = $("#within-container"); + + $( "#elx" ).css( "margin", 10 ); + + collisionTest({ + collision: "flip", + at: "left top" + }, { top: addTop + within.height() - 10, left: addLeft + within.width() - 10 }, "left top" ); + + collisionTest2({ + collision: "flip", + at: "right bottom" + }, { top: addTop + 0, left: addLeft + 0 }, "right bottom" ); +}); + +}( jQuery ) ); diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index b1c27b104..40fd97ba5 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -413,6 +413,7 @@ test( ".option() - getter", function() { qux: [ "quux", "quuux" ] }); + same( div.testWidget( "option", "x" ), null, "non-existent option" ); same( div.testWidget( "option", "foo"), "bar", "single option - string" ); same( div.testWidget( "option", "baz"), 5, "single option - number" ); same( div.testWidget( "option", "qux"), [ "quux", "quuux" ], @@ -431,6 +432,24 @@ test( ".option() - getter", function() { "modifying returned options hash does not modify plugin instance" ); }); +test( ".option() - deep option getter", function() { + $.widget( "ui.testWidget", {} ); + var div = $( "<div>" ).testWidget({ + foo: { + bar: "baz", + qux: { + quux: "xyzzy" + } + } + }); + equal( div.testWidget( "option", "foo.bar" ), "baz", "one level deep - string" ); + deepEqual( div.testWidget( "option", "foo.qux" ), { quux: "xyzzy" }, + "one level deep - object" ); + equal( div.testWidget( "option", "foo.qux.quux" ), "xyzzy", "two levels deep - string" ); + equal( div.testWidget( "option", "x.y" ), null, "top level non-existent" ); + equal( div.testWidget( "option", "foo.x.y" ), null, "one level deep - non-existent" ); +}); + test( ".option() - delegate to ._setOptions()", function() { var calls = []; $.widget( "ui.testWidget", { diff --git a/tests/visual/position/position.html b/tests/visual/position/position.html index da0ff5170..11932da0e 100644 --- a/tests/visual/position/position.html +++ b/tests/visual/position/position.html @@ -35,7 +35,7 @@ <style> input, .ui-menu { position: absolute; } .ui-menu { width: 200px; } - html, body { width: 99%; height: 99% } + html, body { width: 99%; height: 99%; min-height:700px; min-width:700px; } #container { width: 95%; height: 95%; border: 1px solid black; margin: auto; } </style> </head> diff --git a/tests/visual/position/position_within.html b/tests/visual/position/position_within.html new file mode 100644 index 000000000..f120a4b47 --- /dev/null +++ b/tests/visual/position/position_within.html @@ -0,0 +1,189 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>Position Visual Test: Containing Element</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.mouse.js"></script> + <script type="text/javascript" src="../../../ui/jquery.ui.draggable.js"></script> + <script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script> + + <style> + html, body { + height:100%; + width:100%; + margin:0; + /* force scroll bar*/ + min-height:800px; + min-width:800px; + + /* IE6 needs this */ + text-align:center; + } + .demo-description { + text-align:center; + padding:1.5em; + } + .demo-container { + background:#aaa; + width:80%; + height:80%; + + text-align:left; + margin:0 auto; + position:relative; + padding:10px; + } + .demo { + background:#eee; + overflow:hidden; + position:relative; + height:100%; + /* IE6 needs this */ + width:100%; + } + #parent { + width: 60%; + margin: 10px auto; + padding: 5px; + border: 1px solid #777; + background-color: #fbca93; + text-align: center; + cursor:move; + } + .positionable { + width: 75px; + height: 75px; + position: absolute; + display: block; + right: 0; + bottom: 0; + background-color: #bcd5e6; + text-align: center; + border:solid 2px #555; + cursor:move; + } + select, input { + margin-left: 15px; + } + </style> + <script> + $(function() { + function position( using ) { + $( ".positionable" ).position({ + of: $( "#parent" ), + my: $( "#my_horizontal" ).val() + " " + $( "#my_vertical" ).val(), + at: $( "#at_horizontal" ).val() + " " + $( "#at_vertical" ).val(), + offset: $( "#offset" ).val(), + using: using, + within: $( ".demo" ), + collision: $( "#collision_horizontal" ).val() + " " + $( "#collision_vertical" ).val() + }); + } + $( ".demo" ).append("<div style='width:5000px;height:5000px;' />").css("overflow","auto"); + + $( ".positionable" ).css( "opacity", 0.5 ); + + $( ":input" ).bind( "click keyup change", function() { position(); } ); + + $( "#parent" ).draggable({ + drag: function() { position(); } + }); + + $( ".positionable" ).draggable({ + drag: function( event, ui ) { + // reset offset before calculating it + $( "#offset" ).val( "0" ); + position(function( result ) { + var demo = $( ".demo" ); + $( "#offset" ).val( "" + ( ui.offset.left - result.left - demo.offset().left + demo.scrollLeft() ) + + " " + ( ui.offset.top - result.top - demo.offset().top + demo.scrollTop() ) ); + position(); + }); + } + }); + + position(); + }); + </script> +</head> +<body> + +<div class="demo-description"> + Use the form controls to configure the positioning, or drag the positioned element to modify its offset. + <br/>Drag around the parent element to see collision detection in action. +</div><!-- End demo-description --> + +<div class="demo-container"> +<div class="demo"> + + <div id="parent"> + <p>This is the position parent element.</p> + </div> + + <div class="positionable"> + <p>to position</p> + </div> + + <div class="positionable" style="width:120px; height: 40px;"> + <p>to position 2</p> + </div> + + <div style="padding: 20px; margin-top: 75px;"> + position... + <div style="padding-bottom: 20px;"> + <b>my:</b> + <select id="my_horizontal"> + <option value="left">left</option> + <option value="center">center</option> + <option value="right" selected="selected">right</option> + </select> + <select id="my_vertical"> + <option value="top">top</option> + <option value="middle">center</option> + <option value="bottom">bottom</option> + </select> + </div> + <div style="padding-bottom: 20px;"> + <b>at:</b> + <select id="at_horizontal"> + <option value="left">left</option> + <option value="center">center</option> + <option value="right" selected="selected">right</option> + </select> + <select id="at_vertical"> + <option value="top">top</option> + <option value="middle">center</option> + <option value="bottom">bottom</option> + </select> + </div> + <div style="padding-bottom: 20px;"> + <b>offset:</b> + <input id="offset" type="text" size="15"/> + </div> + <div style="padding-bottom: 20px;"> + <b>collision:</b> + <select id="collision_horizontal"> + <option value="flip">flip</option> + <option value="fit">fit</option> + <option value="none">none</option> + </select> + <select id="collision_vertical"> + <option value="flip">flip</option> + <option value="fit">fit</option> + <option value="none">none</option> + </select> + </div> + </div> + +</div><!-- End demo --> +</div> + +</body> +</html> diff --git a/ui/jquery.effects.blind.js b/ui/jquery.effects.blind.js index 8ef544faa..b6485b641 100644 --- a/ui/jquery.effects.blind.js +++ b/ui/jquery.effects.blind.js @@ -21,7 +21,7 @@ $.effects.effect.blind = function( o ) { // Create element var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], mode = $.effects.setMode( el, o.mode || "hide" ), direction = o.direction || "up", vertical = rvertical.test( direction ), diff --git a/ui/jquery.effects.bounce.js b/ui/jquery.effects.bounce.js index 9e1117ce9..78fedb0ce 100644 --- a/ui/jquery.effects.bounce.js +++ b/ui/jquery.effects.bounce.js @@ -16,7 +16,7 @@ $.effects.effect.bounce = function(o) { return this.queue( function( next ) { var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], // defaults: mode = $.effects.setMode( el, o.mode || "effect" ), diff --git a/ui/jquery.effects.core.js b/ui/jquery.effects.core.js index 7650aa8f4..00a803360 100644 --- a/ui/jquery.effects.core.js +++ b/ui/jquery.effects.core.js @@ -410,7 +410,12 @@ $.extend( $.effects, { border: "none", margin: 0, padding: 0 - }); + }), + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }; element.wrap( wrapper ); wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element @@ -438,6 +443,7 @@ $.extend( $.effects, { bottom: "auto" }); } + element.css(size); return wrapper.css( props ).show(); }, diff --git a/ui/jquery.effects.drop.js b/ui/jquery.effects.drop.js index 24fb89db0..4265b737b 100644 --- a/ui/jquery.effects.drop.js +++ b/ui/jquery.effects.drop.js @@ -17,7 +17,7 @@ $.effects.effect.drop = function( o ) { return this.queue( function() { var el = $( this ), - props = [ 'position', 'top', 'bottom', 'left', 'right', 'opacity' ], + props = [ 'position', 'top', 'bottom', 'left', 'right', 'opacity', "height", "width" ], mode = $.effects.setMode( el, o.mode || 'hide' ), direction = o.direction || 'left', ref = ( direction == 'up' || direction == 'down' ) ? 'top' : 'left', diff --git a/ui/jquery.effects.fold.js b/ui/jquery.effects.fold.js index 29da090cb..6100c33a1 100644 --- a/ui/jquery.effects.fold.js +++ b/ui/jquery.effects.fold.js @@ -18,7 +18,7 @@ $.effects.effect.fold = function( o ) { // Create element var el = $( this ), - props = ['position','top','bottom','left','right'], + props = ['position','top','bottom','left','right','height','width'], mode = $.effects.setMode(el, o.mode || 'hide'), size = o.size || 15, percent = /([0-9]+)%/.exec(size), diff --git a/ui/jquery.effects.scale.js b/ui/jquery.effects.scale.js index 00f0151af..fe0c03c53 100644 --- a/ui/jquery.effects.scale.js +++ b/ui/jquery.effects.scale.js @@ -116,7 +116,7 @@ $.effects.effect.size = function( o ) { // Set options mode = $.effects.setMode( el, o.mode || 'effect' ), - restore = o.restore || false, + restore = o.restore || mode !== "effect", scale = o.scale || 'both', origin = o.origin, original, baseline, factor; diff --git a/ui/jquery.effects.shake.js b/ui/jquery.effects.shake.js index 550329ca4..52ab331e8 100644 --- a/ui/jquery.effects.shake.js +++ b/ui/jquery.effects.shake.js @@ -17,7 +17,7 @@ $.effects.effect.shake = function( o ) { return this.queue( function() { var el = $( this ), - props = [ "position", "top", "bottom", "left", "right" ], + props = [ "position", "top", "bottom", "left", "right", "height", "width" ], mode = $.effects.setMode( el, o.mode || "effect" ), direction = o.direction || "left", distance = o.distance || 20, diff --git a/ui/jquery.effects.slide.js b/ui/jquery.effects.slide.js index 6b0296754..ccb13fa1b 100644 --- a/ui/jquery.effects.slide.js +++ b/ui/jquery.effects.slide.js @@ -18,24 +18,26 @@ $.effects.effect.slide = function( o ) { // Create element var el = $( this ), - props = ['position','top','bottom','left','right'], + props = [ "position", "top", "bottom", "left", "right", "width", "height" ], mode = $.effects.setMode( el, o.mode || 'show' ), direction = o.direction || 'left', ref = (direction == 'up' || direction == 'down') ? 'top' : 'left', motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg', distance, - animation = {}; + animation = {}, + size; // Adjust - $.effects.save( el, props ); + $.effects.save( el, props ); el.show(); + distance = o.distance || el[ ref == 'top' ? "outerHeight" : "outerWidth" ]({ + margin: true + }); + $.effects.createWrapper( el ).css({ overflow: 'hidden' - }); - - distance = o.distance || el[ ref == 'top' ? "outerHeight" : "outerWidth" ]({ - margin: true }); + if (mode == 'show') { el.css( ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance ); } diff --git a/ui/jquery.ui.menubar.js b/ui/jquery.ui.menubar.js index a0e9afb3c..39e75924e 100644 --- a/ui/jquery.ui.menubar.js +++ b/ui/jquery.ui.menubar.js @@ -21,7 +21,11 @@ $.widget( "ui.menubar", { version: "@VERSION", options: { buttons: false, - menuIcon: false + menuIcon: false, + position: { + my: "left top", + at: "left bottom" + } }, _create: function() { var that = this; @@ -39,6 +43,9 @@ $.widget( "ui.menubar", { this._hoverable( items ); items.next( "ul" ) .menu({ + position: { + within: this.options.position.within + }, select: function( event, ui ) { ui.item.parents( "ul.ui-menu:last" ).hide(); that._trigger( "select", event, ui ); @@ -119,7 +126,7 @@ $.widget( "ui.menubar", { // TODO ui-menubar-link is added above, not needed here? input.addClass( "ui-menubar-link" ).removeClass( "ui-state-default" ); }; - + }); that._bind( { keydown: function( event ) { @@ -210,11 +217,9 @@ $.widget( "ui.menubar", { var button = menu.prev().addClass( "ui-state-active" ).attr( "tabIndex", -1 ); this.active = menu .show() - .position( { - my: "left top", - at: "left bottom", + .position( $.extend({ of: button - }) + }, this.options.position ) ) .removeAttr( "aria-hidden" ) .attr( "aria-expanded", "true" ) .menu("focus", event, menu.children( "li" ).first() ) diff --git a/ui/jquery.ui.position.js b/ui/jquery.ui.position.js index 98b8198e2..51e7561b3 100644 --- a/ui/jquery.ui.position.js +++ b/ui/jquery.ui.position.js @@ -19,6 +19,34 @@ var rhorizontal = /left|center|right/, center = "center", _position = $.fn.position; +$.position = { + scrollbarWidth : function() { + var div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ), + innerDiv = div.children()[0], + w1, w2; + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return w1 - w2; + }, + getScrollInfo : function( within ) { + var that = within[0], + scrollHeight = within.height() < that.scrollHeight, + scrollWidth = within.width() < that.scrollWidth, + scrollbarWidth = $.position.scrollbarWidth(); + return { height : scrollHeight ? scrollbarWidth : 0, width : scrollWidth ? scrollbarWidth : 0 }; + } +}; + $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); @@ -28,6 +56,7 @@ $.fn.position = function( options ) { options = $.extend( {}, options ); var target = $( options.of ), + within = $( options.within || window ), targetElem = target[0], collision = ( options.collision || "flip" ).split( " " ), offsets = {}, @@ -110,7 +139,7 @@ $.fn.position = function( options ) { parseInt( offsets.at[ 1 ], 10 ) * ( rpercent.test( offsets.at[ 1 ] ) ? targetHeight / 100 : 1 ) ]; - basePosition.left += atOffset[ 0 ], + basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each(function() { @@ -119,10 +148,11 @@ $.fn.position = function( options ) { elemHeight = elem.outerHeight(), marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0, marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0, + scrollInfo = $.position.getScrollInfo( within ), collisionWidth = elemWidth + marginLeft + - ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ), + ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + - ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ), + ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = [ parseInt( offsets.my[ 0 ], 10 ) * @@ -168,7 +198,8 @@ $.fn.position = function( options ) { collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, - at: options.at + at: options.at, + within: within }); } }); @@ -183,32 +214,40 @@ $.fn.position = function( options ) { $.ui.position = { fit: { left: function( position, data ) { - var win = $( window ), - overLeft = win.scrollLeft() - data.collisionPosition.left, - overRight = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(); + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? win.scrollLeft() : within.offset().left, + outerWidth = isWindow ? win.width() : within.outerWidth(), + overLeft = withinOffset - data.collisionPosition.left, + overRight = data.collisionPosition.left + data.collisionWidth - outerWidth - withinOffset; // element is wider than window or too far left -> align with left edge - if ( data.collisionWidth > win.width() || overLeft > 0 ) { - position.left = position.left + overLeft; + if ( data.collisionWidth > outerWidth || overLeft > 0 ) { + position.left += overLeft; // too far right -> align with right edge } else if ( overRight > 0 ) { - position.left = position.left - overRight; + position.left -= overRight; // adjust based on position and margin } else { position.left = Math.max( position.left - data.collisionPosition.left, position.left ); } }, top: function( position, data ) { - var win = $( window ), - overTop = win.scrollTop() - data.collisionPosition.top, - overBottom = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(); + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? win.scrollTop() : within.offset().top, + outerHeight = isWindow ? win.height() : within.outerHeight(), + overTop = withinOffset - data.collisionPosition.top, + overBottom = data.collisionPosition.top + data.collisionHeight - outerHeight - withinOffset; // element is taller than window or too far up -> align with top edge - if ( data.collisionHeight > win.height() || overTop > 0 ) { - position.top = position.top + overTop; + if ( data.collisionHeight > outerHeight || overTop > 0 ) { + position.top += overTop; // too far down -> align with bottom edge } else if ( overBottom > 0 ) { - position.top = position.top - overBottom; + position.top -= overBottom; // adjust based on position and margin } else { position.top = Math.max( position.top - data.collisionPosition.top, position.top ); @@ -220,8 +259,15 @@ $.ui.position = { if ( data.at[ 0 ] === center ) { return; } - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(), + + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? 0 : within.offset().left, + outerWidth = isWindow ? within.width() : within.outerWidth(), + overLeft = data.collisionPosition.left - withinOffset, + overRight = data.collisionPosition.left + data.collisionWidth - outerWidth - withinOffset, + left = data.my[ 0 ] === "left", myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? @@ -231,19 +277,23 @@ $.ui.position = { data.targetWidth : -data.targetWidth, offset = -2 * data.offset[ 0 ]; - position.left += data.collisionPosition.left < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; + if ( overLeft < 0 || overRight > 0 ) { + position.left += myOffset + atOffset + offset; + } }, top: function( position, data ) { if ( data.at[ 1 ] === center ) { return; } - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(), - myOffset = data.my[ 1 ] === "top" ? + var within = data.within, + win = $( window ), + isWindow = $.isWindow( data.within[0] ), + withinOffset = isWindow ? 0 : within.offset().top, + outerHeight = isWindow ? within.height() : within.outerHeight(), + overTop = data.collisionPosition.top - withinOffset, + overBottom = data.collisionPosition.top + data.collisionHeight - outerHeight - withinOffset, + top = data.my[ 1 ] === "top", + myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : @@ -252,11 +302,9 @@ $.ui.position = { data.targetHeight : -data.targetHeight, offset = -2 * data.offset[ 1 ]; - position.top += data.collisionPosition.top < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; + if ( overTop < 0 || overBottom > 0) { + position.top += myOffset + atOffset + offset; + } } } }; @@ -267,7 +315,7 @@ if ( $.uiBackCompat !== false ) { (function( $ ) { var _position = $.fn.position; $.fn.position = function( options ) { - if ( !options || !( "offset" in options ) ) { + if ( !options || !options.offset ) { return _position.call( this, options ); } var offset = options.offset.split( " " ), @@ -297,4 +345,4 @@ if ( $.uiBackCompat !== false ) { }( jQuery ) ); } -}( jQuery ) ); +}( jQuery ) );
\ No newline at end of file diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 00bc07c4f..59d110b6a 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -133,7 +133,7 @@ $.widget.bridge = function( name, object ) { } var methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue.jquery ? + returnValue = methodValue && methodValue.jquery ? returnValue.pushStack( methodValue.get() ) : methodValue; return false; @@ -239,9 +239,6 @@ $.Widget.prototype = { } if ( typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } options = {}; parts = key.split( "." ); @@ -252,8 +249,15 @@ $.Widget.prototype = { curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; curOption = curOption[ parts[ i ] ]; } - curOption[ parts.pop() ] = value; + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } options[ key ] = value; } } |