From 0d8956cf0c48cc6480d17e84b499f3594b51142e Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 13:11:46 -0400 Subject: [PATCH 01/13] big refactor of hashchange event handler - moved logic over to changePage function. --- js/jquery.mobile.js | 317 ++++++++++++++++++++++++-------------------- 1 file changed, 171 insertions(+), 146 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 5a23073a511..1899844665b 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -46,6 +46,7 @@ $pageContainer, startPageId = 'ui-page-start', activePageClass = 'ui-page-active', + $activePage, pageTransition, forceBack, transitions = 'slide slideup slidedown pop flip fade', @@ -56,7 +57,8 @@ transition: "slide" } ], focusable = "[tabindex],a,button:visible,select:visible,input", - nextPageRole = null; + nextPageRole = null, + preventLoad = false; // TODO: don't expose (temporary during code reorg) $.mobile.urlStack = urlStack; @@ -80,8 +82,10 @@ }, 150 ); } - function getBaseURL(){ - var newBaseURL = location.hash.replace(/#/,'').split('/'); + function getBaseURL( nonHashPath ){ + var newPath = nonHashPath || location.hash, + newBaseURL = newPath.replace(/#/,'').split('/'); + if(newBaseURL.length && /[.|&]/.test(newBaseURL[newBaseURL.length-1]) ){ newBaseURL.pop(); } @@ -158,174 +162,195 @@ } } - // transition between pages - based on transitions from jQtouch - function changePage( from, to, transition, back ) { - jQuery( document.activeElement ).blur(); - - - //trigger before show/hide events - from.trigger("pagebeforehide", {nextPage: to}); - to.trigger("pagebeforeshow", {prevPage: from}); - - function loadComplete(){ - pageLoading( true ); - //trigger show/hide events, allow preventing focus change through return false - if( from.trigger("pagehide", {nextPage: to}) !== false && to.trigger("pageshow", {prevPage: from}) !== false ){ - reFocus( to ); - } + //function for setting role of next page + function setPageRole( newPage ) { + if ( nextPageRole ) { + newPage.attr( "data-role", nextPageRole ); + nextPageRole = undefined; } - - if(transition){ - // animate in / out - from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); - to.addClass( activePageClass + " " + transition + - " in " + ( back ? "reverse" : "" ) ); + } + + //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(); - // callback - remove classes, etc - to.animationComplete(function() { - from.add( to ).removeClass(" out in reverse " + transitions ); - from.removeClass( activePageClass ); - loadComplete(); - }); - } - else{ - from.removeClass( activePageClass ); - to.addClass( activePageClass ); - loadComplete(); - } - }; + $.each(copyAttrs,function(i){ + if( newPage.attr( copyAttrs[ i ] ) ){ + wrapper.attr( copyAttrs[ i ], newPage.attr( copyAttrs[ i ] ) ); + newPage.removeAttr( copyAttrs[ i ] ); + } + }); + return wrapper; + } - jQuery(function() { - var preventLoad = false; - $body = jQuery( "body" ); - pageLoading(); + //for getting or creating a new page + function changePage( to, transition, back ){ + //if an error occurred, preventLoad will be true + if ( preventLoad ) { + preventLoad = false; + return; + } - // needs to be bound at domready (for IE6) - // find or load content, make it active - $window.bind( "hashchange", function(e, extras) { - if ( preventLoad ) { - preventLoad = false; - return; - } - - var url = location.hash.replace( /^#/, "" ), - stackLength = urlStack.length, - // pageTransition only exists if the user clicked a link - back = !pageTransition && stackLength > 1 && - urlStack[ stackLength - 2 ].url === url, - transition = (extras && extras.manuallyTriggered) ? false : pageTransition || "slide", - fileUrl = url; - pageTransition = undefined; + //from is always the currently viewed page + var from = $activePage, + url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, + back = forceBack || ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), + transition = (transition !== undefined) ? transition : ( pageTransition || "slide" ); + + //unset pageTransition, forceBack + pageTransition = undefined; + forceBack = undefined; - //reset base to pathname for new request - resetBaseURL(); + //reset base to pathname for new request + resetBaseURL(); - // if the new href is the same as the previous one - if ( back ) { - transition = urlStack.pop().transition; - } else { - urlStack.push({ url: url, transition: transition }); - if ( forceBack ) back = true; - } - forceBack = undefined; + // if the new href is the same as the previous one + if ( back ) { + transition = urlStack.pop().transition; + } else { + urlStack.push({ url: url, transition: transition }); + } + + //function for transitioning between two existing pages + function transitionPages() { + + //kill the keyboard + jQuery( document.activeElement ).blur(); - //function for setting role of next page - function setPageRole( newPage ) { - if ( nextPageRole ) { - newPage.attr( "data-role", nextPageRole ); - nextPageRole = undefined; + //trigger before show/hide events + from.trigger("pagebeforehide", {nextPage: to}); + to.trigger("pagebeforeshow", {prevPage: from}); + + function loadComplete(){ + pageLoading( true ); + //trigger show/hide events, allow preventing focus change through return false + if( from.trigger("pagehide", {nextPage: to}) !== false && to.trigger("pageshow", {prevPage: from}) !== false ){ + $activePage = to; + reFocus( to ); } } - //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 ] ); - } + if(transition){ + // animate in / out + from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); + to.addClass( activePageClass + " " + transition + + " in " + ( back ? "reverse" : "" ) ); + + // callback - remove classes, etc + to.animationComplete(function() { + from.add( to ).removeClass(" out in reverse " + transitions ); + from.removeClass( activePageClass ); + loadComplete(); }); - return wrapper; } + else{ + from.removeClass( activePageClass ); + to.addClass( activePageClass ); + loadComplete(); + } + }; + + // + function enhancePage(){ + setPageRole( to ); + to.page(); + } - if ( url ) { - var active = jQuery( ".ui-page-active" ); - // see if content is present already - var localDiv = jQuery( "[id='" + url + "']" ); - if ( localDiv.length ) { - if ( localDiv.is( "[data-role]" ) ) { - setPageRole( localDiv ); - } + //if url is a string + if( url ){ + to = jQuery( "[id='" + url + "']" ); + } + + // find the "to" page, either locally existing in the dom or by creating it through ajax + if ( to.length ) { + setBaseURL(); + enhancePage(); + transitionPages(); + } else { + + pageLoading(); + + if ( url.match( '&' + jQuery.mobile.subPageUrlKey ) ) { + fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0]; + } + + $.ajax({ + url: fileUrl, + success: function( html ) { setBaseURL(); - localDiv.page(); - changePage( active, localDiv, transition, back ); + var all = jQuery("
"); + //workaround to allow scripts to execute when included in page divs + all.get(0).innerHTML = html; + to = all.find('[data-role="page"]'); - } else { //ajax it in - pageLoading(); - - if ( url.match( '&' + jQuery.mobile.subPageUrlKey ) ) { - fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0]; + //preserve ID on a retrieved page + if ( to.attr('id') ) { + to = wrapNewPage( to ); } - $.ajax({ - url: fileUrl, - success: function( html ) { - var all = jQuery("
"); - //workaround to allow scripts to execute when included in page divs - all.get(0).innerHTML = html; - var page = all.find('[data-role="page"]'); + to + .attr( "id", fileUrl ) + .appendTo( $pageContainer ); + + enhancePage(); + transitionPages(); + }, + error: function() { + pageLoading( true ); - if ( page.attr('id') ) { - page = wrapNewPage( page ); - } + jQuery("

Error Loading Page

") + .css({ "display": "block", "opacity": 0.96 }) + .appendTo( $pageContainer ) + .delay( 800 ) + .fadeOut( 400, function(){ + $(this).remove(); + }); - page - .attr( "id", fileUrl ) - .appendTo( $pageContainer ); + preventLoad = true; + history.back(); + } + }); + } - setPageRole( page ); - page.page(); - changePage( active, page, transition, back ); - }, - error: function() { - pageLoading( true ); + }; - jQuery("

Error Loading Page

") - .css({ "display": "block", "opacity": 0.96 }) - .appendTo( $pageContainer ) - .delay( 800 ) - .fadeOut( 400, function(){ - $(this).remove(); - }); + + jQuery(function() { - preventLoad = true; - history.back(); - } - }); - - setBaseURL(); - } - } else { - // either we've backed up to the root page url - // or it's the first page load with no hash present - var currentPage = jQuery( ".ui-page-active" ); - if ( currentPage.length && !$startPage.is( ".ui-page-active" ) ) { - changePage( currentPage, $startPage, transition, back ); - } else { - $startPage.trigger("pagebeforeshow", {prevPage: $('')}); - $startPage.addClass( activePageClass ); - pageLoading( true ); - - if( $startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ - reFocus($startPage); - } + $body = jQuery( "body" ); + pageLoading(); + + // needs to be bound at domready (for IE6) + // find or load content, make it active + $window.bind( "hashchange", function(e, extras) { + var to = location.hash, + transition = (extras && extras.manuallyTriggered) ? 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( $activePage.length && !$startPage.is( $activePage ) && !(extras && extras.manuallyTriggered) ) { + changePage( $startPage, transition, true ); + } + else{ + $startPage.trigger("pagebeforeshow", {prevPage: $('')}); + $startPage.addClass( activePageClass ); + pageLoading( true ); + + if( $startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ + reFocus($startPage); } } + }); }); @@ -368,7 +393,7 @@ jQuery(function(){ //set up active page - $startPage = jQuery('[data-role="page"]:first'); + $startPage = $activePage = jQuery('[data-role="page"]:first'); //set page container $pageContainer = $startPage.parent(); From cb13c2f84a2492c8e4448836c0225a8f4d9aeaca Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 13:12:25 -0400 Subject: [PATCH 02/13] updated to use new changePage function, which no longer has a "from" arg --- docs/_assets/js/_viewsource.js | 4 ++-- js/jquery.mobile.dialog.js | 8 ++++++++ js/jquery.mobile.forms.select.js | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/_assets/js/_viewsource.js b/docs/_assets/js/_viewsource.js index 38af785cf9f..bb5925e66d3 100644 --- a/docs/_assets/js/_viewsource.js +++ b/docs/_assets/js/_viewsource.js @@ -26,9 +26,9 @@ $.fn.addSourceLink = function(style){ var activePage = $(this).parents('.ui-page-active'); page.find('.ui-content').append(codeblock); - $.changePage(activePage, page, 'slideup',false); + $.changePage(page, 'slideup',false); page.find('.ui-btn-left').click(function(){ - $.changePage(page, activepage, 'slideup',true); + $.changePage(activepage, 'slideup',true); return false; }); }) diff --git a/js/jquery.mobile.dialog.js b/js/jquery.mobile.dialog.js index af7a38f34e6..0fad55834d2 100644 --- a/js/jquery.mobile.dialog.js +++ b/js/jquery.mobile.dialog.js @@ -13,6 +13,14 @@ $.fn.dialog = function(options){ .addClass('ui-page ui-dialog ui-body-a') .find('[data-role=header]') .addClass('ui-corner-top ui-overlay-shadow') + .find('.ui-btn-left').tap(function(){ + $.changePage() + return false; + }) + .click(function(){ + return false; + }) + .end() .end() .find('.ui-content,[data-role=footer]') .last() diff --git a/js/jquery.mobile.forms.select.js b/js/jquery.mobile.forms.select.js index 5d832f4bf2f..4761ec22096 100644 --- a/js/jquery.mobile.forms.select.js +++ b/js/jquery.mobile.forms.select.js @@ -95,7 +95,7 @@ $.fn.customSelect = function(options){ if( menuHeight > window.innerHeight - 80 || !$.support.scrollTop ){ menuType = "page"; menuPageContent.append( list ); - $.changePage(thisPage, menuPage, false, false); + $.changePage(menuPage, false, false); } else { menuType = "overlay"; @@ -119,7 +119,7 @@ $.fn.customSelect = function(options){ function hidemenu(){ if(menuType == "page"){ - $.changePage(menuPage, thisPage, false, true); + $.changePage(thisPage, false, true); } else{ screen.addClass( "ui-screen-hidden" ); From b8cf201a88e129db898a400a8cdc85c0b7111d73 Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 13:16:48 -0400 Subject: [PATCH 03/13] ajaxClick only updates the hash if the clicked element doesn't match the new unHashedSelectors var. So far, this only includes [data-rel=dialog] --- js/jquery.mobile.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 1899844665b..5ff0707be9c 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -58,7 +58,8 @@ } ], focusable = "[tabindex],a,button:visible,select:visible,input", nextPageRole = null, - preventLoad = false; + preventLoad = false, + unHashedSelectors = '[data-rel=dialog]'; // TODO: don't expose (temporary during code reorg) $.mobile.urlStack = urlStack; @@ -129,8 +130,13 @@ location = href } else{ - // let the hashchange event handler take care of requesting the page via ajax - location.hash = href; + changePage(href, pageTransition); + + if( !$(this).is(unHashedSelectors) ){ + // let the hashchange event handler take care of requesting the page via ajax + location.hash = href; + } + } return this; }; From 06e6ceb519aabf0944c1f584d284a118c2f76cd8 Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 14:04:47 -0400 Subject: [PATCH 04/13] moved $activePage to $.activePage, so it's exposed for external plugins --- js/jquery.mobile.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 5ff0707be9c..eba1fa164cc 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -46,7 +46,6 @@ $pageContainer, startPageId = 'ui-page-start', activePageClass = 'ui-page-active', - $activePage, pageTransition, forceBack, transitions = 'slide slideup slidedown pop flip fade', @@ -200,7 +199,7 @@ } //from is always the currently viewed page - var from = $activePage, + var from = $.activePage, url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, back = forceBack || ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), transition = (transition !== undefined) ? transition : ( pageTransition || "slide" ); @@ -233,7 +232,7 @@ pageLoading( true ); //trigger show/hide events, allow preventing focus change through return false if( from.trigger("pagehide", {nextPage: to}) !== false && to.trigger("pageshow", {prevPage: from}) !== false ){ - $activePage = to; + $.activePage = to; reFocus( to ); } } @@ -344,7 +343,7 @@ } //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( $activePage.length && !$startPage.is( $activePage ) && !(extras && extras.manuallyTriggered) ) { + else if( $.activePage.length && !$startPage.is( $.activePage ) && !(extras && extras.manuallyTriggered) ) { changePage( $startPage, transition, true ); } else{ @@ -399,7 +398,7 @@ jQuery(function(){ //set up active page - $startPage = $activePage = jQuery('[data-role="page"]:first'); + $startPage = $.activePage = jQuery('[data-role="page"]:first'); //set page container $pageContainer = $startPage.parent(); From 6f90c3aa176864a2b32daa250d3d393a38ec05aa Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 15:59:44 -0400 Subject: [PATCH 05/13] changed to use click event. --- js/jquery.mobile.page.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/js/jquery.mobile.page.js b/js/jquery.mobile.page.js index 15ef708fd7e..cfe335d80e2 100644 --- a/js/jquery.mobile.page.js +++ b/js/jquery.mobile.page.js @@ -52,11 +52,8 @@ $.widget( "mobile.page", $.mobile.widget, { //auto-add back btn on pages beyond first view if ( $.mobile.addBackBtn && role === "header" && ($.mobile.urlStack.length > 1 || $('.ui-page').length > 1) && !leftbtn && !$this.data( "noBackBtn" ) ) { $( "Back" ) - .tap(function() { - history.back(); - return false; - }) .click(function() { + history.back(); return false; }) .prependTo( $this ); From 5ad3d040848dac91ab84486ea56b87ebe77bc5ce Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 16:28:17 -0400 Subject: [PATCH 06/13] several changes that allow for changing pages without hash changes: - allowed prevention of hash listening during a particular hash change - allowed $.activePage to remain set on previous page when an untracked page is open (return pageshow event false to use this feature) - moved setting and resetting to base urls and made sure they update at the right moment for both new pages and those previously visited. - changePage now accepts an array as first argument, allowing you to specify both [from,to] pages, whereas non-array types would refer to the "to" page, assuming "from" should be the currently viewed page - error requests never generate a hash change now - edited the ajax error to appear in the right location when scrolled Essentially, plugins can now create page changes that are not tracked in history, which is a dialog critical bug. --- js/jquery.mobile.js | 83 ++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index eba1fa164cc..0cf73e963e9 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -57,7 +57,7 @@ } ], focusable = "[tabindex],a,button:visible,select:visible,input", nextPageRole = null, - preventLoad = false, + hashListener = true, unHashedSelectors = '[data-rel=dialog]'; // TODO: don't expose (temporary during code reorg) @@ -94,9 +94,9 @@ return newBaseURL; } - function setBaseURL(){ + function setBaseURL( nonHashPath ){ //set base url for new page assets - $('#ui-base').attr('href', getBaseURL()); + $('#ui-base').attr('href', getBaseURL( nonHashPath )); } function resetBaseURL(){ @@ -128,12 +128,12 @@ if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) { location = href } - else{ - changePage(href, pageTransition); - - if( !$(this).is(unHashedSelectors) ){ - // let the hashchange event handler take care of requesting the page via ajax - location.hash = href; + else{ + if( $(this).is(unHashedSelectors) ){ + changePage(href, pageTransition, undefined); + } + else{ + changePage(href, pageTransition, undefined, true); } } @@ -191,17 +191,14 @@ //for getting or creating a new page - function changePage( to, transition, back ){ - //if an error occurred, preventLoad will be true - if ( preventLoad ) { - preventLoad = false; - return; - } - + function changePage( to, transition, back, changeHash){ + //from is always the currently viewed page - var from = $.activePage, + var toIsArray = $.type(to) === "array", + from = toIsArray ? to[0] : $.activePage, + to = toIsArray ? to[1] : to, url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, - back = forceBack || ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), + back = (back !== undefined) ? back : (forceBack || ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url )), transition = (transition !== undefined) ? transition : ( pageTransition || "slide" ); //unset pageTransition, forceBack @@ -225,15 +222,22 @@ jQuery( document.activeElement ).blur(); //trigger before show/hide events - from.trigger("pagebeforehide", {nextPage: to}); - to.trigger("pagebeforeshow", {prevPage: from}); + 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.trigger("pagehide", {nextPage: to}) !== false && to.trigger("pageshow", {prevPage: from}) !== false ){ + if( from.data("page")._trigger("hide", null, {nextPage: to}) !== false && to.data("page")._trigger("show", null, {prevPage: from}) !== false ){ $.activePage = to; - reFocus( to ); + } + reFocus( to ); + if( changeHash && url ){ + hashListener = false; + location.hash = url; + setTimeout(function(){ + hashListener = true; + }, 500); } } @@ -257,35 +261,46 @@ } }; - // + //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( '&' + jQuery.mobile.subPageUrlKey ) ? url.split( '&' + jQuery.mobile.subPageUrlKey )[0] : url; + } //if url is a string if( url ){ - to = jQuery( "[id='" + url + "']" ); + to = jQuery( "[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 ) { - setBaseURL(); + if( fileUrl ){ + setBaseURL(fileUrl); + } enhancePage(); transitionPages(); } else { pageLoading(); - if ( url.match( '&' + jQuery.mobile.subPageUrlKey ) ) { - fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0]; - } - $.ajax({ url: fileUrl, success: function( html ) { - setBaseURL(); + setBaseURL(fileUrl); var all = jQuery("
"); //workaround to allow scripts to execute when included in page divs all.get(0).innerHTML = html; @@ -307,15 +322,12 @@ pageLoading( true ); jQuery("

Error Loading Page

") - .css({ "display": "block", "opacity": 0.96 }) + .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 }) .appendTo( $pageContainer ) .delay( 800 ) .fadeOut( 400, function(){ $(this).remove(); }); - - preventLoad = true; - history.back(); } }); } @@ -331,6 +343,7 @@ // needs to be bound at domready (for IE6) // find or load content, make it active $window.bind( "hashchange", function(e, extras) { + if( !hashListener ){ return; } var to = location.hash, transition = (extras && extras.manuallyTriggered) ? false : undefined; From ef3fd116dca8be147bac6057ff84e21895c25380 Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 16:29:05 -0400 Subject: [PATCH 07/13] refactored to use widget factory and not be tracked via hash/history. Fixes #176 --- js/jquery.mobile.dialog.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/js/jquery.mobile.dialog.js b/js/jquery.mobile.dialog.js index 0fad55834d2..2e0c266f244 100644 --- a/js/jquery.mobile.dialog.js +++ b/js/jquery.mobile.dialog.js @@ -4,27 +4,32 @@ * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. * Note: Code is in draft form and is subject to change */ -(function($){ -$.fn.dialog = function(options){ - return $(this).each(function(){ - $(this) +(function ( $ ) { +$.widget( "mobile.dialog", $.mobile.widget, { + options: {}, + _create: function(){ + var $el = this.element, + $closeBtn = $('Close') + .click(function(){ + $.changePage([$el, $.activePage], undefined, true ); + return false; + }); + + this.element + .bind("pageshow",function(){ + return false; + }) //add ARIA role .attr("role","dialog") .addClass('ui-page ui-dialog ui-body-a') .find('[data-role=header]') .addClass('ui-corner-top ui-overlay-shadow') - .find('.ui-btn-left').tap(function(){ - $.changePage() - return false; - }) - .click(function(){ - return false; - }) - .end() + .prepend( $closeBtn ) .end() .find('.ui-content,[data-role=footer]') .last() .addClass('ui-corner-bottom ui-overlay-shadow'); - }); -}; -})(jQuery); \ No newline at end of file + + } +}); +})( jQuery ); \ No newline at end of file From 21a7b0a2baca47d79093e13a53601f7243b4cbe3 Mon Sep 17 00:00:00 2001 From: scottjehl Date: Thu, 21 Oct 2010 17:02:59 -0400 Subject: [PATCH 08/13] updated to reflect new changes in pageChange function and dialog behavior --- js/jquery.mobile.forms.select.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/js/jquery.mobile.forms.select.js b/js/jquery.mobile.forms.select.js index 4761ec22096..10249d061a4 100644 --- a/js/jquery.mobile.forms.select.js +++ b/js/jquery.mobile.forms.select.js @@ -16,7 +16,6 @@ $.fn.customSelect = function(options){ //extendable options o = $.extend({ - closeText: "close", chooseText: label.text(), theme: select.data("theme") }, options), @@ -43,7 +42,6 @@ $.fn.customSelect = function(options){ }), menuPage = $( "
" + "
" + - ""+ o.closeText +""+ "
" + o.chooseText + "
"+ "
"+ "
"+ @@ -95,7 +93,7 @@ $.fn.customSelect = function(options){ if( menuHeight > window.innerHeight - 80 || !$.support.scrollTop ){ menuType = "page"; menuPageContent.append( list ); - $.changePage(menuPage, false, false); + $.changePage(menuPage, undefined); } else { menuType = "overlay"; @@ -119,7 +117,7 @@ $.fn.customSelect = function(options){ function hidemenu(){ if(menuType == "page"){ - $.changePage(thisPage, false, true); + $.changePage([menuPage,thisPage], undefined, true); } else{ screen.addClass( "ui-screen-hidden" ); @@ -189,12 +187,6 @@ $.fn.customSelect = function(options){ hidemenu(); return false; }); - - //menu page back button - menuPage.find( ".ui-btn-left" ).click(function(){ - hidemenu(); - return false; - }); //hide on outside click screen.click(function(){ From f91b2523ff176044caa531cb7a323e6bd728a5a0 Mon Sep 17 00:00:00 2001 From: John Resig Date: Thu, 21 Oct 2010 17:18:59 -0400 Subject: [PATCH 09/13] Merge clickable into buttonMarkup. Improve performance of the Button Markup plugin. --- Makefile | 1 - js/jquery.mobile.buttonMarkup.js | 107 +++++++++++++++++-------------- js/jquery.mobile.clickable.js | 28 -------- js/jquery.mobile.listview.js | 9 ++- js/manifest.php | 1 - 5 files changed, 63 insertions(+), 83 deletions(-) delete mode 100644 js/jquery.mobile.clickable.js diff --git a/Makefile b/Makefile index db9bb0674ae..a693ac13426 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ FILES = js/jquery.ui.widget.js \ js/jquery.mobile.event.js \ js/jquery.mobile.hashchange.js \ js/jquery.mobile.page.js \ - js/jquery.mobile.clickable.js \ js/jquery.mobile.fixHeaderFooter.js \ js/jquery.mobile.forms.checkboxradio.js \ js/jquery.mobile.forms.textinput.js \ diff --git a/js/jquery.mobile.buttonMarkup.js b/js/jquery.mobile.buttonMarkup.js index 69fffcc08a9..98976f6e7fe 100644 --- a/js/jquery.mobile.buttonMarkup.js +++ b/js/jquery.mobile.buttonMarkup.js @@ -4,91 +4,102 @@ * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. * Note: Code is in draft form and is subject to change */ -(function($){ +(function( jQuery ) { -$.fn.buttonMarkup = function( options ){ +jQuery.fn.buttonMarkup = function( options ){ return this.each( function() { - var el = $( this ), - o = $.extend( {}, { - theme: (function(){ - //if data-theme attr is present - if(el.is('[data-theme]')){ - return el.attr('data-theme'); - } - //if not, try to find closest theme container - else if( el.parents('body').length ) { - var themedParent = el.closest('[class*=ui-bar-],[class*=ui-body-]'); - return themedParent.length ? themedParent.attr('class').match(/ui-(bar|body)-([a-z])/)[2] : 'c'; - } - else{ - return 'c'; - } - })(), - iconpos: el.data('iconpos'), - icon: el.data('icon'), - inline: el.data('inline') - }, $.fn.buttonMarkup.defaults, options), + var el = jQuery( this ), + o = jQuery.extend( {}, jQuery.fn.buttonMarkup.defaults, el.data(), options), // Classes Defined buttonClass = "ui-btn ui-btn-up-" + o.theme, innerClass = "ui-btn-inner", iconClass; + + if ( attachEvents ) { + attachEvents(); + } + + // if not, try to find closest theme container + if ( !o.theme ) { + var themedParent = el.closest("[class*='ui-bar-'],[class*='ui-body-']"); + return themedParent.length ? + /ui-(bar|body)-([a-z])/.exec( themedParent.attr("class") )[2] : + "c"; + } - if( o.inline ){ + if ( o.inline ) { buttonClass += " ui-btn-inline"; } - if (o.icon) { - o.icon = 'ui-icon-' + o.icon; + if ( o.icon ) { + o.icon = "ui-icon-" + o.icon; + o.iconpos = o.iconpos || "left"; iconClass = "ui-icon " + o.icon; - if (o.shadow) { iconClass += " ui-icon-shadow" } - o.iconpos = o.iconpos || 'left'; + if ( o.shadow ) { + iconClass += " ui-icon-shadow" + } } - if (o.iconpos){ + if ( o.iconpos ) { buttonClass += " ui-btn-icon-" + o.iconpos; - if( o.iconpos == 'notext' && !el.attr('title') ){ - el.attr('title', el.text() ); + if ( o.iconpos == "notext" && !el.attr("title") ) { + el.attr( "title", el.text() ); } - } - - - - if (o.corners) { + if ( o.corners ) { buttonClass += " ui-btn-corner-all"; innerClass += " ui-btn-corner-all"; } - if (o.shadow) { + if ( o.shadow ) { buttonClass += " ui-shadow"; } el - .attr( 'data-theme', o.theme ) - .addClass( buttonClass ) - .wrapInner( $( '<' + o.wrapperEls + '>', { className: "ui-btn-text" } ) ); - - if (o.icon) { - el.prepend( $( '', { className: iconClass } ) ); - } + .attr( "data-theme", o.theme ) + .addClass( buttonClass ); - el.wrapInner( $('<' + o.wrapperEls + '>', { className: innerClass }) ); + var wrap = ("" + + ( o.icon ? "" : "" ) + + "").replace(/D/g, o.wrapperEls); - el.clickable(); + el.wrapInner( wrap ); }); }; -$.fn.buttonMarkup.defaults = { +jQuery.fn.buttonMarkup.defaults = { corners: true, shadow: true, iconshadow: true, - wrapperEls: 'span' + wrapperEls: "span" }; -})(jQuery); +var attachEvents = function() { + jQuery(".ui-btn").live({ + mousedown: function() { + var theme = jQuery(this).attr( "data-theme" ); + jQuery(this).removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme ); + }, + mouseup: function() { + var theme = jQuery(this).attr( "data-theme" ); + jQuery(this).removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme ); + }, + "mouseover focus": function() { + var theme = jQuery(this).attr( "data-theme" ); + jQuery(this).removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme ); + }, + "mouseout blur": function() { + var theme = jQuery(this).attr( "data-theme" ); + jQuery(this).removeClass( "ui-btn-hover-" + theme ).addClass( "ui-btn-up-" + theme ); + } + }); + + attachEvents = null; +} +})(jQuery); diff --git a/js/jquery.mobile.clickable.js b/js/jquery.mobile.clickable.js deleted file mode 100644 index e66a36edf9a..00000000000 --- a/js/jquery.mobile.clickable.js +++ /dev/null @@ -1,28 +0,0 @@ -/* -* jQuery Mobile Framework : sample scripting for manipulating themed interaction states -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. -* Note: Code is in draft form and is subject to change -*/ -(function($){ -$.fn.clickable = function(){ - return $(this).each(function(){ - var theme = $(this).attr('data-theme'); - $(this) - .mousedown(function(){ - $(this).removeClass('ui-btn-up-'+theme).addClass('ui-btn-down-'+theme); - }) - .mouseup(function(){ - $(this).removeClass('ui-btn-down-'+theme).addClass('ui-btn-up-'+theme); - }) - .bind('mouseover focus',function(){ - $(this).removeClass('ui-btn-up-'+theme).addClass('ui-btn-hover-'+theme); - }) - .bind('mouseout blur',function(){ - $(this).removeClass('ui-btn-hover-'+theme).addClass('ui-btn-up-'+theme); - }); - }); -}; -})(jQuery); - - diff --git a/js/jquery.mobile.listview.js b/js/jquery.mobile.listview.js index 58f0b5e648e..a6d26a4e956 100644 --- a/js/jquery.mobile.listview.js +++ b/js/jquery.mobile.listview.js @@ -27,7 +27,7 @@ $.widget( "mobile.listview", $.mobile.widget, { this.element.addClass( "ui-listview-inset ui-corner-all ui-shadow" ); } - this.element.delegate("ui-li", "focusin", function() { + this.element.delegate(".ui-li", "focusin", function() { jQuery(this).attr( "tabindex", "0" ); }); @@ -126,10 +126,7 @@ $.widget( "mobile.listview", $.mobile.widget, { $list.find( ".ui-li-dec" ).remove(); } - li - .addClass( "ui-li" ) - .attr( "role", "option" ) - .attr( "tabindex", "-1" ) + li.attr({ "role": "option", "tabindex": "-1" }); li.first().attr( "tabindex", "0" ); @@ -141,6 +138,8 @@ $.widget( "mobile.listview", $.mobile.widget, { return; } + item.addClass( "ui-li" ); + var a = item.find( "a" ); if ( a.length ) { diff --git a/js/manifest.php b/js/manifest.php index a8440c98907..a5f8a182132 100644 --- a/js/manifest.php +++ b/js/manifest.php @@ -7,7 +7,6 @@ 'jquery.mobile.event.js', 'jquery.mobile.hashchange.js', 'jquery.mobile.page.js', - 'jquery.mobile.clickable.js', 'jquery.mobile.fixHeaderFooter.js', 'jquery.mobile.forms.checkboxradio.js', 'jquery.mobile.forms.textinput.js', From c7003aac9593b83eaa5aa15412d13ff4e630b98b Mon Sep 17 00:00:00 2001 From: Jeffrey Way Date: Thu, 21 Oct 2010 18:06:08 -0400 Subject: [PATCH 10/13] Querystrings containing "http" in the middle will incorrectly load synchronously --- js/jquery.mobile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 6b9ad04ce48..4592236a12e 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -115,17 +115,17 @@ var newBaseURL = getBaseURL(); //if href is absolute but local, or a local ID, no base needed - if( /^\//.test(href) || (/https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){ + if( /^\//.test(href) || (/^https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){ newBaseURL = ''; } // set href to relative path using baseURL and - if( !/https?:\/\//.test(href) ){ + if( !/^https?:\/\//.test(href) ){ href = newBaseURL + href; } //if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax - if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) { + if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /^https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) { location = href } else{ From 562126cae2b2cf7c9e3e5e6f213a04a35a0b6122 Mon Sep 17 00:00:00 2001 From: Jeffrey Way Date: Sat, 23 Oct 2010 02:15:23 -0400 Subject: [PATCH 11/13] Rewrote ajaxClick method to clean-up and use less regexes --- js/jquery.mobile.js | 56 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 4592236a12e..447a6c3adf2 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -106,38 +106,36 @@ // send a link through hash tracking jQuery.fn.ajaxClick = function() { - var href = jQuery( this ).attr( "href" ); - pageTransition = jQuery( this ).data( "transition" ) || "slide"; - forceBack = jQuery( this ).data( "back" ) || undefined; - nextPageRole = jQuery( this ).attr( "data-rel" ); - - //find new base for url building - var newBaseURL = getBaseURL(); - - //if href is absolute but local, or a local ID, no base needed - if( /^\//.test(href) || (/^https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){ - newBaseURL = ''; - } - - // set href to relative path using baseURL and - if( !/^https?:\/\//.test(href) ){ - href = newBaseURL + href; - } - - //if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax - if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /^https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) { - location = href - } - else{ - if( $(this).is(unHashedSelectors) ){ + var $this = jQuery( this ), + href = $this.attr( "href" ), + // If href begins with a slash or hash, or it doesn't contain http, or the hostname is within the href, must be local. + isLocal = ( /^(\/|#)/.test(href) ) || ( href.indexOf('http') === -1 ) || ( href.indexOf(location.hostname) > -1 ), + //find new base for url building + newBaseURL = getBaseURL(); + + pageTransition = $this.data( "transition" ) || "slide"; + forceBack = $this.data( "back" ) || undefined; + nextPageRole = $this.attr( "data-rel" ); + + //if href is absolute but local, no base needed + if ( isLocal && href.indexOf('http') > -1 ) { + newBaseURL = ''; + } else { + // set href to relative path using baseURL + href = newBaseURL + href; + } + + //if it's a non-local-anchor and Ajax is not supported, go to page without ajax + if ( !isLocal || !jQuery.support.ajax ) { + location = href; + } else { + if( $this.is(unHashedSelectors) ) { changePage(href, pageTransition, undefined); - } - else{ + } else { changePage(href, pageTransition, undefined, true); } - - } - return this; + } + return this; }; // ajaxify all navigable links From 7d0a069ed08375abfdd8afff959ef6cb5b650eb5 Mon Sep 17 00:00:00 2001 From: Jeffrey Way Date: Fri, 22 Oct 2010 23:47:36 -0700 Subject: [PATCH 12/13] Added fix for hrefs which direct to parent directories, with ../ --- js/jquery.mobile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index 447a6c3adf2..e67bf1bd7ea 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -108,8 +108,8 @@ jQuery.fn.ajaxClick = function() { var $this = jQuery( this ), href = $this.attr( "href" ), - // If href begins with a slash or hash, or it doesn't contain http, or the hostname is within the href, must be local. - isLocal = ( /^(\/|#)/.test(href) ) || ( href.indexOf('http') === -1 ) || ( href.indexOf(location.hostname) > -1 ), + // If href doesn't contain "http", or it begins with a slash, hash, or period, or the hostname is within the href... must be local. + isLocal = ( href.indexOf('http') === -1 ) || ( /^(\/|\.|#)/.test(href) ) || ( href.indexOf(location.hostname) > -1 ), //find new base for url building newBaseURL = getBaseURL(); From 7b613ca85e2a005931faaf75c8193d4cf51dc6c7 Mon Sep 17 00:00:00 2001 From: Jeffrey Way Date: Sat, 23 Oct 2010 12:38:51 -0700 Subject: [PATCH 13/13] Updated `isLocal` variable to be more precise. --- js/jquery.mobile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/jquery.mobile.js b/js/jquery.mobile.js index e67bf1bd7ea..a324f57f35d 100644 --- a/js/jquery.mobile.js +++ b/js/jquery.mobile.js @@ -109,7 +109,7 @@ var $this = jQuery( this ), href = $this.attr( "href" ), // If href doesn't contain "http", or it begins with a slash, hash, or period, or the hostname is within the href... must be local. - isLocal = ( href.indexOf('http') === -1 ) || ( /^(\/|\.|#)/.test(href) ) || ( href.indexOf(location.hostname) > -1 ), + isLocal = ( !/^(http)/.test(href) ) || ( href.indexOf(location.hostname) > -1 ), //find new base for url building newBaseURL = getBaseURL();