Skip to content
Browse files

Updated the stable version with pageload and pagebeforechange events.

The documentation was also updated with a 'common mistakes' paragraph
  • Loading branch information...
1 parent 5905d14 commit edf70f04b073c5fdb8d5b20d977ce4940794ffb6 @azicchetti committed May 28, 2012
View
37 README.md
@@ -28,6 +28,7 @@ In addition, if you want to use standard hashchange-based routers, you have to d
What's new in the latest versions
=====================
+* support for pagebeforechange, pagebeforeload, pageload
* Added a parameter in the configuration object to execute only the first route handler found
* Support for a different syntax defining your routes
* Added a nice getParams() function to actually 'parse' parameters in the hash and get
@@ -302,6 +303,42 @@ Your handlers will be called with the following arguments:
removed from the dom during the pageremove event.
+Common mistakes
+--------------
+jQuery Mobile is a wonderful framework and it seems really easy to use at first.
+While this may be true for server-side generated mobile pages, things are very different
+once you have to do a full client-side web application, using backbone.js or spine.js and
+possibly Phonegap/Cordova.
+
+The whole hash handling performed by jQuery Mobile may be confusing, dynamic page loading/injection
+must be understood in order to be successfully exploited, and you should also know the jQM
+event system to have a fine grained control over page switching.
+
+Here is a list of things you should check when you're in trouble, based on my experience but,
+more importantly, on the email I've received from other users of the jQM router.
+
+* the router should be instantiated as soon as possible, possibly just after loading jquery mobile.
+This ensures that even the first pageinit event can be catched and handled by the router
+
+* please make sure that the router is not instantiated multiple times by mistake. This will lead to
+routes being fired twice, at least
+
+* do not call $.mobile.changePage during a page transition with the destination page being the one
+the framework is already transitioning to.
+That is to say, if you click on a link to #foo and you have a pagebeforeshow route bound to it,
+DO NOT invoke $.mobile.changePage("#foo") in your handler (the result will be an epic failure due to
+a bug in jquery mobile)
+
+* DOUBLE CHECK your REGULAR EXPRESSIONS! A typical mistake is forgetting the $ operator.
+If you have two pages, such as #product and #productList, a hypothetical route "#product" would
+match both pages, leading to unexpected behaviors. Use the $ operator when unsure: "#product$"
+
+* use setTimeout to avoid doing time-consuming tasks in a page-transition handler. If you have
+a route bound to pagebeforeshow (or even other events) and your code takes too much to execute
+(for instance, very long foreach loops, synchronous ajax calls, complex manupulations
+of markers in a google map), jquery mobile may throw an error.
+
+
Public methods
--------------
View
4 examples/__subTestAjax.html
@@ -2,9 +2,9 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
<body>
View
4 examples/__subTestAjax3.html
@@ -2,9 +2,9 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
<body>
View
6 examples/test-bC.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
- <script type="text/javascript" src="../js/jquery.mobile.router-dev.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router([
{ "#localpage2(?:[?/](.*))?": {handler: "localpage", events: "bc,c,i,bs,s,bh,h"} },
View
6 examples/test-bl.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
- <script type="text/javascript" src="../js/jquery.mobile.router-dev.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router([
{ "#index": {
View
4 examples/test.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router({
"#localpage2(?:[?/](.*))?": {handler: "localpage", events: "bc,c,i,bs,s,bh,h"},
View
4 examples/testAjax.html
@@ -2,7 +2,7 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript">
$(document).bind("mobileinit",function(){
@@ -12,7 +12,7 @@
});
</script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router({
"/testAjax.html(?:[?](.*))?": {handler: "ajaxPage", events: "bc,c,bs,s,h"},
View
4 examples/testArraySyntax.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router([
{ "#localpage2(?:[?/](.*))?": {handler: "localpageA", events: "bc,c,i"} },
View
58 examples/testBackButton.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
+ <script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
+<script>
+var actions = {
+ showSite: function(eventType, matchObj, ui, page, evt) {
+ console.log('showSite');
+ console.log(matchObj);
+ },
+ showSection: function(eventType, matchObj, ui, page, evt) {
+ console.log('showSection');
+ console.log(matchObj);
+ },
+ showChunk: function(eventType, matchObj, ui, page, evt) {
+ console.log('showChunk');
+ console.log(matchObj);
+ }
+};
+
+
+new $.mobile.Router(
+{
+ "#site": { handler: "showSite", events: "bs" },
+ "#section([?].*)?": { handler: "showSection", events: "bs" },
+ "#chunk([?].*)?": { handler: "showChunk", events: "bs" },
+},
+actions);
+</script>
+</head>
+
+<body>
+<div id="site" data-role="page" data-add-back-btn="true">
+ <div data-role="header"><h1>Site</h1></div>
+ <div data-role="content">
+ <ul data-role="listview">
+ <li><a href="#section?id=1">Section 1</a></li>
+ </ul>
+ </div>
+</div>
+
+<div id="section" data-role="page" data-add-back-btn="true">
+ <div data-role="header"><h1>Section</h1></div>
+ <div data-role="content">
+ <ul data-role="listview">
+ <li><a href="#chunk?id=1">Chunk 1</a></li>
+ </ul>
+ </div>
+</div>
+
+<div id="chunk" data-role="page" data-add-back-btn="true">
+ <div data-role="header"><h1>Chunk</h1></div>
+ <div data-role="content"></div>
+</div>
+</body>
+</html>
View
4 examples/testChangePage.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router({
"#localpage2(?:[?/](.*))?": {
View
4 examples/testChangePageAjax.html
@@ -2,7 +2,7 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript">
$(document).bind("mobileinit",function(){
@@ -13,7 +13,7 @@
});
</script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router({
"/testChangePageAjax.html(?:[?](.*))?": {handler: "ajaxPage", events: "bc,c,bs,s,h"},
View
4 examples/testSamePage.html
@@ -2,10 +2,10 @@
<html>
<head>
<title>Page Title</title>
- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="../js/jquery.mobile.router.js"></script>
- <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
<script type="text/javascript">
var router=new $.mobile.Router({
"#localpage2(?:[?/](.*))?": "localpage2",
View
612 js/jquery.mobile.router.js
@@ -1,294 +1,318 @@
-/*!
- * jQueryMobile-router v0.81
- * http://github.com/azicchetti/jquerymobile-router
- *
- * Copyright 2011 (c) Andrea Zicchetti
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://github.com/azicchetti/jquerymobile-router/blob/master/MIT-LICENSE.txt
- * http://github.com/azicchetti/jquerymobile-router/blob/master/GPL-LICENSE.txt
- */
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- define(['jquery'], factory);
- } else {
- factory(jQuery);
- }
-}(this, function ($) {
- $(document).bind("mobileinit",function(){
-
- /* supports the following configurations:
- $.mobile.jqmRouter.fixFirstPageDataUrl=true
- $.mobile.jqmRouter.firstPageDataUrl="index.html"
- jQM doesn't handle correctly the dataurl of the first page you display
- in a single-page template mode. In fact, the page is fetched again
- from the server the first time you try to access it through a link.
- If this option is set to true, jquery mobile router extensions will
- try to fix this problem. In order to set the data url, you have to
- provide the name of the file containing the first page into the
- "firstPageDataUrl" property (for example: index.html)
-
- */
-
- var config=$.extend({
- fixFirstPageDataUrl: false, firstPageDataUrl: "index.html",
- ajaxApp: false, firstMatchOnly: false
- },$.mobile.jqmRouter || {});
-
-
- var DEBUG=true;
- function debug(err){
- if (DEBUG) console.log(err);
- }
-
- var previousUrl=null, nextUrl=null;
-
- $(document).bind("pagebeforechange", function( e, data ) {
- var toPage=( typeof data.toPage === "string" ) ? data.toPage : data.toPage.jqmData("url")||"";
-
- if ( data.options.hasOwnProperty("_jqmrouter_handled") ){ return; }
- data.options._jqmrouter_handled = true;
-
- var u = $.mobile.path.parseUrl( toPage );
- previousUrl=nextUrl;
- nextUrl=u;
-
- if ( u.hash.indexOf("?") !== -1 ) {
- var page=u.hash.replace( /\?.*$/, "" );
- // We don't want the data-url of the page we just modified
- // to be the url that shows up in the browser's location field,
- // so set the dataUrl option to the URL with hash parameters
- data.options.dataUrl = u.href;
- // Now call changePage() and tell it to switch to
- // the page we just modified, but only in case it's different
- // from the current page
- if ( $.mobile.activePage &&
- page.replace(/^#/,"")==$.mobile.activePage.jqmData("url")
- ){
- data.options.allowSamePageTransition=true;
- $.mobile.changePage( $(page), data.options );
- } else {
- $.mobile.changePage( $(page), data.options );
- }
- // Make sure to tell changePage() we've handled this call so it doesn't
- // have to do anything.
- e.preventDefault();
- $.mobile.urlHistory.ignoreNextHashChange=true;
- }
- });
-
-
- if (config.fixFirstPageDataUrl){
- $(document).ready(function(){
- if (!window.location.pathname.match("/$")){
- return;
- }
- var page=$(":jqmData(role='page')").first();
- var dataUrl=page.jqmData("url"),
- guessedDataUrl=window.location.pathname
- +config.firstPageDataUrl
- +window.location.search
- +window.location.hash
- ;
-
- if (dataUrl!=guessedDataUrl){
- page.attr("data-url",guessedDataUrl)
- .jqmData("url",guessedDataUrl);
- }
- });
- }
-
- $.mobile.Router=function(userRoutes,userHandlers,conf){
- /* userRoutes format:
- {
- "regexp": "function name", // defaults to jqm pagebeforeshow event
- "regexp": function(){ ... }, // defaults to jqm pagebeforeshow event
- "regexp": { handler: "function name", events: "bc,c,bs,s,bh,h" },
- "regexp": { handler: function(){ ... }, events: "bc,c,bs,s,bh,h" }
- }
- */
- this.routes={
- pagebeforecreate: null, pagecreate: null,
- pagebeforeshow: null, pageshow: null,
- pagebeforehide: null, pagehide: null,
- pageinit: null, pageremove: null
- };
- this.evtLookup = {
- bc: "pagebeforecreate", c: "pagecreate",
- bs: "pagebeforeshow", s: "pageshow",
- bh: "pagebeforehide", h: "pagehide",
- i: "pageinit", rm: "pageremove"
- };
- this.routesRex={};
- this.conf=$.extend({}, config, conf || {});
- this.defaultHandlerEvents = {};
- if (this.conf.defaultHandlerEvents) {
- var evts = this.conf.defaultHandlerEvents.split(",");
- for (var i = 0; i < evts.length; i++) {
- this.defaultHandlerEvents[this.evtLookup[evts[i]]] = evts[i];
- }
- }
- this.add(userRoutes,userHandlers);
- }
- $.extend($.mobile.Router.prototype,{
- add: function(userRoutes,userHandlers){
- if (!userRoutes) return;
-
- var _self=this, evtList=[];
- if (userRoutes instanceof Array){
- $.each(userRoutes,$.proxy(function(k,v){
- this.add(v,userHandlers);
- },this));
- } else {
- $.each(userRoutes,function(r,el){
- if(typeof(el)=="string" || typeof(el)=="function"){
- if (_self.routes.pagebeforeshow===null){
- _self.routes.pagebeforeshow={};
- }
- _self.routes.pagebeforeshow[r]=el;
- if (! _self.routesRex.hasOwnProperty(r)){
- _self.routesRex[r]=new RegExp(r);
- }
- } else {
- var i,trig=el.events.split(","),evt;
- for(i=0;i<trig.length;i++){
- evt=_self.evtLookup[trig[i]];
- if (_self.routes.hasOwnProperty(evt)){
- if (_self.routes[evt]===null){
- _self.routes[evt]={};
- }
- _self.routes[evt][r]=el.handler;
- if (! _self.routesRex.hasOwnProperty(r)){
- _self.routesRex[r]=new RegExp(r);
- }
- } else {
- debug("can't set unsupported route "+trig[i]);
- }
- }
- }
- });
- $.each(_self.routes,function(evt,el){
- if (el!==null){
- evtList.push(evt);
- }
- });
- if (!this.userHandlers){
- this.userHandlers=userHandlers||{};
- } else {
- $.extend(this.userHandlers,userHandlers||{});
- }
- this._detachEvents();
- if (evtList.length>0){
- this._eventData={
- events: evtList.join(" "),
- selectors: ":jqmData(role='page'),:jqmData(role='dialog')",
- handler: function(e,ui){ _self._processRoutes(e,ui,this); }
- };
- $(document).delegate(
- this._eventData.selectors,
- this._eventData.events, this._eventData.handler
- );
- }
- }
- },
-
- _processRoutes: function(e,ui,page){
- var _self=this, refUrl, url, $page, retry=0;
- if (e.type in {
- "pagebeforehide":true, "pagehide":true, "pageremove": true
- }){
- refUrl=previousUrl;
- } else {
- refUrl=nextUrl;
- }
- do {
- if (!refUrl){
- if (page){
- $page=$(page);
- refUrl=$page.jqmData("url");
- if (refUrl){
- if ($page.attr("id")==refUrl) refUrl="#"+refUrl;
- refUrl=$.mobile.path.parseUrl(refUrl);
- }
- }
- } else if (page && !$(page).jqmData("url")){
- return;
- }
- if (!refUrl) return;
- url=( !this.conf.ajaxApp ?
- refUrl.hash
- :refUrl.pathname + refUrl.search + refUrl.hash
- );
- if (url.length==0){
- // if ajaxApp is false, url may be "" when the user clicks the back button
- // and returns to the first page of the application (which is usually
- // loaded without the hash part of the url). Let's handle this...
- refUrl="";
- }
- retry++;
- } while(url.length==0 && retry<=1);
-
- var bHandled = false;
- $.each(this.routes[e.type],function(route,handler){
- var res, handleFn;
- if ( (res=url.match(_self.routesRex[route])) ){
- if (typeof(handler)=="function"){
- handleFn=handler;
- } else if (typeof(_self.userHandlers[handler])=="function"){
- handleFn=_self.userHandlers[handler];
- }
- if (handleFn){
- try { handleFn.apply(_self.userHandlers, [e.type,res,ui,page,e]);
- bHandled = true;
- }catch(err){ debug(err); }
- }
- }
- if (bHandled && _self.conf.firstMatchOnly) return false;
- });
- //Pass to default if specified and can handle this event type
- if (!bHandled && this.conf.defaultHandler && this.defaultHandlerEvents[e.type]) {
- if (typeof(this.conf.defaultHandler) == "function") {
- try {
- this.conf.defaultHandler.apply(_self.userHandlers, [e.type, ui, page, e]);
- } catch(err) { debug(err); }
- }
- }
- },
-
- _detachEvents: function(){
- if (this._eventData){
- $(document).undelegate(
- this._eventData.selectors,
- this._eventData.events, this._eventData.handler
- );
- }
- } ,
-
- destroy: function(){
- this._detachEvents();
- this.routes=this.routesRex=null;
- } ,
-
- getParams: function(hashparams){
- if (!hashparams) return null;
- var params={}, tmp;
- var tokens=hashparams.slice( hashparams.indexOf('?')+1 ).split("&");
- $.each(tokens,function(k,v){
- tmp=v.split("=");
- tmp[0]=decodeURIComponent(tmp[0]);
- if (params[tmp[0]]){
- if (!(params[tmp[0]] instanceof Array)){
- params[tmp[0]]=[ params[tmp[0]] ];
- }
- params[tmp[0]].push( decodeURIComponent(tmp[1]) );
- } else {
- params[tmp[0]]=decodeURIComponent(tmp[1]);
- }
- });
- if ($.isEmptyObject(params)) return null;
- return params;
- }
- });
-
- });
- return {};
-}));
+/*!
+ * jQueryMobile-router v0.9
+ * http://github.com/azicchetti/jquerymobile-router
+ *
+ * Copyright 2011 (c) Andrea Zicchetti
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://github.com/azicchetti/jquerymobile-router/blob/master/MIT-LICENSE.txt
+ * http://github.com/azicchetti/jquerymobile-router/blob/master/GPL-LICENSE.txt
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery'], factory);
+ } else {
+ factory(jQuery);
+ }
+}(this, function ($) {
+
+$(document).bind("mobileinit",function(){
+
+ /* supports the following configurations:
+ $.mobile.jqmRouter.fixFirstPageDataUrl=true
+ $.mobile.jqmRouter.firstPageDataUrl="index.html"
+ jQM doesn't handle correctly the dataurl of the first page you display
+ in a single-page template mode. In fact, the page is fetched again
+ from the server the first time you try to access it through a link.
+ If this option is set to true, jquery mobile router extensions will
+ try to fix this problem. In order to set the data url, you have to
+ provide the name of the file containing the first page into the
+ "firstPageDataUrl" property (for example: index.html)
+
+ */
+
+ var config=$.extend({
+ fixFirstPageDataUrl: false, firstPageDataUrl: "index.html",
+ ajaxApp: false, firstMatchOnly: false
+ },$.mobile.jqmRouter || {});
+
+
+ var DEBUG=true;
+ function debug(err){
+ if (DEBUG) console.log(err);
+ }
+
+ var previousUrl=null, nextUrl=null;
+
+ $(document).bind("pagebeforechange", function( e, data ) {
+ var toPage=( typeof data.toPage === "string" ) ? data.toPage : data.toPage.jqmData("url")||"";
+
+ if ( data.options.hasOwnProperty("_jqmrouter_handled") ){ return; }
+ data.options._jqmrouter_handled = true;
+
+ var u = $.mobile.path.parseUrl( toPage );
+ previousUrl=nextUrl;
+ nextUrl=u;
+
+ if ( u.hash.indexOf("?") !== -1 ) {
+ var page=u.hash.replace( /\?.*$/, "" );
+ // We don't want the data-url of the page we just modified
+ // to be the url that shows up in the browser's location field,
+ // so set the dataUrl option to the URL with hash parameters
+ data.options.dataUrl = u.href;
+ // Now call changePage() and tell it to switch to
+ // the page we just modified, but only in case it's different
+ // from the current page
+ if ( $.mobile.activePage &&
+ page.replace(/^#/,"")==$.mobile.activePage.jqmData("url")
+ ){
+ data.options.allowSamePageTransition=true;
+ $.mobile.changePage( $(page), data.options );
+ } else {
+ $.mobile.changePage( $(page), data.options );
+ }
+ // Make sure to tell changePage() we've handled this call so it doesn't
+ // have to do anything.
+ e.preventDefault();
+ $.mobile.urlHistory.ignoreNextHashChange=true;
+ }
+ });
+
+
+ if (config.fixFirstPageDataUrl){
+ $(document).ready(function(){
+ if (!window.location.pathname.match("/$")){
+ return;
+ }
+ var page=$(":jqmData(role='page')").first();
+ var dataUrl=page.jqmData("url"),
+ guessedDataUrl=window.location.pathname
+ +config.firstPageDataUrl
+ +window.location.search
+ +window.location.hash
+ ;
+
+ if (dataUrl!=guessedDataUrl){
+ page.attr("data-url",guessedDataUrl)
+ .jqmData("url",guessedDataUrl);
+ }
+ });
+ }
+
+ $.mobile.Router=function(userRoutes,userHandlers,conf){
+ /* userRoutes format:
+ {
+ "regexp": "function name", // defaults to jqm pagebeforeshow event
+ "regexp": function(){ ... }, // defaults to jqm pagebeforeshow event
+ "regexp": { handler: "function name", events: "bc,c,bs,s,bh,h" },
+ "regexp": { handler: function(){ ... }, events: "bc,c,bs,s,bh,h" }
+ }
+ */
+ this.routes={
+ pagebeforecreate: null, pagecreate: null,
+ pagebeforeshow: null, pageshow: null,
+ pagebeforehide: null, pagehide: null,
+ pageinit: null, pageremove: null,
+ pagebeforechange: null, pagebeforeload: null,
+ pageload: null
+ };
+ this.evtLookup = {
+ bC: "pagebeforechange", bl: "pagebeforeload",
+ l: "pageload",
+ bc: "pagebeforecreate", c: "pagecreate",
+ bs: "pagebeforeshow", s: "pageshow",
+ bh: "pagebeforehide", h: "pagehide",
+ i: "pageinit", rm: "pageremove"
+ };
+ this.routesRex={};
+ this.conf=$.extend({}, config, conf || {});
+ this.defaultHandlerEvents = {};
+ if (this.conf.defaultHandlerEvents) {
+ var evts = this.conf.defaultHandlerEvents.split(",");
+ for (var i = 0; i < evts.length; i++) {
+ this.defaultHandlerEvents[this.evtLookup[evts[i]]] = evts[i];
+ }
+ }
+ this.add(userRoutes,userHandlers);
+ }
+ $.extend($.mobile.Router.prototype,{
+ documentEvts: { pagebeforechange:1, pagebeforeload:1, pageload:1 },
+
+ add: function(userRoutes,userHandlers,_skipAttach){
+ if (!userRoutes) return;
+
+ var _self=this, evtList=[], docEvtList=[];
+ if (userRoutes instanceof Array){
+ $.each(userRoutes,$.proxy(function(k,v){
+ this.add(v,userHandlers,true);
+ },this));
+ } else {
+ $.each(userRoutes,function(r,el){
+ if(typeof(el)=="string" || typeof(el)=="function"){
+ if (_self.routes.pagebeforeshow===null){
+ _self.routes.pagebeforeshow={};
+ }
+ _self.routes.pagebeforeshow[r]=el;
+ if (! _self.routesRex.hasOwnProperty(r)){
+ _self.routesRex[r]=new RegExp(r);
+ }
+ } else {
+ var i,trig=el.events.split(","),evt;
+ for(i=0;i<trig.length;i++){
+ evt=_self.evtLookup[trig[i]];
+ if (_self.routes.hasOwnProperty(evt)){
+ if (_self.routes[evt]===null){
+ _self.routes[evt]={};
+ }
+ _self.routes[evt][r]=el.handler;
+ if (! _self.routesRex.hasOwnProperty(r)){
+ _self.routesRex[r]=new RegExp(r);
+ }
+ } else {
+ debug("can't set unsupported route "+trig[i]);
+ }
+ }
+ }
+ });
+ }
+ if (_skipAttach===true) return;
+ if (!this.userHandlers){
+ this.userHandlers=userHandlers||{};
+ } else {
+ $.extend(this.userHandlers,userHandlers||{});
+ }
+ $.each(_self.routes,function(evt,el){
+ if (el!==null){
+ if (!_self.documentEvts[evt]){
+ evtList.push(evt);
+ } else {
+ docEvtList.push(evt);
+ }
+ }
+ });
+ this._detachEvents();
+ var routeHandler=function(e,ui){ _self._processRoutes(e,ui,this); };
+ if (evtList.length>0){
+ this._eventData={
+ events: evtList.join(" "),
+ selectors: ":jqmData(role='page'),:jqmData(role='dialog')",
+ handler: routeHandler
+ };
+ $(document).delegate(
+ this._eventData.selectors,
+ this._eventData.events, this._eventData.handler
+ );
+ }
+ if (docEvtList.length>0){
+ this._docEventData={
+ events: docEvtList.join(" "),
+ handler: routeHandler
+ };
+ $(document).bind(this._docEventData.events, this._docEventData.handler);
+ }
+ },
+
+ _processRoutes: function(e,ui,page){
+ var _self=this, refUrl, url, $page, retry=0;
+ if (e.type in {
+ "pagebeforehide":true, "pagehide":true, "pageremove": true
+ }){
+ refUrl=previousUrl;
+ } else {
+ refUrl=nextUrl;
+ }
+ do {
+ if (!refUrl){
+ if (page){
+ $page=$(page);
+ refUrl=$page.jqmData("url");
+ if (refUrl){
+ if ($page.attr("id")==refUrl) refUrl="#"+refUrl;
+ refUrl=$.mobile.path.parseUrl(refUrl);
+ }
+ }
+ } else if (!this.documentEvts[e.type] && page && !$(page).jqmData("url")){
+ return;
+ }
+ if (!refUrl) return;
+ url=( !this.conf.ajaxApp ?
+ refUrl.hash
+ :refUrl.pathname + refUrl.search + refUrl.hash
+ );
+ if (url.length==0){
+ // if ajaxApp is false, url may be "" when the user clicks the back button
+ // and returns to the first page of the application (which is usually
+ // loaded without the hash part of the url). Let's handle this...
+ refUrl="";
+ }
+ retry++;
+ } while(url.length==0 && retry<=1);
+
+ var bHandled = false;
+ $.each(this.routes[e.type],function(route,handler){
+ var res, handleFn;
+ if ( (res=url.match(_self.routesRex[route])) ){
+ if (typeof(handler)=="function"){
+ handleFn=handler;
+ } else if (typeof(_self.userHandlers[handler])=="function"){
+ handleFn=_self.userHandlers[handler];
+ }
+ if (handleFn){
+ try { handleFn.apply(_self.userHandlers, [e.type,res,ui,page,e]);
+ bHandled = true;
+ }catch(err){ debug(err); }
+ }
+ }
+ if (bHandled && _self.conf.firstMatchOnly) return false;
+ });
+ //Pass to default if specified and can handle this event type
+ if (!bHandled && this.conf.defaultHandler && this.defaultHandlerEvents[e.type]) {
+ if (typeof(this.conf.defaultHandler) == "function") {
+ try {
+ this.conf.defaultHandler.apply(_self.userHandlers, [e.type, ui, page, e]);
+ } catch(err) { debug(err); }
+ }
+ }
+ },
+
+ _detachEvents: function(){
+ if (this._eventData){
+ $(document).undelegate(
+ this._eventData.selectors,
+ this._eventData.events, this._eventData.handler
+ );
+ }
+ if (this._docEventData){
+ $(document).unbind(this._docEventData.events, this._docEventData.handler);
+ }
+ } ,
+
+ destroy: function(){
+ this._detachEvents();
+ this.routes=this.routesRex=null;
+ } ,
+
+ getParams: function(hashparams){
+ if (!hashparams) return null;
+ var params={}, tmp;
+ var tokens=hashparams.slice( hashparams.indexOf('?')+1 ).split("&");
+ $.each(tokens,function(k,v){
+ tmp=v.split("=");
+ tmp[0]=decodeURIComponent(tmp[0]);
+ if (params[tmp[0]]){
+ if (!(params[tmp[0]] instanceof Array)){
+ params[tmp[0]]=[ params[tmp[0]] ];
+ }
+ params[tmp[0]].push( decodeURIComponent(tmp[1]) );
+ } else {
+ params[tmp[0]]=decodeURIComponent(tmp[1]);
+ }
+ });
+ if ($.isEmptyObject(params)) return null;
+ return params;
+ }
+ });
+
+});
+return {};
+
+}));
View
4 js/jquery.mobile.router.min.js
@@ -1,10 +1,10 @@
/*!
- * jQueryMobile-router v0.81
+ * jQueryMobile-router v0.9
* http://github.com/azicchetti/jquerymobile-router
*
* Copyright 2011 (c) Andrea Zicchetti
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://github.com/azicchetti/jquerymobile-router/blob/master/MIT-LICENSE.txt
* http://github.com/azicchetti/jquerymobile-router/blob/master/GPL-LICENSE.txt
*/
-(function(a,b){if(typeof define==="function"&&define.amd){define(["jquery"],b)}else{b(jQuery)}}(this,function(a){a(document).bind("mobileinit",function(){var d=a.extend({fixFirstPageDataUrl:false,firstPageDataUrl:"index.html",ajaxApp:false,firstMatchOnly:false},a.mobile.jqmRouter||{});var c=true;function b(g){if(c){console.log(g)}}var e=null,f=null;a(document).bind("pagebeforechange",function(k,j){var h=(typeof j.toPage==="string")?j.toPage:j.toPage.jqmData("url")||"";if(j.options.hasOwnProperty("_jqmrouter_handled")){return}j.options._jqmrouter_handled=true;var g=a.mobile.path.parseUrl(h);e=f;f=g;if(g.hash.indexOf("?")!==-1){var i=g.hash.replace(/\?.*$/,"");j.options.dataUrl=g.href;if(a.mobile.activePage&&i.replace(/^#/,"")==a.mobile.activePage.jqmData("url")){j.options.allowSamePageTransition=true;a.mobile.changePage(a(i),j.options)}else{a.mobile.changePage(a(i),j.options)}k.preventDefault();a.mobile.urlHistory.ignoreNextHashChange=true}});if(d.fixFirstPageDataUrl){a(document).ready(function(){if(!window.location.pathname.match("/$")){return}var h=a(":jqmData(role='page')").first();var i=h.jqmData("url"),g=window.location.pathname+d.firstPageDataUrl+window.location.search+window.location.hash;if(i!=g){h.attr("data-url",g).jqmData("url",g)}})}a.mobile.Router=function(k,h,j){this.routes={pagebeforecreate:null,pagecreate:null,pagebeforeshow:null,pageshow:null,pagebeforehide:null,pagehide:null,pageinit:null,pageremove:null};this.evtLookup={bc:"pagebeforecreate",c:"pagecreate",bs:"pagebeforeshow",s:"pageshow",bh:"pagebeforehide",h:"pagehide",i:"pageinit",rm:"pageremove"};this.routesRex={};this.conf=a.extend({},d,j||{});this.defaultHandlerEvents={};if(this.conf.defaultHandlerEvents){var g=this.conf.defaultHandlerEvents.split(",");for(var l=0;l<g.length;l++){this.defaultHandlerEvents[this.evtLookup[g[l]]]=g[l]}}this.add(k,h)};a.extend(a.mobile.Router.prototype,{add:function(j,i){if(!j){return}var g=this,h=[];if(j instanceof Array){a.each(j,a.proxy(function(m,l){this.add(l,i)},this))}else{a.each(j,function(o,m){if(typeof(m)=="string"||typeof(m)=="function"){if(g.routes.pagebeforeshow===null){g.routes.pagebeforeshow={}}g.routes.pagebeforeshow[o]=m;if(!g.routesRex.hasOwnProperty(o)){g.routesRex[o]=new RegExp(o)}}else{var l,n=m.events.split(","),k;for(l=0;l<n.length;l++){k=g.evtLookup[n[l]];if(g.routes.hasOwnProperty(k)){if(g.routes[k]===null){g.routes[k]={}}g.routes[k][o]=m.handler;if(!g.routesRex.hasOwnProperty(o)){g.routesRex[o]=new RegExp(o)}}else{b("can't set unsupported route "+n[l])}}}});a.each(g.routes,function(k,l){if(l!==null){h.push(k)}});if(!this.userHandlers){this.userHandlers=i||{}}else{a.extend(this.userHandlers,i||{})}this._detachEvents();if(h.length>0){this._eventData={events:h.join(" "),selectors:":jqmData(role='page'),:jqmData(role='dialog')",handler:function(l,k){g._processRoutes(l,k,this)}};a(document).delegate(this._eventData.selectors,this._eventData.events,this._eventData.handler)}}},_processRoutes:function(l,p,m){var n=this,o,h,k,g=0;if(l.type in {pagebeforehide:true,pagehide:true,pageremove:true}){o=e}else{o=f}do{if(!o){if(m){k=a(m);o=k.jqmData("url");if(o){if(k.attr("id")==o){o="#"+o}o=a.mobile.path.parseUrl(o)}}}else{if(m&&!a(m).jqmData("url")){return}}if(!o){return}h=(!this.conf.ajaxApp?o.hash:o.pathname+o.search+o.hash);if(h.length==0){o=""}g++}while(h.length==0&&g<=1);var j=false;a.each(this.routes[l.type],function(q,s){var r,u;if((r=h.match(n.routesRex[q]))){if(typeof(s)=="function"){u=s}else{if(typeof(n.userHandlers[s])=="function"){u=n.userHandlers[s]}}if(u){try{u.apply(n.userHandlers,[l.type,r,p,m,l]);j=true}catch(t){b(t)}}}if(j&&n.conf.firstMatchOnly){return false}});if(!j&&this.conf.defaultHandler&&this.defaultHandlerEvents[l.type]){if(typeof(this.conf.defaultHandler)=="function"){try{this.conf.defaultHandler.apply(n.userHandlers,[l.type,p,m,l])}catch(i){b(i)}}}},_detachEvents:function(){if(this._eventData){a(document).undelegate(this._eventData.selectors,this._eventData.events,this._eventData.handler)}},destroy:function(){this._detachEvents();this.routes=this.routesRex=null},getParams:function(g){if(!g){return null}var j={},h;var i=g.slice(g.indexOf("?")+1).split("&");a.each(i,function(m,l){h=l.split("=");h[0]=decodeURIComponent(h[0]);if(j[h[0]]){if(!(j[h[0]] instanceof Array)){j[h[0]]=[j[h[0]]]}j[h[0]].push(decodeURIComponent(h[1]))}else{j[h[0]]=decodeURIComponent(h[1])}});if(a.isEmptyObject(j)){return null}return j}})});return{}}));
+(function(a,b){if(typeof define==="function"&&define.amd){define(["jquery"],b)}else{b(jQuery)}}(this,function(a){a(document).bind("mobileinit",function(){var d=a.extend({fixFirstPageDataUrl:false,firstPageDataUrl:"index.html",ajaxApp:false,firstMatchOnly:false},a.mobile.jqmRouter||{});var c=true;function b(g){if(c){console.log(g)}}var e=null,f=null;a(document).bind("pagebeforechange",function(k,j){var h=(typeof j.toPage==="string")?j.toPage:j.toPage.jqmData("url")||"";if(j.options.hasOwnProperty("_jqmrouter_handled")){return}j.options._jqmrouter_handled=true;var g=a.mobile.path.parseUrl(h);e=f;f=g;if(g.hash.indexOf("?")!==-1){var i=g.hash.replace(/\?.*$/,"");j.options.dataUrl=g.href;if(a.mobile.activePage&&i.replace(/^#/,"")==a.mobile.activePage.jqmData("url")){j.options.allowSamePageTransition=true;a.mobile.changePage(a(i),j.options)}else{a.mobile.changePage(a(i),j.options)}k.preventDefault();a.mobile.urlHistory.ignoreNextHashChange=true}});if(d.fixFirstPageDataUrl){a(document).ready(function(){if(!window.location.pathname.match("/$")){return}var h=a(":jqmData(role='page')").first();var i=h.jqmData("url"),g=window.location.pathname+d.firstPageDataUrl+window.location.search+window.location.hash;if(i!=g){h.attr("data-url",g).jqmData("url",g)}})}a.mobile.Router=function(k,h,j){this.routes={pagebeforecreate:null,pagecreate:null,pagebeforeshow:null,pageshow:null,pagebeforehide:null,pagehide:null,pageinit:null,pageremove:null,pagebeforechange:null,pagebeforeload:null,pageload:null};this.evtLookup={bC:"pagebeforechange",bl:"pagebeforeload",l:"pageload",bc:"pagebeforecreate",c:"pagecreate",bs:"pagebeforeshow",s:"pageshow",bh:"pagebeforehide",h:"pagehide",i:"pageinit",rm:"pageremove"};this.routesRex={};this.conf=a.extend({},d,j||{});this.defaultHandlerEvents={};if(this.conf.defaultHandlerEvents){var g=this.conf.defaultHandlerEvents.split(",");for(var l=0;l<g.length;l++){this.defaultHandlerEvents[this.evtLookup[g[l]]]=g[l]}}this.add(k,h)};a.extend(a.mobile.Router.prototype,{documentEvts:{pagebeforechange:1,pagebeforeload:1,pageload:1},add:function(j,i,l){if(!j){return}var g=this,h=[],m=[];if(j instanceof Array){a.each(j,a.proxy(function(o,n){this.add(n,i,true)},this))}else{a.each(j,function(s,p){if(typeof(p)=="string"||typeof(p)=="function"){if(g.routes.pagebeforeshow===null){g.routes.pagebeforeshow={}}g.routes.pagebeforeshow[s]=p;if(!g.routesRex.hasOwnProperty(s)){g.routesRex[s]=new RegExp(s)}}else{var o,q=p.events.split(","),n;for(o=0;o<q.length;o++){n=g.evtLookup[q[o]];if(g.routes.hasOwnProperty(n)){if(g.routes[n]===null){g.routes[n]={}}g.routes[n][s]=p.handler;if(!g.routesRex.hasOwnProperty(s)){g.routesRex[s]=new RegExp(s)}}else{b("can't set unsupported route "+q[o])}}}})}if(l===true){return}if(!this.userHandlers){this.userHandlers=i||{}}else{a.extend(this.userHandlers,i||{})}a.each(g.routes,function(n,o){if(o!==null){if(!g.documentEvts[n]){h.push(n)}else{m.push(n)}}});this._detachEvents();var k=function(o,n){g._processRoutes(o,n,this)};if(h.length>0){this._eventData={events:h.join(" "),selectors:":jqmData(role='page'),:jqmData(role='dialog')",handler:k};a(document).delegate(this._eventData.selectors,this._eventData.events,this._eventData.handler)}if(m.length>0){this._docEventData={events:m.join(" "),handler:k};a(document).bind(this._docEventData.events,this._docEventData.handler)}},_processRoutes:function(l,p,m){var n=this,o,h,k,g=0;if(l.type in {pagebeforehide:true,pagehide:true,pageremove:true}){o=e}else{o=f}do{if(!o){if(m){k=a(m);o=k.jqmData("url");if(o){if(k.attr("id")==o){o="#"+o}o=a.mobile.path.parseUrl(o)}}}else{if(!this.documentEvts[l.type]&&m&&!a(m).jqmData("url")){return}}if(!o){return}h=(!this.conf.ajaxApp?o.hash:o.pathname+o.search+o.hash);if(h.length==0){o=""}g++}while(h.length==0&&g<=1);var j=false;a.each(this.routes[l.type],function(q,s){var r,u;if((r=h.match(n.routesRex[q]))){if(typeof(s)=="function"){u=s}else{if(typeof(n.userHandlers[s])=="function"){u=n.userHandlers[s]}}if(u){try{u.apply(n.userHandlers,[l.type,r,p,m,l]);j=true}catch(t){b(t)}}}if(j&&n.conf.firstMatchOnly){return false}});if(!j&&this.conf.defaultHandler&&this.defaultHandlerEvents[l.type]){if(typeof(this.conf.defaultHandler)=="function"){try{this.conf.defaultHandler.apply(n.userHandlers,[l.type,p,m,l])}catch(i){b(i)}}}},_detachEvents:function(){if(this._eventData){a(document).undelegate(this._eventData.selectors,this._eventData.events,this._eventData.handler)}if(this._docEventData){a(document).unbind(this._docEventData.events,this._docEventData.handler)}},destroy:function(){this._detachEvents();this.routes=this.routesRex=null},getParams:function(g){if(!g){return null}var j={},h;var i=g.slice(g.indexOf("?")+1).split("&");a.each(i,function(m,l){h=l.split("=");h[0]=decodeURIComponent(h[0]);if(j[h[0]]){if(!(j[h[0]] instanceof Array)){j[h[0]]=[j[h[0]]]}j[h[0]].push(decodeURIComponent(h[1]))}else{j[h[0]]=decodeURIComponent(h[1])}});if(a.isEmptyObject(j)){return null}return j}})});return{}}));

0 comments on commit edf70f0

Please sign in to comment.
Something went wrong with that request. Please try again.