-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Refactor: navigation paths
The purpose of this document is mainly for me to get my head around all of the factors involved with loading files in jQuery Mobile (JQM). Path handling within jQuery mobile is probably the most delicate area of the code and many of us have tweaked and broken specific use cases at various times. I'm hoping that by enumerating factors, and specific use cases, that we can avoid breaking things in the future.
When I say file loading, I am specifcally talking about the code within jquery.mobile.navigation.js that handles clicks for all links, and the changePage() function itself.
At a minimum, I would say that we need to support the following types of paths:
- Absolute Examples:
- http://company.com/
- http://company.com/dir1/dir2/
- http://company.com/dir1/dir2/foo.html
- http://company.com:8080/
- http://company.com:8080/dir1/dir2/
- http://company.com:8080/dir1/dir2/foo.html
- file:///foo.html
- file:///dir1/dir2/foo.html
- file:///C:/foo.html
- file:///C:/dir1/dir2/foo.html
- Protocol Relative Examples:
- //company.com/
- //company.com/foo.html
- //company.com/dir1/dir2/
- //company.com/dir1/dir2/foo.html
- Site Relative Examples:
- /
- /foo.html
- /dir1/dir2/
- /dir1/dir2/foo.html
- Document Relative Examples:
- foo.html
- dir1/dir2/
- dir1/dir2/foo.html
- ../../
- ../../foo.html
- ../../dir1/dir2/foo.html
- Query Relative Example:
- ?a=1&b=2&c=3
When the initial JQM document is loaded, the navigation code checks for an existing base tag, if one does not exist, JQM injects one and sets its @href to path for the current document.
When the user navigates to an external page within their application, JQM first loads the content as HTML source, it then sets the base tag's @href attribute to the URL directory path of the page it just loaded. It has to do this prior to injecting the HTML source into the document so that any relative links or image paths get resolved relative to the new page's directory path.
There is a twist to this though. The new page could have a @data-url attribute defined, and the value of that attribute should be used as the base for any content related @href/@src attributes.
Key take aways here are that the path used for the base tag can be EITHER the value of a data-url OR the directory path used to load the page.
For Clarification (for docs, mostly)
The relationship (authoritative hierarchy?) between the id
attribute and the data-url
property. In particular, if we know a page is in the DOM, and it has both, do we address it by id
or data-url
?
NOTE
I did some testing a few months back on the types of paths (relative, site relative, absolute) that browsers support as the @href value of a base tag. What I found was that the only reliable format was absolute.
When the user clicks on a navigation link or programmatically calls changePage() with a URL, we should be able to handle any of the URLs mentioned in the "Overview" section above. How each type of URL is handled is slightly different. Below are my thoughts on how they should be handled.
In order to resolve a relative path, you need 2 pieces of information, the relative URL and the ABSOLUTE directory path that it is relative to. In JQM, we currently assume that relative URLs are relative to whatever path is currently in the location hash. So for example if the following link was clicked:
<a href="foo/bar.html">...</a>
and the URL bar of the browser contains:
http://jquerymobile.com/test/#/test/demos/index.html
JQM will resolve the path to be:
http://jquerymobile.com/test/demos/index.html
ISSUE
The document may initially contain a base tag that specifies a URL that has a different protocol and/or host from the document URL. (PhoneGap)
Also, if the page containing the link was an externally loaded page, it may have specified a different URL with the @data-url attribute.
Shouldn't we be resolving the relative URL against the data-url value placed on the page containing the link? Or alternately, shouldn't we be extracting the href property from the link element instead of retrieving the @href attribute off the element since the property should contain the fully resolved URL?
To resolve a site relative URL, you need 2 additional pieces of info, the protocol, and the server.
The server and the protocol need to come from the URL that was used to load the page containing the link. Today, I believe we don't factor in the protocol or host into our page loading. That is, we simply pass a relative or site relative path to our $.ajax() call.
ISSUE
As of 04/22/2011 PhoneGap is broken for the case where the document is loaded via file:// URL and the document contains a base tag that is a remote http:// server. The path JQM attempts to load seems to be a site relative path, where the path is actually the path from the document's file:// URL. The actual resolved href property on the link has the correct http:// path in it.
ISSUE
When we reduce an URL to its path component via path.clean(), we assume we are using the same protocol and host as the document. This may not be true.
POTENTIAL ISSUE
Today, we don't factor in the optional server port number. Should we?
To resolve a protocol relative path, we simply need the protocol used to load the page that contains the link.
ISSUE
I'm not sure if we handle protocol relative paths today, but I doubt it. Even if we did, we're probably pulling the protocol off the location.protocol property instead of the actual URL/base used to load the page where the link came from.
Handling absolute paths should be a no-brainer since they contain all the info we need. The only snag here is that cross-domain URLs are not allowed in the typical case, but are allowed in the Phone Gap case. For JQM Alpha 4 I added a config option that allows cross-domain URLs to pass through the page loading code:
$(document).live("mobileinit", function(){
$.mobile.allowCrossDomainPages = true;
});
The default for the config option above is false. If it is enabled in a normal browser, cross-domain http/https urls will be allowed to pass through the navigation code, but the browser will throw a security exception resulting in a failed page load.
Under the hood, this option triggers a check to see if the document URL was loaded via a file:// URL. If it was, and the current page request URL is http or https, it is allowed to pass through the navigation code by simply setting the isExternal boolean to false.
After walking through each path case above, I've come to the following conclusions:
- We need to stop using location.protocol and location.pathname in our code. We should be calculating the absolute URL for the document base when it is initially loaded, and then extracting any info we need from that.
- Switch to using the href property on any links we hijack. This will eliminate any guessing we have to do when processing the link URL since it will have already been resolved by the browser.
- We need to make sure we store the absolute URL used to load a given page in the @data-url attribute. The main reason for this is that we need to make sure we have the protocol and host available just in case a non-absolute URL is given to changePage().
- Base tags we set must contain the full file path, not the directory path to the file. This is necessary so that query relative and hash-only @href values get resolved properly by the browser.
- If possible make use of href expando properties on links instead of the @href attribute since it contains the fully resolved URL as calculated by the browser.
ISSUE
I'm not sure of the implications of some of these changes on our deep linking.
Broken base tags causes images and links to die in sub-directory documents. https://github.com/jquery/jquery-mobile/issues/1508
Image link in dialog rewritten incorrectly https://github.com/jquery/jquery-mobile/issues/1557
Href handling - make sure internal vs. external is as robust as needed https://github.com/jquery/jquery-mobile/issues/384
Absolute url cause page reload in 1.0a4.1 even when the host is the same https://github.com/jquery/jquery-mobile/issues/1534
Duplicate pages created in DOM https://github.com/jquery/jquery-mobile/issues/1024 https://github.com/jquery/jquery-mobile/pull/1456
Dialog Link inside Nested Listview causes error (a4.1) https://github.com/jquery/jquery-mobile/issues/1512
ChangePage() and Navigation paths refactor https://github.com/jquery/jquery-mobile/issues/1778
Using latest - AJAX requests broken due to incorrect host name https://github.com/jquery/jquery-mobile/issues/1640
Latest JQM breaks ajax requests in Chrome and android https://github.com/jquery/jquery-mobile/issues/1605
Absolute URLs in page navigation https://github.com/jquery/jquery-mobile/issues/1484
Firefox 3 fails to navigate to pages containing data-ajax='false' form attribute https://github.com/jquery/jquery-mobile/issues/1501
Ampersands in URLs cause issues https://github.com/jquery/jquery-mobile/issues/1251
Parameter passing between pages does not work https://github.com/jquery/jquery-mobile/issues/450
Parenthesis in URL leads to javascript errors in Firefox https://github.com/jquery/jquery-mobile/issues/1408
duplicateCachedPage delete wrong page if using REST principles https://github.com/jquery/jquery-mobile/issues/1537