|
27 | 27 | var splitkey = '&' + $.mobile.subPageUrlKey; |
28 | 28 | return path && path.split( splitkey )[0].split( dialogHashKey )[0]; |
29 | 29 | }, |
30 | | - |
| 30 | + |
31 | 31 | //set location hash to path |
32 | 32 | set: function( path ){ |
33 | 33 | location.hash = path; |
|
39 | 39 | setOrigin: function(){ |
40 | 40 | path.origin = path.get( location.protocol + '//' + location.host + location.pathname ); |
41 | 41 | }, |
42 | | - |
| 42 | + |
43 | 43 | //prefix a relative url with the current path |
44 | 44 | makeAbsolute: function( url ){ |
45 | 45 | return path.get() + url; |
46 | 46 | }, |
47 | | - |
| 47 | + |
48 | 48 | //return a url path with the window's location protocol/hostname removed |
49 | 49 | clean: function( url ){ |
50 | 50 | return url.replace( location.protocol + "//" + location.host, ""); |
51 | 51 | }, |
52 | | - |
| 52 | + |
53 | 53 | //just return the url without an initial # |
54 | 54 | stripHash: function( url ){ |
55 | 55 | return url.replace( /^#/, "" ); |
56 | 56 | }, |
57 | | - |
| 57 | + |
58 | 58 | //check whether a url is referencing the same domain, or an external domain or different protocol |
59 | 59 | //could be mailto, etc |
60 | 60 | isExternal: function( url ){ |
61 | 61 | return path.hasProtocol( path.clean( url ) ); |
62 | 62 | }, |
63 | | - |
| 63 | + |
64 | 64 | hasProtocol: function( url ){ |
65 | 65 | return /^(:?\w+:)/.test( url ); |
66 | 66 | }, |
67 | | - |
| 67 | + |
68 | 68 | //check if the url is relative |
69 | 69 | isRelative: function( url ){ |
70 | 70 | return /^[^\/|#]/.test( url ) && !path.hasProtocol( url ); |
| 71 | + }, |
| 72 | + |
| 73 | + isEmbeddedPage: function( url ){ |
| 74 | + return /^#/.test( url ); |
71 | 75 | } |
72 | 76 | }, |
73 | 77 |
|
74 | 78 | //will be defined when a link is clicked and given an active class |
75 | 79 | $activeClickedLink = null, |
76 | | - |
| 80 | + |
77 | 81 | //urlHistory is purely here to make guesses at whether the back or forward button was clicked |
78 | 82 | //and provide an appropriate transition |
79 | 83 | urlHistory = { |
80 | 84 | //array of pages that are visited during a single page load. each has a url and optional transition |
81 | 85 | stack: [], |
82 | | - |
| 86 | + |
83 | 87 | //maintain an index number for the active page in the stack |
84 | 88 | activeIndex: 0, |
85 | | - |
| 89 | + |
86 | 90 | //get active |
87 | 91 | getActive: function(){ |
88 | 92 | return urlHistory.stack[ urlHistory.activeIndex ]; |
89 | 93 | }, |
90 | | - |
| 94 | + |
91 | 95 | getPrev: function(){ |
92 | 96 | return urlHistory.stack[ urlHistory.activeIndex - 1 ]; |
93 | 97 | }, |
94 | | - |
| 98 | + |
95 | 99 | getNext: function(){ |
96 | 100 | return urlHistory.stack[ urlHistory.activeIndex + 1 ]; |
97 | 101 | }, |
98 | | - |
| 102 | + |
99 | 103 | // addNew is used whenever a new page is added |
100 | 104 | addNew: function( url, transition ){ |
101 | 105 | //if there's forward history, wipe it |
102 | 106 | if( urlHistory.getNext() ){ |
103 | 107 | urlHistory.clearForward(); |
104 | 108 | } |
105 | | - |
| 109 | + |
106 | 110 | urlHistory.stack.push( {url : url, transition: transition } ); |
107 | | - |
| 111 | + |
108 | 112 | urlHistory.activeIndex = urlHistory.stack.length - 1; |
109 | 113 | }, |
110 | | - |
| 114 | + |
111 | 115 | //wipe urls ahead of active index |
112 | 116 | clearForward: function(){ |
113 | 117 | urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 ); |
114 | 118 | }, |
115 | | - |
| 119 | + |
116 | 120 | //enable/disable hashchange event listener |
117 | 121 | //toggled internally when location.hash is updated to match the url of a successful page load |
118 | 122 | listeningEnabled: true |
|
123 | 127 |
|
124 | 128 | //contains role for next page, if defined on clicked link via data-rel |
125 | 129 | nextPageRole = null, |
126 | | - |
| 130 | + |
127 | 131 | //nonsense hash change key for dialogs, so they create a history entry |
128 | 132 | dialogHashKey = "&ui-state=dialog"; |
129 | 133 |
|
|
213 | 217 | return $(this).one('webkitAnimationEnd', callback); |
214 | 218 | } |
215 | 219 | else{ |
216 | | - callback(); |
| 220 | + // defer execution for consistency between webkit/non webkit |
| 221 | + setTimeout(callback, 0); |
217 | 222 | } |
218 | 223 | }; |
219 | 224 |
|
|
224 | 229 | //update location.hash, with or without triggering hashchange event |
225 | 230 | //TODO - deprecate this one at 1.0 |
226 | 231 | $.mobile.updateHash = path.set; |
227 | | - |
| 232 | + |
228 | 233 | //expose path object on $.mobile |
229 | 234 | $.mobile.path = path; |
230 | | - |
| 235 | + |
231 | 236 | //expose base object on $.mobile |
232 | 237 | $.mobile.base = base; |
233 | 238 |
|
234 | 239 | //url stack, useful when plugins need to be aware of previous pages viewed |
235 | 240 | //TODO: deprecate this one at 1.0 |
236 | 241 | $.mobile.urlstack = urlHistory.stack; |
237 | | - |
| 242 | + |
238 | 243 | //history stack |
239 | 244 | $.mobile.urlHistory = urlHistory; |
240 | 245 |
|
|
254 | 259 | currPage = urlHistory.getActive(), |
255 | 260 | back = false, |
256 | 261 | forward = false; |
257 | | - |
258 | | - |
| 262 | + |
| 263 | + |
259 | 264 | // If we are trying to transition to the same page that we are currently on ignore the request. |
260 | 265 | // an illegal same page request is defined by the current page being the same as the url, as long as there's history |
261 | 266 | // and to is not an array or object (those are allowed to be "same") |
262 | 267 | if( currPage && urlHistory.stack.length > 1 && currPage.url === url && !toIsArray && !toIsObject ) { |
263 | 268 | return; |
264 | | - } |
265 | | - |
| 269 | + } |
| 270 | + |
266 | 271 | // if the changePage was sent from a hashChange event |
267 | 272 | // guess if it came from the history menu |
268 | 273 | if( fromHashChange ){ |
269 | | - |
| 274 | + |
270 | 275 | // check if url is in history and if it's ahead or behind current page |
271 | 276 | $.each( urlHistory.stack, function( i ){ |
272 | 277 | //if the url is in the stack, it's a forward or a back |
|
280 | 285 | urlHistory.activeIndex = i; |
281 | 286 | } |
282 | 287 | }); |
283 | | - |
| 288 | + |
284 | 289 | //if it's a back, use reverse animation |
285 | 290 | if( back ){ |
286 | 291 | reverse = true; |
|
290 | 295 | transition = transition || urlHistory.getActive().transition; |
291 | 296 | } |
292 | 297 | } |
293 | | - |
| 298 | + |
294 | 299 |
|
295 | 300 | if( toIsObject && to.url ){ |
296 | 301 | url = to.url, |
|
302 | 307 | if($.type( data ) == "object" ){ |
303 | 308 | data = $.param(data); |
304 | 309 | } |
305 | | - |
| 310 | + |
306 | 311 | url += "?" + data; |
307 | 312 | data = undefined; |
308 | 313 | } |
|
328 | 333 | var currScroll = $window.scrollTop(), |
329 | 334 | perspectiveTransitions = [ "flip" ], |
330 | 335 | pageContainerClasses = []; |
331 | | - |
332 | | - //support deep-links to generated sub-pages |
| 336 | + |
| 337 | + //support deep-links to generated sub-pages |
333 | 338 | if( url.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ){ |
334 | 339 | to = $( "[data-url='" + url + "']" ); |
335 | 340 | } |
|
351 | 356 | path.set( url ); |
352 | 357 | urlHistory.listeningEnabled = true; |
353 | 358 | } |
354 | | - |
| 359 | + |
355 | 360 | //add page to history stack if it's not back or forward |
356 | 361 | if( !back && !forward ){ |
357 | 362 | urlHistory.addNew( url, transition ); |
358 | 363 | } |
359 | | - |
| 364 | + |
360 | 365 | removeActiveLinkClass(); |
361 | 366 |
|
362 | 367 | //jump to top or prev scroll, sometimes on iOS the page has not rendered yet. I could only get by this with a setTimeout, but would like to avoid that. |
363 | 368 | $.mobile.silentScroll( to.data( "lastScroll" ) ); |
| 369 | + |
364 | 370 | reFocus( to ); |
365 | 371 |
|
366 | 372 | //trigger show/hide events |
|
394 | 400 |
|
395 | 401 | pageContainerClasses = []; |
396 | 402 | }; |
397 | | - |
398 | | - |
| 403 | + |
| 404 | + |
399 | 405 |
|
400 | 406 | if(transition && (transition !== 'none')){ |
401 | 407 | $.mobile.pageLoading( true ); |
|
467 | 473 | fileUrl = toIDfileurl; |
468 | 474 | } |
469 | 475 | } |
470 | | - |
| 476 | + |
471 | 477 | // ensure a transition has been set where pop is undefined |
472 | 478 | defaultTransition(); |
473 | 479 |
|
|
492 | 498 | type: type, |
493 | 499 | data: data, |
494 | 500 | success: function( html ) { |
495 | | - |
496 | | - //pre-parse html to check for a data-url, |
| 501 | + |
| 502 | + //pre-parse html to check for a data-url, |
497 | 503 | //use it as the new fileUrl, base path, etc |
498 | 504 | var redirectLoc = / data-url="(.*)"/.test( html ) && RegExp.$1; |
499 | 505 |
|
500 | 506 | if( redirectLoc ){ |
501 | 507 | if(base){ |
502 | 508 | base.set( redirectLoc ); |
503 | | - } |
| 509 | + } |
504 | 510 | url = fileUrl = path.makeAbsolute( path.getFilePath( redirectLoc ) ); |
505 | 511 | } |
506 | 512 | else { |
507 | 513 | if(base){ |
508 | 514 | base.set(fileUrl); |
509 | | - } |
| 515 | + } |
510 | 516 | } |
511 | | - |
| 517 | + |
512 | 518 | var all = $("<div></div>"); |
513 | 519 | //workaround to allow scripts to execute when included in page divs |
514 | 520 | all.get(0).innerHTML = html; |
|
529 | 535 | } |
530 | 536 | }); |
531 | 537 | } |
532 | | - |
| 538 | + |
533 | 539 | //append to page and enhance |
534 | 540 | to |
535 | 541 | .attr( "data-url", fileUrl ) |
|
592 | 598 |
|
593 | 599 | //click routing - direct to HTTP or Ajax, accordingly |
594 | 600 | $( "a" ).live( "click", function(event) { |
595 | | - |
| 601 | + |
596 | 602 | var $this = $(this), |
597 | 603 | //get href, remove same-domain protocol and host |
598 | 604 | url = path.clean( $this.attr( "href" ) ), |
599 | | - |
600 | | - //check if it's external |
601 | | - isExternal = path.isExternal( url ) || $this.is( "[rel='external']" ), |
602 | | - |
| 605 | + |
| 606 | + //rel set to external |
| 607 | + isRelExternal = $this.is( "[rel='external']" ), |
| 608 | + |
| 609 | + //rel set to external |
| 610 | + isEmbeddedPage = path.isEmbeddedPage( url ), |
| 611 | + |
| 612 | + //check for protocol or rel and its not an embedded page |
| 613 | + //TODO overlap in logic from isExternal, rel=external check should be |
| 614 | + // moved into more comprehensive isExternalLink |
| 615 | + isExternal = path.isExternal( url ) || isRelExternal && !isEmbeddedPage, |
| 616 | + |
603 | 617 | //if target attr is specified we mimic _blank... for now |
604 | 618 | hasTarget = $this.is( "[target]" ); |
605 | | - |
| 619 | + |
606 | 620 | //if there's a data-rel=back attr, go back in history |
607 | 621 | if( $this.is( "[data-rel='back']" ) ){ |
608 | 622 | window.history.back(); |
609 | 623 | return false; |
610 | | - } |
| 624 | + } |
611 | 625 |
|
612 | 626 | if( url === "#" ){ |
613 | 627 | //for links created purely for interaction - ignore |
|
634 | 648 | //use ajax |
635 | 649 | var transition = $this.data( "transition" ), |
636 | 650 | direction = $this.data("direction"), |
637 | | - reverse = direction && direction == "reverse" || |
| 651 | + reverse = direction && direction == "reverse" || |
638 | 652 | // deprecated - remove by 1.0 |
639 | 653 | $this.data( "back" ); |
640 | | - |
641 | | - //this may need to be more specific as we use data-rel more |
| 654 | + |
| 655 | + //this may need to be more specific as we use data-rel more |
642 | 656 | nextPageRole = $this.attr( "data-rel" ); |
643 | 657 |
|
644 | 658 | //if it's a relative href, prefix href with base url |
|
0 commit comments