Permalink
Browse files

Toolbar: Back button correctly appears/disappears on external toolbar

This commit makes the following modifications:
1. Rename _addBackButton() to _updateBackButton() and move the decision making
   as to whether to add or remove a back button from _setOptions() into
   _updateBackButton().

2. Call _updateBackButton() from refresh() as well. This will cause the back
   button to be updated whenever the page changes, because the "pageshow"
   handler is hooked up to refresh().

3. Modify _addHeaderButtonClasses to not recognize the back button as a left
   button.

4. Modify the preconditions for adding a button to include an alternative to
   checking the page's URL for instances where there is no page present - i.e.
   when the toolbar is external. In such cases one must add a back button if
   the active item on the history stack is not the first item.

Closes gh-7188
Fixes gh-6950
  • Loading branch information...
1 parent dde4873 commit b0685b3f19d1f5b7a9d0198a89236eade2cb801b @gabrielschulhof gabrielschulhof committed Feb 27, 2014
View
84 js/widgets/toolbar.js
@@ -46,15 +46,7 @@ define( [
},
_setOptions: function( o ) {
if ( o.addBackBtn !== undefined ) {
- if ( this.options.addBackBtn &&
- this.role === "header" &&
- $( ".ui-page" ).length > 1 &&
- this.page[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) !== $.mobile.path.stripHash( location.hash ) &&
- !this.leftbtn ) {
- this._addBackButton();
- } else {
- this.element.find( ".ui-toolbar-back-btn" ).remove();
- }
+ this._updateBackButton();
}
if ( o.backBtnTheme != null ) {
this.element
@@ -81,6 +73,8 @@ define( [
this._setRelative();
if ( this.role === "footer" ) {
this.element.appendTo( "body" );
+ } else if ( this.role === "header" ) {
+ this._updateBackButton();
}
}
this._addHeadingClasses();
@@ -102,25 +96,71 @@ define( [
},
// Deprecated in 1.4. As from 1.5 ui-btn-left/right classes have to be present in the markup.
_addHeaderButtonClasses: function() {
- var $headeranchors = this.element.children( "a, button" );
- this.leftbtn = $headeranchors.hasClass( "ui-btn-left" );
- this.rightbtn = $headeranchors.hasClass( "ui-btn-right" );
+ var headerAnchors = this.element.children( "a, button" );
+
+ // Do not mistake a back button for a left toolbar button
+ this.leftbtn = headerAnchors.hasClass( "ui-btn-left" ) &&
+ !headerAnchors.hasClass( "ui-toolbar-back-btn" );
- this.leftbtn = this.leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
+ this.rightbtn = headerAnchors.hasClass( "ui-btn-right" );
- this.rightbtn = this.rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
+ // Filter out right buttons and back buttons
+ this.leftbtn = this.leftbtn ||
+ headerAnchors.eq( 0 )
+ .not( ".ui-btn-right,.ui-toolbar-back-btn" )
+ .addClass( "ui-btn-left" )
+ .length;
+ this.rightbtn = this.rightbtn || headerAnchors.eq( 1 ).addClass( "ui-btn-right" ).length;
},
- _addBackButton: function() {
- var options = this.options,
+ _updateBackButton: function() {
+ var backButton,
+ options = this.options,
theme = options.backBtnTheme || options.theme;
- $( "<a role='button' href='javascript:void(0);' " +
- "class='ui-btn ui-corner-all ui-shadow ui-btn-left " +
- ( theme ? "ui-btn-" + theme + " " : "" ) +
- "ui-toolbar-back-btn ui-icon-carat-l ui-btn-icon-left' " +
- "data-" + $.mobile.ns + "rel='back'>" + options.backBtnText + "</a>" )
- .prependTo( this.element );
+ // Retrieve the back button or create a new, empty one
+ backButton = this._backButton = ( this._backButton || {} );
+
+ // We add a back button only if the option to do so is on
+ if ( this.options.addBackBtn &&
+
+ // This must also be a header toolbar
+ this.role === "header" &&
+
+ // There must be multiple pages in the DOM
+ $( ".ui-page" ).length > 1 &&
+ ( this.page ?
+
+ // If the toolbar is internal the page's URL must differ from the hash
+ ( this.page[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) !==
+ $.mobile.path.stripHash( location.hash ) ) :
+
+ // Otherwise, if the toolbar is external there must be at least one
+ // history item to which one can go back
+ ( $.mobile.navigate && $.mobile.navigate.history &&
+ $.mobile.navigate.history.activeIndex > 0 ) ) &&
+
+ // The toolbar does not have a left button
+ !this.leftbtn ) {
+
+ // Skip back button creation if one is already present
+ if ( !backButton.attached ) {
+ backButton.element = ( backButton.element ||
+ $( "<a role='button' href='javascript:void(0);' " +
+ "class='ui-btn ui-corner-all ui-shadow ui-btn-left " +
+ ( theme ? "ui-btn-" + theme + " " : "" ) +
+ "ui-toolbar-back-btn ui-icon-carat-l ui-btn-icon-left' " +
+ "data-" + $.mobile.ns + "rel='back'>" + options.backBtnText +
+ "</a>" ) )
+ .prependTo( this.element );
+ backButton.attached = true;
+ }
+
+ // If we are not adding a back button, then remove the one present, if any
+ } else if ( backButton.element ) {
+ backButton.element.detach();
+ backButton.attached = false;
+ }
},
_addHeadingClasses: function() {
this.element.children( "h1, h2, h3, h4, h5, h6" )
View
55 tests/integration/toolbar/external-toolbar-tests.html
@@ -0,0 +1,55 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>jQuery Mobile Toolbar Test Suite</title>
+
+ <script src="../../../external/requirejs/require.js"></script>
+ <script src="../../../js/requirejs.config.js"></script>
+ <script src="../../../js/jquery.tag.inserter.js"></script>
+ <script src="../../jquery.setNameSpace.js"></script>
+ <script src="../../../tests/jquery.testHelper.js"></script>
+ <script src="../../../external/qunit/qunit.js"></script>
+ <script>
+ $.testHelper.asyncLoad([
+ [
+ "widgets/toolbar",
+ "buttonMarkup"
+ ],
+ [ "init" ],
+ [ "external_toolbar_core.js" ]
+ ]);
+ </script>
+
+ <link rel="stylesheet" href="../../../css/themes/default/jquery.mobile.css"/>
+ <link rel="stylesheet" href="../../../external/qunit/qunit.css"/>
+ <link rel="stylesheet" href="../../jqm-tests.css"/>
+
+ <script src="../../swarminject.js"></script>
+</head>
+<body>
+
+ <div id="qunit"></div>
+
+ <div id="header" data-nstest-role="header" data-nstest-add-back-btn="true">
+ <h1>Header</h1>
+ </div>
+ <div data-nstest-role="page">
+ <div class="ui-content" role="main">
+ <a href="#page2" id="go-to-page-2">Go to page 2</a>
+ </div>
+ </div>
+ <div data-nstest-role="page" id="page2">
+ <div data-nstest-role="content">
+ <a href="#page3" id="go-to-page-3">Go to page 3</a>
+ <p>Content</p>
+ </div>
+ </div>
+ <div data-nstest-role="page" id="page3">
+ <div data-nstest-role="content">
+ <p>Content</p>
+ </div>
+ </div>
+</body>
+</html>
View
43 tests/integration/toolbar/external_toolbar_core.js
@@ -0,0 +1,43 @@
+asyncTest( "Back button added to external toolbar", function() {
+ $( "#header" ).toolbar();
+ $.testHelper.pageSequence([
+ function() {
+ $( "#go-to-page-2" ).click();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 1,
+ "After navigating to page 2 exactly one back button " +
+ "appears on the external toolbar" );
+ $.mobile.back();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 0,
+ "After going back from page 2 no back button appears on the external toolbar" );
+ $( "#go-to-page-2" ).click();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 1,
+ "After navigating to page 2 again exactly one back button " +
+ "appears on the external toolbar" );
+ $( "#go-to-page-3" ).click();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 1,
+ "After navigating to page 3 exactly one back button " +
+ "appears on the external toolbar" );
+ $.mobile.back();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 1,
+ "After navigating back to page 2 exactly one back button " +
+ "appears on the external toolbar" );
+ $.mobile.back();
+ },
+ function() {
+ deepEqual( $( "#header" ).find( ".ui-toolbar-back-btn" ).length, 0,
+ "After navigating back from page 2 no back button " +
+ "appears on the external toolbar" );
+ start();
+ },
+ ]);
+});

0 comments on commit b0685b3

Please sign in to comment.