2013-01-22 19:32:26 +01:00
|
|
|
/**
|
|
|
|
* @provides javelin-uri
|
|
|
|
* @requires javelin-install
|
|
|
|
* javelin-util
|
|
|
|
* javelin-stratcom
|
|
|
|
*
|
|
|
|
* @javelin-installs JX.$U
|
|
|
|
*
|
|
|
|
* @javelin
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handy convenience function that returns a @{class:JX.URI} instance. This
|
|
|
|
* allows you to write things like:
|
|
|
|
*
|
|
|
|
* JX.$U('http://zombo.com/').getDomain();
|
|
|
|
*
|
|
|
|
* @param string Unparsed URI.
|
|
|
|
* @return @{class:JX.URI} JX.URI instance.
|
|
|
|
*
|
|
|
|
* @group uri
|
|
|
|
*/
|
|
|
|
JX.$U = function(uri) {
|
|
|
|
return new JX.URI(uri);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a string URI into a maleable object.
|
|
|
|
*
|
|
|
|
* var uri = new JX.URI('http://www.example.com/asdf.php?a=b&c=d#anchor123');
|
|
|
|
* uri.getProtocol(); // http
|
|
|
|
* uri.getDomain(); // www.example.com
|
|
|
|
* uri.getPath(); // /asdf.php
|
|
|
|
* uri.getQueryParams(); // {a: 'b', c: 'd'}
|
|
|
|
* uri.getFragment(); // anchor123
|
|
|
|
*
|
|
|
|
* ...and back into a string:
|
|
|
|
*
|
|
|
|
* uri.setFragment('clowntown');
|
|
|
|
* uri.toString() // http://www.example.com/asdf.php?a=b&c=d#clowntown
|
|
|
|
*
|
|
|
|
* @group uri
|
|
|
|
*/
|
|
|
|
JX.install('URI', {
|
|
|
|
statics : {
|
|
|
|
_uriPattern : /(?:([^:\/?#]+):)?(?:\/\/([^:\/?#]*)(?::(\d*))?)?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a Javascript object into an HTTP query string.
|
|
|
|
*
|
|
|
|
* @param Object Map of query keys to values.
|
|
|
|
* @return String HTTP query string, like 'cow=quack&duck=moo'.
|
|
|
|
*/
|
|
|
|
_defaultQuerySerializer : function(obj) {
|
|
|
|
var kv_pairs = [];
|
|
|
|
for (var key in obj) {
|
2013-05-19 02:04:22 +02:00
|
|
|
if (obj[key] !== null) {
|
2013-01-22 19:32:26 +01:00
|
|
|
var value = encodeURIComponent(obj[key]);
|
|
|
|
kv_pairs.push(encodeURIComponent(key) + (value ? '=' + value : ''));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return kv_pairs.join('&');
|
2013-05-06 16:24:00 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
_decode : function(str) {
|
|
|
|
return decodeURIComponent(str.replace(/\+/g, ' '));
|
2013-01-22 19:32:26 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a URI
|
|
|
|
*
|
|
|
|
* Accepts either absolute or relative URIs. Relative URIs may have protocol
|
|
|
|
* and domain properties set to undefined
|
|
|
|
*
|
|
|
|
* @param string absolute or relative URI
|
|
|
|
*/
|
|
|
|
construct : function(uri) {
|
|
|
|
// need to set the default value here rather than in the properties map,
|
|
|
|
// or else we get some crazy global state breakage
|
|
|
|
this.setQueryParams({});
|
|
|
|
|
|
|
|
if (uri) {
|
|
|
|
// parse the url
|
|
|
|
var result = JX.URI._uriPattern.exec(uri);
|
|
|
|
|
|
|
|
// fallback to undefined because IE has weird behavior otherwise
|
|
|
|
this.setProtocol(result[1] || undefined);
|
|
|
|
this.setDomain(result[2] || undefined);
|
|
|
|
this.setPort(result[3] || undefined);
|
|
|
|
var path = result[4];
|
|
|
|
var query = result[5];
|
|
|
|
this.setFragment(result[6] || undefined);
|
|
|
|
|
|
|
|
// parse the path
|
|
|
|
this.setPath(path.charAt(0) == '/' ? path : '/' + path);
|
|
|
|
|
|
|
|
// parse the query data
|
2013-05-06 16:24:00 +02:00
|
|
|
if (query && query.length) {
|
|
|
|
var dict = {};
|
|
|
|
var parts = query.split('&');
|
|
|
|
for (var ii = 0; ii < parts.length; ii++) {
|
|
|
|
var part = parts[ii];
|
|
|
|
if (!part.length) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var pieces = part.split('=');
|
|
|
|
var name = pieces[0];
|
|
|
|
if (!name.length) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var value = pieces.slice(1).join('=') || '';
|
|
|
|
dict[JX.URI._decode(name)] = JX.URI._decode(value);
|
2013-01-22 19:32:26 +01:00
|
|
|
}
|
2013-05-06 16:24:00 +02:00
|
|
|
this.setQueryParams(dict);
|
2013-01-22 19:32:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
properties : {
|
|
|
|
protocol: undefined,
|
|
|
|
port: undefined,
|
|
|
|
path: undefined,
|
|
|
|
queryParams: undefined,
|
|
|
|
fragment: undefined,
|
|
|
|
querySerializer: undefined
|
|
|
|
},
|
|
|
|
|
|
|
|
members : {
|
|
|
|
_domain: undefined,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append and override query data values
|
|
|
|
* Remove a query key by setting it undefined
|
|
|
|
*
|
|
|
|
* @param map
|
|
|
|
* @return @{JX.URI} self
|
|
|
|
*/
|
|
|
|
addQueryParams : function(map) {
|
|
|
|
JX.copy(this.getQueryParams(), map);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a specific query parameter
|
|
|
|
* Remove a query key by setting it undefined
|
|
|
|
*
|
|
|
|
* @param string
|
|
|
|
* @param wild
|
|
|
|
* @return @{JX.URI} self
|
|
|
|
*/
|
|
|
|
setQueryParam : function(key, value) {
|
|
|
|
var map = {};
|
|
|
|
map[key] = value;
|
|
|
|
return this.addQueryParams(map);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the domain
|
|
|
|
*
|
|
|
|
* This function checks the domain name to ensure that it is safe for
|
|
|
|
* browser consumption.
|
|
|
|
*/
|
|
|
|
setDomain : function(domain) {
|
|
|
|
var re = new RegExp(
|
|
|
|
// For the bottom 128 code points, we use a strict whitelist of
|
|
|
|
// characters that are allowed by all browsers: -.0-9:A-Z[]_a-z
|
|
|
|
'[\\x00-\\x2c\\x2f\\x3b-\\x40\\x5c\\x5e\\x60\\x7b-\\x7f' +
|
|
|
|
// In IE, these chararacters cause problems when entity-encoded.
|
|
|
|
'\\uFDD0-\\uFDEF\\uFFF0-\\uFFFF' +
|
|
|
|
// In Safari, these characters terminate the hostname.
|
|
|
|
'\\u2047\\u2048\\uFE56\\uFE5F\\uFF03\\uFF0F\\uFF1F]');
|
|
|
|
if (re.test(domain)) {
|
|
|
|
JX.$E('JX.URI.setDomain(...): invalid domain specified.');
|
|
|
|
}
|
|
|
|
this._domain = domain;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
getDomain : function() {
|
|
|
|
return this._domain;
|
|
|
|
},
|
|
|
|
|
|
|
|
toString : function() {
|
|
|
|
if (__DEV__) {
|
|
|
|
if (this.getPath() && this.getPath().charAt(0) != '/') {
|
|
|
|
JX.$E(
|
|
|
|
'JX.URI.toString(): ' +
|
|
|
|
'Path does not begin with a "/" which means this URI will likely' +
|
|
|
|
'be malformed. Ensure any string passed to .setPath() leads "/"');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var str = '';
|
|
|
|
if (this.getProtocol()) {
|
|
|
|
str += this.getProtocol() + '://';
|
|
|
|
}
|
|
|
|
str += this.getDomain() || '';
|
|
|
|
|
|
|
|
if (this.getPort()) {
|
|
|
|
str += ':' + this.getPort();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a domain or a protocol, we need to provide '/' for the
|
|
|
|
// path. If we don't have either and also don't have a path, we can omit
|
|
|
|
// it to produce a partial URI without path information which begins
|
|
|
|
// with "?", "#", or is empty.
|
|
|
|
str += this.getPath() || (str ? '/' : '');
|
|
|
|
|
|
|
|
str += this._getQueryString();
|
|
|
|
if (this.getFragment()) {
|
|
|
|
str += '#' + this.getFragment();
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getQueryString : function() {
|
|
|
|
var str = (
|
|
|
|
this.getQuerySerializer() || JX.URI._defaultQuerySerializer
|
|
|
|
)(this.getQueryParams());
|
|
|
|
return str ? '?' + str : '';
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirect the browser to another page by changing the window location. If
|
|
|
|
* the URI is empty, reloads the current page.
|
|
|
|
*
|
|
|
|
* You can install a Stratcom listener for the 'go' event if you need to log
|
|
|
|
* or prevent redirects.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
go : function() {
|
|
|
|
var uri = this.toString();
|
|
|
|
if (JX.Stratcom.invoke('go', null, {uri: uri}).getPrevented()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!uri) {
|
|
|
|
// window.location.reload clears cache in Firefox.
|
|
|
|
uri = window.location.pathname + (window.location.query || '');
|
|
|
|
}
|
|
|
|
window.location = uri;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|