diff --git a/Makefile b/Makefile index 5cbe697b..e97775a6 100755 --- a/Makefile +++ b/Makefile @@ -14,6 +14,8 @@ FILES = js/jquery.ui.widget.js \ js/jquery.mobile.support.js \ js/jquery.mobile.event.js \ js/jquery.mobile.hashchange.js \ + js/jquery.mobile.core.js \ + js/jquery.mobile.navigation.js \ js/jquery.mobile.page.js \ js/jquery.ui.position.js \ js/jquery.mobile.fixHeaderFooter.js \ @@ -30,8 +32,7 @@ FILES = js/jquery.ui.widget.js \ js/jquery.mobile.listview.filter.js \ js/jquery.mobile.dialog.js \ js/jquery.mobile.navbar.js \ - js/jquery.mobile.grid.js \ - js/jquery.mobile.core.js + js/jquery.mobile.grid.js CSSFILES = themes/default/jquery.mobile.theme.css \ themes/default/jquery.mobile.core.css \ diff --git a/build.xml b/build.xml index 236a4bfb..bb03acff 100644 --- a/build.xml +++ b/build.xml @@ -20,11 +20,20 @@ jquery.mobile.theme.css, jquery.mobile.transitions.css"/> + jquery.mobile.listview.filter.js, + jquery.mobile.navbar.js" + /> diff --git a/experiments/themeswitcher/jquery.mobile.themeswitcher.js b/experiments/themeswitcher/jquery.mobile.themeswitcher.js index dc6d847c..34d67712 100644 --- a/experiments/themeswitcher/jquery.mobile.themeswitcher.js +++ b/experiments/themeswitcher/jquery.mobile.themeswitcher.js @@ -10,7 +10,7 @@ ''+ '
    '+ '' ) - .appendTo( $.pageContainer ), + .appendTo( $.mobile.pageContainer ), menu = menuPage.find('ul'); //menu items diff --git a/js/index.php b/js/index.php index 32ba4fed..e8239c7c 100644 --- a/js/index.php +++ b/js/index.php @@ -9,6 +9,7 @@ $elements = array( 'jquery.mobile.event.js', 'jquery.mobile.hashchange.js', 'jquery.mobile.core.js', + 'jquery.mobile.navigation.js', 'jquery.mobile.page.js', 'jquery.ui.position.js', 'jquery.mobile.fixHeaderFooter.js', diff --git a/js/jquery.mobile.core.js b/js/jquery.mobile.core.js index bbb913d1..11709e9f 100644 --- a/js/jquery.mobile.core.js +++ b/js/jquery.mobile.core.js @@ -49,23 +49,24 @@ } }); - //trigger mobileinit event - useful hook for configuring $.mobile settings before they're used - $( window.document ).trigger('mobileinit'); +//trigger mobileinit event - useful hook for configuring $.mobile settings before they're used + $( window.document ).trigger('mobileinit'); + + +//support conditions //if device support condition(s) aren't met, leave things as they are -> a basic, usable experience, //otherwise, proceed with the enhancements if ( !$.mobile.gradeA() ) { return; } - //define vars for interal use + +//define vars for interal use var $window = $(window), $html = $('html'), $head = $('head'), - //to be populated at DOM ready - $body, - //loading div which appears during Ajax requests //will not appear if $.mobile.loadingMessage is false $loader = $.mobile.loadingMessage ? @@ -73,504 +74,60 @@ ''+ '

    '+ $.mobile.loadingMessage +'

    '+ '') - : undefined, - - //define meta viewport tag, if content is defined - $metaViewport = $.mobile.metaViewportContent ? $("", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined, - - //define baseUrl for use in relative url management - baseUrl = getPathDir( location.protocol + '//' + location.host + location.pathname ), - - //define base element, for use in routing asset urls that are referenced in Ajax-requested markup - $base = $.support.dynamicBaseTag ? $("", { href: baseUrl }).prependTo( $head ) : undefined, - - //will be defined as first page element in DOM - $startPage, - - //will be defined as $startPage.parent(), which is usually the body element - //will receive ui-mobile-viewport class - $pageContainer, - - //will be defined when a link is clicked and given an active class - $activeClickedLink = null, - - //array of pages that are visited during a single page load - //length will grow as pages are visited, and shrink as "back" link/button is clicked - //each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked) - urlStack = [ { - url: location.hash.replace( /^#/, "" ), - transition: undefined - } ], - - //define first selector to receive focus when a page is shown - focusable = "[tabindex],a,button:visible,select:visible,input", - - //contains role for next page, if defined on clicked link via data-rel - nextPageRole = null, - - //enable/disable hashchange event listener - //toggled internally when location.hash is updated to match the url of a successful page load - hashListener = true; - - //add mobile, initial load "rendering" classes to docEl - $html.addClass('ui-mobile ui-mobile-rendering'); - - // TODO: don't expose (temporary during code reorg) - $.mobile.urlStack = urlStack; - - //consistent string escaping for urls and IDs - function idStringEscape(str){ - return str.replace(/[^a-zA-Z0-9]/g, '-'); - } - - $.mobile.idStringEscape = idStringEscape; - - // hide address bar - function silentScroll( ypos ) { - // prevent scrollstart and scrollstop events - $.event.special.scrollstart.enabled = false; - setTimeout(function() { - window.scrollTo( 0, ypos || 0 ); - },20); - setTimeout(function() { - $.event.special.scrollstart.enabled = true; - }, 150 ); - } - - function getPathDir( path ){ - var newPath = path.replace(/#/,'').split('/'); - newPath.pop(); - return newPath.join('/') + (newPath.length ? '/' : ''); - } - - function getBaseURL( nonHashPath ){ - return getPathDir( nonHashPath || location.hash ); - } - - var setBaseURL = !$.support.dynamicBaseTag ? $.noop : function( nonHashPath ){ - //set base url for new page assets - $base.attr('href', baseUrl + getBaseURL( nonHashPath )); - } - - var resetBaseURL = !$.support.dynamicBaseTag ? $.noop : function(){ - $base.attr('href', baseUrl); - } - - //set base href to pathname - resetBaseURL(); - - //for form submission - $('form').live('submit', function(event){ - if( !$.mobile.ajaxFormsEnabled ){ return; } - - var type = $(this).attr("method"), - url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, ""); - - //external submits use regular HTTP - if( /^(:?\w+:)/.test( url ) ){ - return; - } - - //if it's a relative href, prefix href with base url - if( url.indexOf('/') && url.indexOf('#') !== 0 ){ - url = getBaseURL() + url; - } - - changePage({ - url: url, - type: type, - data: $(this).serialize() - }, - undefined, - undefined, - true - ); - event.preventDefault(); - }); - - //click routing - direct to HTTP or Ajax, accordingly - $( "a" ).live( "click", function(event) { - var $this = $(this), - //get href, remove same-domain protocol and host - href = $this.attr( "href" ).replace( location.protocol + "//" + location.host, ""), - //if target attr is specified, it's external, and we mimic _blank... for now - target = $this.is( "[target]" ), - //if it still starts with a protocol, it's external, or could be :mailto, etc - external = target || /^(:?\w+:)/.test( href ) || $this.is( "[rel=external]" ), - target = $this.is( "[target]" ); - - if( href === '#' ){ - //for links created purely for interaction - ignore - return false; - } - - $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass ); - - if( external || !$.mobile.ajaxLinksEnabled ){ - //remove active link class if external - removeActiveLinkClass(true); - - //deliberately redirect, in case click was triggered - if( target ){ - window.open(href); - } - else{ - location.href = href; - } - } - else { - //use ajax - var transition = $this.data( "transition" ), - back = $this.data( "back" ), - changeHashOnSuccess = !$this.is( "[data-rel="+ $.mobile.nonHistorySelectors +"]" ); - - nextPageRole = $this.attr( "data-rel" ); - - //if it's a relative href, prefix href with base url - if( href.indexOf('/') && href.indexOf('#') !== 0 ){ - href = getBaseURL() + href; - } - - href.replace(/^#/,''); - - changePage(href, transition, back, changeHashOnSuccess); - } - event.preventDefault(); - }); - - // turn on/off page loading message. - function pageLoading( done ) { - if ( done ) { - $html.removeClass( "ui-loading" ); - } else { - - if( $.mobile.loadingMessage ){ - $loader.appendTo($pageContainer).css({top: $(window).scrollTop() + 75}); - } - $html.addClass( "ui-loading" ); - } - }; - - //for directing focus to the page title, or otherwise first focusable element - function reFocus(page){ - var pageTitle = page.find( ".ui-title:eq(0)" ); - if( pageTitle.length ){ - pageTitle.focus(); - } - else{ - page.find( focusable ).eq(0).focus(); - } - } - - //function for setting role of next page - function setPageRole( newPage ) { - if ( nextPageRole ) { - newPage.attr( "data-role", nextPageRole ); - nextPageRole = undefined; - } - } - - //update hash, with or without triggering hashchange event - $.mobile.updateHash = function(url, disableListening){ - if(disableListening) { hashListener = false; } - location.hash = url; - } - - //wrap page and transfer data-attrs if it has an ID - function wrapNewPage( newPage ){ - var copyAttrs = ['data-role', 'data-theme', 'data-fullscreen'], //TODO: more page-level attrs? - wrapper = newPage.wrap( "
    " ).parent(); - - $.each(copyAttrs,function(i){ - if( newPage.attr( copyAttrs[ i ] ) ){ - wrapper.attr( copyAttrs[ i ], newPage.attr( copyAttrs[ i ] ) ); - newPage.removeAttr( copyAttrs[ i ] ); - } - }); - return wrapper; - } - - //remove active classes after page transition or error - function removeActiveLinkClass(forceRemoval){ - if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){ - $activeClickedLink.removeClass( $.mobile.activeBtnClass ); - } - $activeClickedLink = null; - } + : undefined; - //for getting or creating a new page - function changePage( to, transition, back, changeHash){ - - //from is always the currently viewed page - var toIsArray = $.type(to) === "array", - from = toIsArray ? to[0] : $.mobile.activePage, - to = toIsArray ? to[1] : to, - url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, - data = undefined, - type = 'get', - isFormRequest = false, - duplicateCachedPage = null, - back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), - transition = (transition !== undefined) ? transition : $.mobile.defaultTransition; +//add mobile, initial load "rendering" classes to docEl + $html.addClass('ui-mobile ui-mobile-rendering'); - if( $.type(to) === "object" && to.url ){ - url = to.url, - data = to.data, - type = to.type, - isFormRequest = true; - //make get requests bookmarkable - if( data && type == 'get' ){ - url += "?" + data; - data = undefined; - } - } - //reset base to pathname for new request - resetBaseURL(); - - // if the new href is the same as the previous one - if ( back ) { - var pop = urlStack.pop(); - if( pop ){ - transition = pop.transition; - } - } else { - urlStack.push({ url: url, transition: transition }); - } - - //function for transitioning between two existing pages - function transitionPages() { - - //kill the keyboard - $( window.document.activeElement ).blur(); - - //get current scroll distance - var currScroll = $window.scrollTop(); - - //set as data for returning to that spot - from.data('lastScroll', currScroll); - - //trigger before show/hide events - from.data("page")._trigger("beforehide", {nextPage: to}); - to.data("page")._trigger("beforeshow", {prevPage: from}); - - function loadComplete(){ - pageLoading( true ); - //trigger show/hide events, allow preventing focus change through return false - if( from.data("page")._trigger("hide", null, {nextPage: to}) !== false && to.data("page")._trigger("show", null, {prevPage: from}) !== false ){ - $.mobile.activePage = to; - } - reFocus( to ); - if( changeHash && url ){ - $.mobile.updateHash(url, true); - } - removeActiveLinkClass(); - - //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden - if( duplicateCachedPage != null ){ - duplicateCachedPage.remove(); - } - - //jump to top or prev scroll, if set - silentScroll( to.data( 'lastScroll' ) ); - } - - if(transition && (transition !== 'none')){ - $pageContainer.addClass('ui-mobile-viewport-transitioning'); - // animate in / out - from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); - to.addClass( $.mobile.activePageClass + " " + transition + - " in " + ( back ? "reverse" : "" ) ); - - // callback - remove classes, etc - to.animationComplete(function() { - from.add( to ).removeClass("out in reverse " + transition ); - from.removeClass( $.mobile.activePageClass ); - loadComplete(); - $pageContainer.removeClass('ui-mobile-viewport-transitioning'); - }); - } - else{ - from.removeClass( $.mobile.activePageClass ); - to.addClass( $.mobile.activePageClass ); - loadComplete(); - } - }; - - //shared page enhancements - function enhancePage(){ - setPageRole( to ); - to.page(); - } - - //get the actual file in a jq-mobile nested url - function getFileURL( url ){ - return url.match( '&' + $.mobile.subPageUrlKey ) ? url.split( '&' + $.mobile.subPageUrlKey )[0] : url; - } - - //if url is a string - if( url ){ - to = $( "[id='" + url + "']" ), - fileUrl = getFileURL(url); - } - else{ //find base url of element, if avail - var toID = to.attr('id'), - toIDfileurl = getFileURL(toID); - - if(toID != toIDfileurl){ - fileUrl = toIDfileurl; - } - } - - // find the "to" page, either locally existing in the dom or by creating it through ajax - if ( to.length && !isFormRequest ) { - if( fileUrl ){ - setBaseURL(fileUrl); - } - enhancePage(); - transitionPages(); - } else { - - //if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change - if( to.length ){ - duplicateCachedPage = to; - } - - pageLoading(); - - $.ajax({ - url: fileUrl, - type: type, - data: data, - success: function( html ) { - setBaseURL(fileUrl); - var all = $("
    "); - //workaround to allow scripts to execute when included in page divs - all.get(0).innerHTML = html; - to = all.find('[data-role="page"]'); - - //rewrite src and href attrs to use a base url - if( !$.support.dynamicBaseTag ){ - var baseUrl = getBaseURL(fileUrl); - to.find('[src],link[href]').each(function(){ - var thisAttr = $(this).is('[href]') ? 'href' : 'src', - thisUrl = $(this).attr(thisAttr); - - //if full path exists and is same, chop it - helps IE out - thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); - - if( !/^(\w+:|#|\/)/.test(thisUrl) ){ - $(this).attr(thisAttr, baseUrl + thisUrl); - } - }); - } - - //preserve ID on a retrieved page - if ( to.attr('id') ) { - to = wrapNewPage( to ); - } - - to - .attr( "id", fileUrl ) - .appendTo( $pageContainer ); - - enhancePage(); - transitionPages(); - }, - error: function() { - pageLoading( true ); - removeActiveLinkClass(true); - $("

    Error Loading Page

    ") - .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 }) - .appendTo( $pageContainer ) - .delay( 800 ) - .fadeOut( 400, function(){ - $(this).remove(); - }); - } - }); - } - - }; +//define & prepend meta viewport tag, if content is defined + $.mobile.metaViewportContent ? $("", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined; - $(function() { - - $body = $( "body" ); - pageLoading(); - - // needs to be bound at domready (for IE6) - // find or load content, make it active - $window.bind( "hashchange", function(e, triggered) { - if( !hashListener ){ - hashListener = true; - return; - } - - if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){ - return; - } - - var to = location.hash, - transition = triggered ? false : undefined; - - // either we've backed up to the root page url - // or it's the first page load with no hash present - //there's a hash and it wasn't manually triggered - // > probably a new page, "back" will be figured out by changePage - if ( to ){ - changePage( to, transition); - } - //there's no hash, the active page is not the start page, and it's not manually triggered hashchange - // > probably backed out to the first page visited - else if( $.mobile.activePage.length && $startPage[0] !== $.mobile.activePage[0] && !triggered ) { - changePage( $startPage, transition, true ); - } - else{ - $startPage.trigger("pagebeforeshow", {prevPage: $('')}); - $startPage.addClass( $.mobile.activePageClass ); - pageLoading( true ); - - if( $startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ - reFocus($startPage); - } - } - - }); - }); - - //animation complete callback - //TODO - update support test and create special event for transitions - //check out transitionEnd (opera per Paul's request) - $.fn.animationComplete = function(callback){ - if($.support.cssTransitions){ - return $(this).one('webkitAnimationEnd', callback); - } - else{ - callback(); - } - }; - - //TODO - add to jQuery.mobile, not $ +//expose some core utilities $.extend($.mobile, { - pageLoading: pageLoading, - changePage: changePage, - silentScroll: silentScroll - }); - - //dom-ready + + // turn on/off page loading message. + pageLoading: function ( done ) { + if ( done ) { + $html.removeClass( "ui-loading" ); + } else { + if( $.mobile.loadingMessage ){ + $loader.appendTo($.mobile.pageContainer).css({top: $(window).scrollTop() + 75}); + } + $html.addClass( "ui-loading" ); + } + }, + + //scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value + silentScroll: function( ypos ) { + // prevent scrollstart and scrollstop events + $.event.special.scrollstart.enabled = false; + setTimeout(function() { + window.scrollTo( 0, ypos || 0 ); + },20); + setTimeout(function() { + $.event.special.scrollstart.enabled = true; + }, 150 ); + } + }); + + +//dom-ready inits $(function(){ + + //find present pages var $pages = $("[data-role='page']"); + //set up active page - $startPage = $.mobile.activePage = $pages.first(); + $.mobile.startPage = $.mobile.activePage = $pages.first(); //set page container - $pageContainer = $startPage.parent().addClass('ui-mobile-viewport'); + $.mobile.pageContainer = $.mobile.startPage.parent().addClass('ui-mobile-viewport'); - $.extend({ - pageContainer: $pageContainer - }); + //cue page loading message + $.mobile.pageLoading(); //initialize all pages present $pages.page(); @@ -582,6 +139,9 @@ $html.removeClass('ui-mobile-rendering'); }); - $window.load(silentScroll); + +//window load event + //hide iOS browser chrome on load + $window.load( $.mobile.silentScroll ); })( jQuery, this ); diff --git a/js/jquery.mobile.forms.select.js b/js/jquery.mobile.forms.select.js index 22e8c265..cfb2106d 100644 --- a/js/jquery.mobile.forms.select.js +++ b/js/jquery.mobile.forms.select.js @@ -65,7 +65,7 @@ $.widget( "mobile.selectmenu", $.mobile.widget, { "
    "+ "
    "+ "" ) - .appendTo( $.pageContainer ) + .appendTo( $.mobile.pageContainer ) .page(), menuPageContent = menuPage.find( ".ui-content" ), diff --git a/js/jquery.mobile.listview.js b/js/jquery.mobile.listview.js index 6ed95f3c..ff2a42da 100644 --- a/js/jquery.mobile.listview.js +++ b/js/jquery.mobile.listview.js @@ -289,13 +289,14 @@ $.widget( "mobile.listview", $.mobile.widget, { parentPage = parentList.closest( ".ui-page" ), parentId = parentPage.attr( "id" ), o = this.options, + self = this, persistentFooterID = parentPage.find( "[data-role='footer']" ).data( "id" ); $( parentList.find( "ul, ol" ).toArray().reverse() ).each(function( i ) { var list = $( this ), parent = list.parent(), title = parent.contents()[ 0 ].nodeValue.split("\n")[0], - id = parentId + "&" + $.mobile.subPageUrlKey + "=" + self.idStringEscape(title + " " + i), + id = parentId + "&" + $.mobile.subPageUrlKey + "=" + self._idStringEscape(title + " " + i), theme = list.data( "theme" ) || o.theme, countTheme = list.data( "counttheme" ) || parentList.data( "counttheme" ) || o.countTheme, newPage = list.wrap( "
    " ) @@ -308,7 +309,7 @@ $.widget( "mobile.listview", $.mobile.widget, { "data-theme": theme, "data-count-theme": countTheme }) - .appendTo( $.pageContainer ); + .appendTo( $.mobile.pageContainer ); diff --git a/js/jquery.mobile.navigation.js b/js/jquery.mobile.navigation.js new file mode 100644 index 00000000..b45ad18c --- /dev/null +++ b/js/jquery.mobile.navigation.js @@ -0,0 +1,466 @@ +/* +* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt, +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +(function($, undefined ) { + + //define vars for interal use + var $window = $(window), + $html = $('html'), + $head = $('head'), + + //url path helpers for use in relative url management + path = { + + //get path from current hash, or from a file path + get: function( newPath ){ + if( newPath == undefined ){ + newPath = location.hash; + } + newPath = newPath.replace(/#/,'').split('/'); + newPath.pop(); + return newPath.join('/') + (newPath.length ? '/' : ''); + }, + + //return the substring of a filepath before the sub-page key, for making a server request + getFilePath: function( path ){ + var splitkey = '&' + $.mobile.subPageUrlKey; + return path.indexOf( splitkey ) > -1 ? path.split( splitkey )[0] : path; + }, + + set: function( path, disableListening){ + if(disableListening) { hashListener = false; } + location.hash = path; + }, + + //location pathname from intial directory request + origin: null, + + setOrigin: function(){ + path.origin = path.get( location.protocol + '//' + location.host + location.pathname ); + } + }, + + //base element management, defined depending on dynamic base tag support + base = $.support.dynamicBaseTag ? { + + //define base element, for use in routing asset urls that are referenced in Ajax-requested markup + element: $("", { href: path.origin }).prependTo( $head ), + + //set the generated BASE element's href attribute to a new page's base path + set: function( href ){ + base.element.attr('href', path.origin + path.get( href )); + }, + + //set the generated BASE element's href attribute to a new page's base path + reset: function(){ + base.element.attr('href', path.origin ); + } + + } : undefined, + + + //will be defined when a link is clicked and given an active class + $activeClickedLink = null, + + //array of pages that are visited during a single page load + //length will grow as pages are visited, and shrink as "back" link/button is clicked + //each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked) + urlStack = [ { + url: location.hash.replace( /^#/, "" ), + transition: undefined + } ], + + //define first selector to receive focus when a page is shown + focusable = "[tabindex],a,button:visible,select:visible,input", + + //contains role for next page, if defined on clicked link via data-rel + nextPageRole = null, + + //enable/disable hashchange event listener + //toggled internally when location.hash is updated to match the url of a successful page load + hashListener = true; + + //set location pathname from intial directory request + path.setOrigin(); + + +/* + internal utility functions +--------------------------------------*/ + + + //direct focus to the page title, or otherwise first focusable element + function reFocus( page ){ + var pageTitle = page.find( ".ui-title:eq(0)" ); + if( pageTitle.length ){ + pageTitle.focus(); + } + else{ + page.find( focusable ).eq(0).focus(); + } + }; + + //remove active classes after page transition or error + function removeActiveLinkClass( forceRemoval ){ + if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){ + $activeClickedLink.removeClass( $.mobile.activeBtnClass ); + } + $activeClickedLink = null; + }; + + + //animation complete callback + $.fn.animationComplete = function( callback ){ + if($.support.cssTransitions){ + return $(this).one('webkitAnimationEnd', callback); + } + else{ + callback(); + } + }; + + + +/* exposed $.mobile methods */ + + //update location.hash, with or without triggering hashchange event + $.mobile.updateHash = path.set; + + //url stack, useful when plugins need to be aware of previous pages viewed + $.mobile.urlStack = urlStack; + + // changepage function + $.mobile.changePage = function( to, transition, back, changeHash){ + + //from is always the currently viewed page + var toIsArray = $.type(to) === "array", + from = toIsArray ? to[0] : $.mobile.activePage, + to = toIsArray ? to[1] : to, + url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, + data = undefined, + type = 'get', + isFormRequest = false, + duplicateCachedPage = null, + back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), + transition = (transition !== undefined) ? transition : $.mobile.defaultTransition; + + if( $.type(to) === "object" && to.url ){ + url = to.url, + data = to.data, + type = to.type, + isFormRequest = true; + //make get requests bookmarkable + if( data && type == 'get' ){ + url += "?" + data; + data = undefined; + } + } + + //reset base to pathname for new request + if(base){ base.reset(); } + + // if the new href is the same as the previous one + if ( back ) { + var pop = urlStack.pop(); + if( pop ){ + transition = pop.transition; + } + } else { + urlStack.push({ url: url, transition: transition }); + } + + //function for transitioning between two existing pages + function transitionPages() { + + //kill the keyboard + $( window.document.activeElement ).blur(); + + //get current scroll distance + var currScroll = $window.scrollTop(); + + //set as data for returning to that spot + from.data('lastScroll', currScroll); + + //trigger before show/hide events + from.data("page")._trigger("beforehide", {nextPage: to}); + to.data("page")._trigger("beforeshow", {prevPage: from}); + + function loadComplete(){ + $.mobile.pageLoading( true ); + //trigger show/hide events, allow preventing focus change through return false + if( from.data("page")._trigger("hide", null, {nextPage: to}) !== false && to.data("page")._trigger("show", null, {prevPage: from}) !== false ){ + $.mobile.activePage = to; + } + reFocus( to ); + if( changeHash !== false && url ){ + path.set(url, true); + } + removeActiveLinkClass(); + + //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden + if( duplicateCachedPage != null ){ + duplicateCachedPage.remove(); + } + + //jump to top or prev scroll, if set + $.mobile.silentScroll( to.data( 'lastScroll' ) ); + }; + + if(transition && (transition !== 'none')){ + $.mobile.pageContainer.addClass('ui-mobile-viewport-transitioning'); + // animate in / out + from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); + to.addClass( $.mobile.activePageClass + " " + transition + + " in " + ( back ? "reverse" : "" ) ); + + // callback - remove classes, etc + to.animationComplete(function() { + from.add( to ).removeClass("out in reverse " + transition ); + from.removeClass( $.mobile.activePageClass ); + loadComplete(); + $.mobile.pageContainer.removeClass('ui-mobile-viewport-transitioning'); + }); + } + else{ + from.removeClass( $.mobile.activePageClass ); + to.addClass( $.mobile.activePageClass ); + loadComplete(); + } + }; + + //shared page enhancements + function enhancePage(){ + + //set next page role, if defined + if ( nextPageRole ) { + to.attr( "data-role", nextPageRole ); + nextPageRole = undefined; + } + + //run page plugin + to.page(); + }; + + //if url is a string + if( url ){ + to = $( "[id='" + url + "']" ), + fileUrl = path.getFilePath(url); + } + else{ //find base url of element, if avail + var toID = to.attr('id'), + toIDfileurl = path.getFilePath(toID); + + if(toID != toIDfileurl){ + fileUrl = toIDfileurl; + } + } + + // find the "to" page, either locally existing in the dom or by creating it through ajax + if ( to.length && !isFormRequest ) { + if( fileUrl && base ){ + base.set( fileUrl ); + } + enhancePage(); + transitionPages(); + } else { + + //if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change + if( to.length ){ + duplicateCachedPage = to; + } + + $.mobile.pageLoading(); + + $.ajax({ + url: fileUrl, + type: type, + data: data, + success: function( html ) { + + if(base){ base.set(fileUrl); } + + var all = $("
    "); + //workaround to allow scripts to execute when included in page divs + all.get(0).innerHTML = html; + to = all.find('[data-role="page"]'); + + //rewrite src and href attrs to use a base url + if( !$.support.dynamicBaseTag ){ + var newPath = path.get( fileUrl ); + to.find('[src],link[href]').each(function(){ + var thisAttr = $(this).is('[href]') ? 'href' : 'src', + thisUrl = $(this).attr(thisAttr); + + //if full path exists and is same, chop it - helps IE out + thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); + + if( !/^(\w+:|#|\/)/.test(thisUrl) ){ + $(this).attr(thisAttr, newPath + thisUrl); + } + }); + } + + //preserve ID on a retrieved page + if ( to.attr('id') ) { + //wrap page and transfer data-attrs if it has an ID + var copyAttrs = ['data-role', 'data-theme', 'data-fullscreen'], //TODO: more page-level attrs? + wrapper = to.wrap( "
    " ).parent(); + + $.each(copyAttrs,function(i){ + if( to.attr( copyAttrs[ i ] ) ){ + wrapper.attr( copyAttrs[ i ], to.attr( copyAttrs[ i ] ) ); + to.removeAttr( copyAttrs[ i ] ); + } + }); + to = wrapper; + } + + to + .attr( "id", fileUrl ) + .appendTo( $.mobile.pageContainer ); + + enhancePage(); + transitionPages(); + }, + error: function() { + $.mobile.pageLoading( true ); + removeActiveLinkClass(true); + $("

    Error Loading Page

    ") + .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 }) + .appendTo( $.mobile.pageContainer ) + .delay( 800 ) + .fadeOut( 400, function(){ + $(this).remove(); + }); + } + }); + } + + }; + + + + +/* Event Bindings - hashchange, submit, and click */ + + //bind to form submit events, handle with Ajax + $('form').live('submit', function(event){ + if( !$.mobile.ajaxFormsEnabled ){ return; } + + var type = $(this).attr("method"), + url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, ""); + + //external submits use regular HTTP + if( /^(:?\w+:)/.test( url ) ){ + return; + } + + //if it's a relative href, prefix href with base url + if( url.indexOf('/') && url.indexOf('#') !== 0 ){ + url = path.get() + url; + } + + $.mobile.changePage({ + url: url, + type: type, + data: $(this).serialize() + }, + undefined, + undefined, + true + ); + event.preventDefault(); + }); + + + //click routing - direct to HTTP or Ajax, accordingly + $( "a" ).live( "click", function(event) { + if( !$.mobile.ajaxLinksEnabled ){ return; } + var $this = $(this), + //get href, remove same-domain protocol and host + href = $this.attr( "href" ).replace( location.protocol + "//" + location.host, ""), + //if target attr is specified, it's external, and we mimic _blank... for now + target = $this.is( "[target]" ), + //if it still starts with a protocol, it's external, or could be :mailto, etc + external = target || /^(:?\w+:)/.test( href ) || $this.is( "[rel=external]" ), + target = $this.is( "[target]" ); + + if( href === '#' ){ + //for links created purely for interaction - ignore + return false; + } + + $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass ); + + if( external || !$.mobile.ajaxLinksEnabled ){ + //remove active link class if external + removeActiveLinkClass(true); + + //deliberately redirect, in case click was triggered + if( target ){ + window.open(href); + } + else{ + location.href = href; + } + } + else { + //use ajax + var transition = $this.data( "transition" ), + back = $this.data( "back" ), + changeHashOnSuccess = !$this.is( "[data-rel="+ $.mobile.nonHistorySelectors +"]" ); + + nextPageRole = $this.attr( "data-rel" ); + + //if it's a relative href, prefix href with base url + if( href.indexOf('/') && href.indexOf('#') !== 0 ){ + href = path.get() + href; + } + + href.replace(/^#/,''); + + $.mobile.changePage(href, transition, back, changeHashOnSuccess); + } + event.preventDefault(); + }); + + + + //hashchange event handler + $window.bind( "hashchange", function(e, triggered) { + if( !hashListener ){ + hashListener = true; + return; + } + + if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){ + return; + } + + var to = location.hash, + transition = triggered ? false : undefined; + + //if to is defined, use it + if ( to ){ + $.mobile.changePage( to, transition); + } + //there's no hash, the active page is not the start page, and it's not manually triggered hashchange + //we probably backed out to the first page visited + else if( $.mobile.activePage.length && $.mobile.startPage[0] !== $.mobile.activePage[0] && !triggered ) { + $.mobile.changePage( $.mobile.startPage, transition, true ); + } + //probably the first page - show it + else{ + $.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')}); + $.mobile.startPage.addClass( $.mobile.activePageClass ); + $.mobile.pageLoading( true ); + + if( $.mobile.startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ + reFocus($.mobile.startPage); + } + } + }); +})( jQuery ); \ No newline at end of file