console
(function (window, name) {
if (name in window) {
throw new Error(["already '", name, "' in 'window'"].join(""));
}
function trim(str) {
switch (typeof str) {
case "undefined": return "";
case "boolean": return str ? "true" : "";
case "string": return str.replace(/^\s+|\s+$/g, "");
case "number": return str.toString();
case "object": return trim(str || "");
case "function": return trim(str());
}
}
function findIndex(str, chars) {
var r = str.length;
for (var i = 0; i < chars.length; i++) {
var j = str.indexOf(chars[i]);
if (j >= 0 && r > j)
r = j;
}
return r;
}
function backtrack(path) {
while (path !== (path = path.replace(/((\/|^)[^\/]+\/\.\.)|^\/\.\.|^\.\./g, ""))) {
}
return path;
}
function parseSearch(search) {
if (/^[^#][^?#]*[?#]/.test(search)) {
throw new Error('Argument format is invalid. from string : "' + search + '"');
}
search = trim(search);
if (search.charAt(0) === "?") {
search = search.substr(1);
}
var params = {};
if (search.charAt(0) === "&") {
params[""] = "";
search = search.substr(1);
}
if (search === "") return params;
var args = search.match(/[?]?[^=&]+=[^=&]+/g);
if (args == null) return params;
for (var j = 0; j < args.length; j++) {
try {
var kv = args[j].split("=");
setPropValue(params, decodeURIComponent(kv[0]), decodeURIComponent(kv[1]));
} catch (e) {
console.debug(e);
}
}
return params;
function setPropValue(object, name, value) {
var reg = /\[([0-9]*)\]|\[([^\[\]\.]+)\]|\.([^\[\]\.]+)|^([^\[\]\.]+)|\./g;
var p = {
get: function () { return object; },
set: function (value) { object[""] = (object[""] === undefined ? value : [object[""], value]); },
build: function () { return object; },
append: function (name) { return new prop(p, name); }
};
var m = null;
while (m = reg.exec(name)) {
if (m[1] != null) {
p = p.append(parseInt(m[1]) || 0);
} else {
var n = m[2] || m[3] || m[4];
if (n == null) {
object[name] = value;
return object;
}
p = p.append(n);
}
}
p.set(value);
return p.build();
}
function prop(ref, name) {
this.ref = ref;
this.name = name;
this.build = this.ref.build;
this.get = function () {
var object = this.ref.get();
if (object == null) {
return null;
}
return object[this.name];
}
this.set = function (value) {
var object = this.ref.get();
if (object == null) {
if (typeof this.name === "number") {
this.ref.set(object = []);
} else {
this.ref.set(object = {});
}
}
if (object[name] !== undefined) {
object[name] = [object[name], value];
} else {
object[name] = value;
}
}
this.append = function (name) {
return this.next = new prop(this, name);
}
}
}
function urlencoded(params) {
if (params == null || Object.keys(params).length === 0) {
return "";
}
var arr = [];
(function (value, name) {
switch (typeof value) {
case "undefined":
break;
case "boolean":
arr.push(name + "=" + value);
break;
case "string":
if (name == null || name === "") {
arr.push(encodeURIComponent(value));
} else {
arr.push(name + "=" + encodeURIComponent(value));
}
break;
case "number":
arr.push(name + "=" + value);
break;
case "object":
if (value instanceof Array) {
for (var index = 0; index < value.length; index++) {
arguments.callee(value[index] || "", name.length > 0 ? name + "%5B" + (index || "") + "%5D" : "");
}
return;
}
if (value !== null) {
var keys = Object.keys(value);
if (keys.length > 0) {
for (var index = 0; index < keys.length; index++) {
var key = encodeURIComponent(keys[index]);
if (key == null) {
key = "";
}
if (name.length != 0) {
key = name + "%5B" + key + "%5D";
}
var val = value[keys[index]];
if (val == null) {
val = "";
}
arguments.callee(val, key);
}
} else if (value instanceof Date) {
var date = value.getFullYear()
+ "-" + ("0" + (date.getMonth() + 1)).slice(-2)
+ "-" + ("0" + value.getDate()).slice(-2)
+ " " + ("0" + value.getHours()).slice(-2)
+ "%3A" + ("0" + value.getMinutes()).slice(-2)
+ "%3A" + ("0" + value.getSeconds()).slice(-2);
} else {
arguments.callee(value.toString(), name);
}
}
break;
case "function":
arguments.callee(value(), name);
break;
}
})(params, "");
return arr.join("&");
}
var token = new Object();
function Url(url) {
if (arguments[1] !== token) {
return new Url(url, token);
}
url = trim(url || window.location.href);
var _scheme = null;
var _domain = null;
var _path = null;
var _query = null;
var _anchor = null;
if (url.length > 0) {
var scheme = /^([^:/]+:\/\/|\/\/)/.exec(url) || "";
if (scheme && scheme.length > 0) {
_scheme = scheme[0];
url = url.substr(_scheme.length);
_domain = url.substr(0, findIndex(url, ["/", "\\", "?", "#"])).replace(/[\/\\]$/g, "");
if (_domain.indexOf(".") == -1) {
throw new Error('The "url" argument is invalid. because "domian" doesn\'t exist. from string : "' + arguments[0] + '"');
}
url = url.substr(_domain.length);
}
_path = url.substr(0, findIndex(url, ["?", "#"])).replace(/[?#]$/g, "");
url = url.substr(_path.length);
_path = _path.replace(/(\\|\/)+/g, "/");
_query = url.substr(0, findIndex(url, ["#"]));
url = url.substr(_query.length);
_anchor = url;
}
this.params = parseSearch(_query);
var me = this;
if (typeof Object.defineProperties === "function") {
var error = function (name, value) {
throw new Error('The "' + name + '" format is invalid. from string : "' + value + '"');
}
Object.defineProperties(this, {
scheme: {
get: function () { return _scheme; },
set: function (value) {
value = trim(value);
if (/^([a-z]+:)?\/\/$/.test(value) === false) {
error("scheme", value);
}
_scheme = value;
}
},
domain: {
get: function () { return _domain; },
set: function (value) {
value = trim(value);
if (/^([a-z0-9]([a-z0-9\-]+[a-z0-9])?\.)+[a-z0-9]+\/?$/.test(value) === false) {
error("domain", value);
}
if (value.slice(-1) === "/") {
value = value.slice(0, -1);
}
_domain = value;
}
},
path: {
get: function () {
var path = _path;
if (_scheme == null || _scheme === "" || _domain == null || _domain !== "") {
return path;
}
path = backtrack(path);
if (path.charAt(0) !== "/") {
path = "/" + path;
}
return path;
},
set: function (value) {
value = trim(value).replace(/([^\/]|^)[\/]{2,}/g, function (m) { return m.charAt(0) === ":" ? "://" : m.charAt(0) + "/"; });
if (/^\/?(([^\/?#]+)(\/|$))+$/.test(value) === false) {
error("path", value);
}
_path = value;
}
},
query: {
get: function () {
var str = urlencoded(me.params);
return (str === "") ? "" : "?" + str;
},
set: function (value) {
me.params = parseSearch(value);
}
},
anchor: {
get: function () { return (_anchor === "" || _anchor.charAt(0) === "#") ? _anchor : "#" + _anchor; },
set: function (value) { _anchor = trim(value); }
}
});
} else {
this.scheme = _scheme;
this.domain = _domain;
this.path = _path;
this.query = _query;
this.anchor = _anchor;
}
this.toString = function () {
var path = this.path;
if (_scheme == null || _scheme === "" || _domain == null || _domain !== "") {
path = backtrack(path);
if (path.charAt(0) !== "/") {
path = "/" + path;
}
}
return [this.scheme, this.domain, path, this.query, this.anchor === "#" ? "" : this.anchor].join("");
};
}
function combine(url1, url2) {
if (url1 == null || url1.length === 0) {
return url2 && new Url(url2).toString();
}
if (url2 == null || url2.length === 0) {
return new Url(url1).toString();
}
if (/^\.\.[^/]/.test(url2)) {
url2 = "./" + url2;
}
var u1 = new Url(url1);
var u2 = new Url(url2);
if (u2.scheme != null) {
if (u2.scheme === "//" && u1.scheme != null) {
u2.scheme = u1.scheme;
}
return u2.toString();
}
if (u2.path != null && u2.path.length > 0) {
if (u2.path.charAt(0) === "/") {
u1.path = u2.path;
}
else if (u2.path.substr(0, 2) === "./") {
u1.path = [u1.path, u2.path.substr(u1.path.slice(-1) === "/" ? 2 : 1)].join("");
}
else if (u1.path.slice(-1) === "/") {
u1.path = [u1.path, u2.path].join("");
} else if (u2.path.charAt(0) !== ".") {
u1.path = [u1.path.substr(0, u1.path.lastIndexOf("/")), "/", u2.path].join("");
}
}
if (u2.query == null || u2.query.length === 0) {
if (u2.anchor != null && u2.anchor.length > 0) {
u1.anchor = u2.anchor;
} else {
u1.query = "";
}
return u1.toString();
}
if (u2.query.substr(0, 2) === "?&") {
if (u2.query.length > 2) {
if (u1.query == null || u1.query.length === 0) {
u1.query = ["?", u2.query.substr(2)];
} else {
u1.query = [u1.query, u2.query.substr(u1.query.slice(-1) === "&" ? 2 : 1)].join("");
}
}
} else {
u1.query = u2.query;
}
if (u2.anchor != null && u2.anchor.length > 0) {
u1.anchor = u2.anchor;
}
return u1.toString();
}
Url.encoded = urlencoded;
Url.combine = function (url1, url2) {
if (arguments.length < 2) {
return arguments[0];
}
var _base = url1;
for (var i = 1; i < arguments.length; i++) {
_base = combine(_base, arguments[i]).toString();
}
return _base;
};
Url.parseSearch = parseSearch;
window[name] = Url;
if (typeof window.define === "function") {
window.define(name, [], function () { return Url; });
}
})(window, "Url");
var test = new Url("http://baidu.com/api/get?id=1");
console.log(test)
<pre>
<script>
var urls = [
"http://baidu.com/a/b", "c/d",
"http://baidu.com/a/b", "//c.com/d",
"http://baidu.com/a/b", "c/d",
"http://baidu.com/a/b/", "c/d",
"http://baidu.com/a/b", "/c/d",
"http://baidu.com/a/b", "../c/d",
"http://baidu.com/a/b", "./c/d",
"http://baidu.com/a/b", "../../../c/d",
"http://baidu.com/a/b", "c//////d",
"http://baidu.com/a/b?id=1#abc", "c/d?name=blqw",
"http://baidu.com/a/b?id=1#abc", "c/d?&name=blqw#def",
"http://baidu.com/a/b?id=1#abc", "..?id=2#"
];
function padRight(str,length) {
var space = " ";
return (str + space).substr(0, length);
}
for (var i = 0; i < urls.length; i += 2) {
var url = Url.combine(urls[i], urls[i + 1]);
document.writeln([padRight(urls[i],30), padRight(urls[i + 1],20), url].join(" | "));
}
</script>
</pre>