jquery-mobile/js/jquery.mobile.page.js
scottjehl 9b57c46349 Refactored urlStack and updated dialog and page plugins to match. jQuery Mobile's internal history now attempts to follow the history menu when urls change from hashchange, even for changes that go multiple steps forward or back. The internal history stack is now pruned based on whether a user goes back and then changes direction, whereas before a back-button click would result in a pop off the history. Instead we now maintain an active index number in the history stack, which allows us to maintain references to transitions that are saved on pages reached through clicking the forward button as well. This fixes #636.
In the process, some other small changes should be noted:
urlStack is now urlHistory, a hash of methods and properties used for history stack management (stack, activeIndex, getActive, getPrev, getNext, addNew, clearForward, and listening Enabled). All these are documented inline and exposed on $.mobile.urlHistory (I'm not sure these will be publicly documented, but just exposed internally for plugins for now).

$.changePage has two argument changes: the "back" argument is now called "reverse"; this results in no change from an end-user standpoint, but reflects the fact that it only reverses the direction of a transition without affecting the internal history stack, and second, a new argument at the end defines whether changePage was called from a hashChange which makes that url open to history menu guessing.
2011-01-23 17:33:36 -05:00

191 lines
5 KiB
JavaScript

/*
* jQuery Mobile Framework : "page" plugin
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function($, undefined ) {
$.widget( "mobile.page", $.mobile.widget, {
options: {
backBtnText: "Back",
addBackBtn: true,
degradeInputs: {
color: false,
date: false,
datetime: false,
"datetime-local": false,
email: false,
month: false,
number: false,
range: "number",
search: true,
tel: false,
time: false,
url: false,
week: false
},
keepNative: null
},
_create: function() {
var $elem = this.element,
o = this.options;
this.keepNative = "[data-role='none'], [data-role='nojs']" + (o.keepNative ? ", " + o.keepNative : "");
if ( this._trigger( "beforeCreate" ) === false ) {
return;
}
//some of the form elements currently rely on the presence of ui-page and ui-content
// classes so we'll handle page and content roles outside of the main role processing
// loop below.
$elem.find( "[data-role='page'], [data-role='content']" ).andSelf().each(function() {
$(this).addClass( "ui-" + $(this).data( "role" ) );
});
$elem.find( "[data-role='nojs']" ).addClass( "ui-nojs" );
// pre-find data els
var $dataEls = $elem.find( "[data-role]" ).andSelf().each(function() {
var $this = $( this ),
role = $this.data( "role" ),
theme = $this.data( "theme" );
//apply theming and markup modifications to page,header,content,footer
if ( role === "header" || role === "footer" ) {
$this.addClass( "ui-bar-" + (theme || $this.parent('[data-role=page]').data( "theme" ) || "a") );
// add ARIA role
$this.attr( "role", role === "header" ? "banner" : "contentinfo" );
//right,left buttons
var $headeranchors = $this.children( "a" ),
leftbtn = $headeranchors.hasClass( "ui-btn-left" ),
rightbtn = $headeranchors.hasClass( "ui-btn-right" );
if ( !leftbtn ) {
leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
}
if ( !rightbtn ) {
rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
}
// auto-add back btn on pages beyond first view
if ( o.addBackBtn && role === "header" &&
($.mobile.urlHistory.getPrev() || $(".ui-page").length > 1) &&
!leftbtn && $this.data( "backbtn" ) !== false ) {
$( "<a href='#' class='ui-btn-left' data-icon='arrow-l'>"+ o.backBtnText +"</a>" )
.click(function() {
history.back();
return false;
})
.prependTo( $this );
}
//page title
$this.children( "h1, h2, h3, h4, h5, h6" )
.addClass( "ui-title" )
//regardless of h element number in src, it becomes h1 for the enhanced page
.attr({ "tabindex": "0", "role": "heading", "aria-level": "1" });
} else if ( role === "content" ) {
if ( theme ) {
$this.addClass( "ui-body-" + theme );
}
// add ARIA role
$this.attr( "role", "main" );
} else if ( role === "page" ) {
$this.addClass( "ui-body-" + (theme || "c") );
}
switch(role) {
case "header":
case "footer":
case "page":
case "content":
$this.addClass( "ui-" + role );
break;
case "collapsible":
case "fieldcontain":
case "navbar":
case "listview":
case "dialog":
$this[ role ]();
break;
}
});
//enhance form controls
this._enhanceControls();
//links in bars, or those with data-role become buttons
$elem.find( "[data-role='button'], .ui-bar > a, .ui-header > a, .ui-footer > a" )
.not( ".ui-btn" )
.not(this.keepNative)
.buttonMarkup();
$elem
.find("[data-role='controlgroup']")
.controlgroup();
//links within content areas
$elem.find( "a:not(.ui-btn):not(.ui-link-inherit)" )
.not(this.keepNative)
.addClass( "ui-link" );
//fix toolbars
$elem.fixHeaderFooter();
},
_enhanceControls: function() {
var o = this.options;
// degrade inputs to avoid poorly implemented native functionality
this.element.find( "input" ).not(this.keepNative).each(function() {
var type = this.getAttribute( "type" ),
optType = o.degradeInputs[ type ] || "text";
if ( o.degradeInputs[ type ] ) {
$( this ).replaceWith(
$( "<div>" ).html( $(this).clone() ).html()
.replace( /type="([a-zA-Z]+)"/, "type="+ optType +" data-type='$1'" ) );
}
});
// enchance form controls
this.element
.find( "[type='radio'], [type='checkbox']" )
.not(this.keepNative)
.checkboxradio();
this.element
.find( "button, [type='button'], [type='submit'], [type='reset'], [type='image']" )
.not(this.keepNative)
.button();
this.element
.find( "input, textarea" )
.not( "[type='radio'], [type='checkbox'], button, [type='button'], [type='submit'], [type='reset'], [type='image'], [type='hidden']" )
.not(this.keepNative)
.textinput();
this.element
.find( "input, select" )
.not(this.keepNative)
.filter( "[data-role='slider'], [data-type='range']" )
.slider();
this.element
.find( "select:not([data-role='slider'])" )
.not(this.keepNative)
.selectmenu();
}
});
})( jQuery );