Skip to content

Instantly share code, notes, and snippets.

@draeton
Created January 24, 2012 16:53
Show Gist options
  • Save draeton/1671095 to your computer and use it in GitHub Desktop.
Save draeton/1671095 to your computer and use it in GitHub Desktop.
A URL parsing utility
/**
* ngbs.u.anchor - A URL parsing utility
*/
/*global ngbs */
var ngbs = ngbs || {};
ngbs.u = ngbs.u || {};
ngbs.u.anchor = (function (window, document, location) {
"use strict";
/**
* isObject
* Type check
* @private
* @return {Boolean}
*/
var isObject = function (o) {
return typeof o === "object" && o !== null;
};
/**
* isArray
* Type check
* @private
* @return {Boolean}
*/
var isArray = function (o) {
return Object.prototype.toString.call(o) === "[object Array]";
};
/**
* extend
* Object extend
* @private
* @return {Object}
*/
var extend = function (o, o2) {
var args = Array.prototype.slice.call(arguments, 2), i;
for (i in o2) {
if (o2.hasOwnProperty(i)) {
o[i] = o2[i];
}
}
if (args.length) {
args.unshift(o);
extend.apply(this, args);
}
return o;
};
/**
* append
* Append a key-value pair to an object
* @private
*/
var append = function (o, key, val) {
if (isObject(o) && o.hasOwnProperty(key)) {
if (isArray(o[key])) {
o[key].push(val);
} else {
o[key] = [o[key], val];
}
} else {
o[key] = val;
}
};
/**
* stringify
* Turn a key value pair into a string
* @private
* @return {String}
*/
var stringify = function (key, val, eq, sep) {
var s = "", i, l;
if (isArray(val)) {
for (i = 0, l = val.length; i < l; i++) {
s += sep + key + eq + val[i];
}
} else {
s += sep + key + eq + val;
}
return s;
};
/**
* update
* Update the value of a key
* @private
*/
var update = function (o, key, val) {
if (isObject(o)) {
if (typeof val === "undefined") {
delete o[key];
} else {
o[key] = val;
}
}
};
/**
* Private methods to add to the Anchor prototype
* @lends Anchor
*/
var privateMethods = {
/**
* toObject_
* Turn a search or hash into an object
* @private
* @param {String} type Either "search" or "hash"
* @return {Object}
*/
toObject_: function (type) {
var str, eq, sep, list, map, i, l, pair;
if (type === "search") {
str = this.anchor.search.replace(/^\?/, "");
eq = this.seq;
sep = this.ssp;
} else {
str = this.anchor.hash.replace(/^\#/, "");
eq = this.heq;
sep = this.hsp;
}
list = str.split(sep);
map = {};
for (i = 0, l = list.length; i < l; i++) {
pair = list[i].split(eq);
if (pair[0] !== "") {
append(map, pair[0], pair[1]);
}
}
return map;
},
/**
* toString_
* Turn an object into a str for search or hash
* @private
* @param {String} type Either "search" or "hash"
* @param {Object} map Key-value map of parameters
* @return {String}
*/
toString_: function (type, map) {
var str = "", eq, sep, i;
if (type === "search") {
eq = this.seq;
sep = this.ssp;
} else {
eq = this.heq;
sep = this.hsp;
}
for (i in map) {
if (map.hasOwnProperty(i)) {
str += stringify(i, map[i], eq, sep);
}
}
return str.replace(new RegExp("^\\" + sep), "");
},
/**
* getUrlVars_
* Get search and hash vars
* @private
* @param {String} type Either "search" or "hash"
* @return {Object}
*/
getUrlVars_: function (type) {
var vars;
vars = {
search: this.toObject_("search"),
hash: this.toObject_("hash")
};
return type ? vars[type] : vars;
},
/**
* setUrlVars_
* Set search and hash vars
* @private
* @param {String} type Either "search" or "hash"
* @param {Object} map Key-value map of parameters
* @return {Anchor}
*/
setUrlVars_: function (type, map) {
var vars = this.getUrlVars_(type),
i;
for (i in map) {
if (map.hasOwnProperty(i)) {
update(vars, i, map[i]);
}
}
return this.toString_(type, vars);
}
};
/**
* Public methods to add to the Anchor prototype
* @lends Anchor
*/
var publicMethods = {
/**
* getSearchVars
* Return a key-value object with the parameters in the URL search
* @return {Object}
*/
getSearchVars: function () {
return this.getUrlVars_("search");
},
/**
* getHashVars
* Return a key-value object with the parameters in the URL hash
* @return {Object}
*/
getHashVars: function () {
return this.getUrlVars_("hash");
},
/**
* getUrlVars
* Return combined url variables
* @return {Object}
*/
getUrlVars: function () {
var vars = this.getUrlVars_(),
search = vars.search,
hash = vars.hash,
combined = extend(search, hash);
return combined;
},
/**
* setSearchVars
* Sets parameters using a key-value object in the URL search; returns this
* @param {Object} map The key-value object
* @return {Anchor}
*/
setSearchVars: function (map) {
this.anchor.search = this.setUrlVars_("search", map);
return this;
},
/**
* setSearchVar
* Sets the key parameter to val in the URL search; returns this
* @param {String} key The name of the parameter
* @param {String} val The value of the parameter
* @return {Anchor}
*/
setSearchVar: function (key, val) {
var o = {};
o[key] = val;
return this.setSearchVars(o);
},
/**
* setHashVars
* Sets parameters using a key-value object in the URL hash; returns this
* @param {Object} map The key-value object
* @return {Anchor}
*/
setHashVars: function (map) {
this.anchor.hash = this.setUrlVars_("hash", map);
return this;
},
/**
* setHashVar
* Sets the key parameter to val in the URL hash; returns this
* @param {String} key The name of the parameter
* @param {String} val The value of the parameter
* @return {Anchor}
*/
setHashVar: function (key, val) {
var o = {};
o[key] = val;
return this.setHashVars(o);
},
/**
* delSearchVar
* Deletes the key parameter from the URL search; returns this
* @param {String} key The name of the parameter
* @return {Anchor}
*/
delSearchVar: function (key) {
return this.setSearchVar(key);
},
/**
* delHashVar
* Deletes the key parameter from the URL hash; returns this
* @param {String} key The name of the parameter
* @return {Anchor}
*/
delHashVar: function (key) {
return this.setHashVar(key);
},
/**
* toString
* Used for type coercion of Anchor instance to string
* @return {String}
*/
toString: function () {
return this.anchor.href;
}
};
/**
* nativeGetter
* Returns a get method that aliases a native anchor property
* @private
* @param {String} prop The property name
* @return {Function}
*/
var nativeGetter = function (prop) {
return function () {
return this.anchor[prop];
};
};
// Native get methods to add to the Anchor prototype
var nativeMethods = (function () {
var methods = {}, props, prop, i, l;
props = ["href", "protocol", "host", "hostname", "port", "pathname", "search", "hash"];
for (i = 0, l = props.length; i < l; i++) {
prop = props[i];
methods[prop] = nativeGetter(prop);
}
return methods;
}());
// regular expression URL test for protocol
var regexP = /^(http|https|ftp):/;
/**
* Anchor
* Constructor and prototype
* @constructor
* @param {String} href
* @param {String} [searchEq] Optional search equal operator; default of "="
* @param {String} [searchSep] Optional search separator operator; default of "&"
* @param {String} [hashEq] Optional hash equal operator; default of "="
* @param {String} [hashSep] Optional hash separator operator; default of "&"
* @return {Anchor}
*/
var Anchor = function (href, /* optional */ searchEq, searchSep, hashEq, hashSep) {
if (typeof href === "undefined" || href === "") {
throw new Error("The href argument must be defined and non-empty.");
}
this.anchor = this.a = document.createElement("a");
this.anchor.href = href;
// this forces the anchor to fill out the full path
if (!regexP.test(this.anchor.protocol)) {
this.anchor.protocol = location.protocol;
}
if (!this.anchor.hostname) {
this.anchor.hostname = location.hostname;
}
this.seq = searchEq || "=";
this.ssp = searchSep || "&";
this.heq = hashEq || "=";
this.hsp = hashSep || "&";
};
Anchor.prototype = extend({}, nativeMethods, privateMethods, publicMethods);
/**
* Anchor.factory
* Create an Anchor instance
* @return {Anchor}
*/
Anchor.factory = function (href, /* optional */ searchEq, searchSep, hashEq, hashSep) {
return new Anchor(href, searchEq, searchSep, hashEq, hashSep);
};
/**
* Anchor.getQuery
* Legacy method alias for ngbs.u.getQuery
* @param {String|Array|Undefined} key The parameter name, or an array of names, or undefined for all values
* @return {String|Object|Boolean}
*/
Anchor.factory.getQuery = function (key) {
var href = location.href,
t = new Anchor(href),
vars = t.getUrlVars(),
result = {},
l;
if (typeof key === "string" && vars[key]) {
return vars[key];
}
if (isArray(key)) {
l = key.length;
while (l--) {
result[key[l]] = false;
if (vars[key[l]]) {
result[key[l]] = vars[key[l]];
}
}
return result;
}
if (typeof key === "undefined") {
return vars;
}
return false;
};
// return the factory method for ngbs.u.anchor
return Anchor.factory;
}(window, document, location));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment