/**
* This is the Code 42 Software application extension to jQuery. jQuery must
* already be loaded This includes: auth : authentication url : URL manipulation
* routines safe : null-safe value routines date : date parsing routines nav :
* nav routines
*/
// //////////////////////////////////////////////////////////////////////////////
//
// Browser validation code - Jquery 1.2.x does *not* work in Safari 2 or
// older!!!
// alert($.browser.safari + $.browser.version);
//
// //////////////////////////////////////////////////////////////////////////////
if ($.browser.safari && $.browser.version < 522) {
top.location.href = "incompatible.html";
}
// //////////////////////////////////////////////////////////////////////////////
//
// Required native Javascript / JQuery extensions
//
// //////////////////////////////////////////////////////////////////////////////
// Date formatting
Date.prototype.format = function(format) {
var returnStr = '';
var replace = Date.replaceChars;
for ( var i = 0; i < format.length; i++) {
var curChar = format.charAt(i);
if (replace[curChar]) {
returnStr += replace[curChar].call(this);
} else {
returnStr += curChar;
}
}
return returnStr;
};
Date.replaceChars = {
shortMonths : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
'Sep', 'Oct', 'Nov', 'Dec' ],
longMonths : [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December' ],
shortDays : [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
longDays : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday' ],
d : function() {
return (this.getDate() < 10 ? '0' : '') + this.getDate();
},
D : function() {
return Date.replaceChars.shortDays[this.getDay()];
},
j : function() {
return this.getDate();
},
l : function() {
return Date.replaceChars.longDays[this.getDay()];
},
N : function() {
return this.getDay() + 1;
},
S : function() {
return (this.getDate() % 10 == 1 && this.getDate() != 11 ? 'st' : (this
.getDate() % 10 == 2
&& this.getDate() != 12 ? 'nd' : (this.getDate() % 10 == 3
&& this.getDate() != 13 ? 'rd' : 'th')));
},
w : function() {
return this.getDay();
},
z : function() {
return "Not Yet Supported";
},
W : function() {
return "Not Yet Supported";
},
F : function() {
return Date.replaceChars.longMonths[this.getMonth()];
},
m : function() {
return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1);
},
M : function() {
return Date.replaceChars.shortMonths[this.getMonth()];
},
n : function() {
return this.getMonth() + 1;
},
t : function() {
return "Not Yet Supported";
},
L : function() {
return "Not Yet Supported";
},
o : function() {
return "Not Supported";
},
Y : function() {
return this.getFullYear();
},
y : function() {
return ('' + this.getFullYear()).substr(2);
},
a : function() {
return this.getHours() < 12 ? 'am' : 'pm';
},
A : function() {
return this.getHours() < 12 ? 'AM' : 'PM';
},
B : function() {
return "Not Yet Supported";
},
g : function() {
return this.getHours() % 12 || 12;
},
G : function() {
return this.getHours();
},
h : function() {
return ((this.getHours() % 12 || 12) < 10 ? '0' : '')
+ (this.getHours() % 12 || 12);
},
H : function() {
return (this.getHours() < 10 ? '0' : '') + this.getHours();
},
i : function() {
return (this.getMinutes() < 10 ? '0' : '') + this.getMinutes();
},
s : function() {
return (this.getSeconds() < 10 ? '0' : '') + this.getSeconds();
},
e : function() {
return "Not Yet Supported";
},
I : function() {
return "Not Supported";
},
O : function() {
return (-this.getTimezoneOffset() < 0 ? '-' : '+')
+ (Math.abs(this.getTimezoneOffset() / 60) < 10 ? '0' : '')
+ (Math.abs(this.getTimezoneOffset() / 60)) + '00';
},
T : function() {
var m = this.getMonth();
this.setMonth(0);
var result = this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/, '$1');
this.setMonth(m);
return result;
},
Z : function() {
return -this.getTimezoneOffset() * 60;
},
c : function() {
return "Not Yet Supported";
},
r : function() {
return this.toString();
},
U : function() {
return this.getTime() / 1000;
}
};
// Parse ISO8601 formats to Dates
Date.parseISO8601 = function(dString) {
var regexp = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T|\s)?(\d\d)(:)?(\d\d)(:)?(\d\d)(\.\d+)?(Z|([+-])(\d\d)(:)?(\d\d))/;
var _date = new Date();
if (dString.toString().match(new RegExp(regexp))) {
var d = dString.match(new RegExp(regexp));
var offset = 0;
_date.setUTCDate(1);
_date.setUTCFullYear(parseInt(d[1], 10));
_date.setUTCMonth(parseInt(d[3], 10) - 1);
_date.setUTCDate(parseInt(d[5], 10));
_date.setUTCHours(parseInt(d[7], 10));
_date.setUTCMinutes(parseInt(d[9], 10));
_date.setUTCSeconds(parseInt(d[11], 10));
if (d[12])
_date.setUTCMilliseconds(parseFloat(d[12]) * 1000);
else
_date.setUTCMilliseconds(0);
if (d[13] != 'Z') {
offset = (d[15] * 60) + parseInt(d[17], 10);
offset *= ((d[14] == '-') ? -1 : 1);
_date.setTime(_date.getTime() - offset * 60 * 1000);
}
} else {
_date.setTime(Date.parse(dString));
}
return _date;
}
// Add fadeToggle to jQuery
$.fn.fadeToggle = function(speed, easing, callback) {
return this.animate( {
opacity : 'toggle'
}, speed, easing, callback);
};
// //////////////////////////////////////////////////////////////////////////////
//
// Start c42 library
//
// //////////////////////////////////////////////////////////////////////////////
if (!$.c42) {
$.c42 = {}; // Create the c42 plugin namespace if it is not already
}
// //////////////////////////////////////////////////////////////////////////////
//
// c42 Script Loader
// This is essential to loading scripts and making sure they load out of
// a common location
//
// //////////////////////////////////////////////////////////////////////////////
if (!$.c42.scriptHome) {
$.c42.scriptHome = "../common/js"; // Create the base url directory for
// loading scripts
}
// //////////////////////////////////////////////////////////////////////////////
//
// Error logging extensions
//
// $.c42.log : Log a message to the error console
//
// //////////////////////////////////////////////////////////////////////////////
$.c42.logError = function(msg) {
throw msg;
}
$.c42.log = function(msg) {
if (console && console.log) {
console.log(msg); // Firebug, Safari, etc
} else if (Components && Components.utils && Components.utils.reportError) {
Components.utils.reportError(msg); // Firefox sans Firebug
} else if (opera && opera.postError) {
opera.postError(msg); // Opera
} else {
throw msg;
}
}
/**
* Load a remote script on demand only if it hasn't been loaded. This is
* primarily used internally by the c42 framework for managing javascript
* library dependencies
*/
$.c42.load = function(url) {
if ($.c42._includeTable == undefined) {
$.c42._includeTable = {};
}
if ($.c42._includeTable[url] == undefined) {
if (url.indexOf("/") == -1) {
url = $.c42.scriptHome + "/" + url;
}
$.c42._includeTable[url] = true;
$.ajax( {
url : url,
type : "GET",
async : false,
dataType : "script",
error : function(request, status, error) {
// This used to be an alert, but we don't want non-developers
// being alarmed about it
$.c42.logError("Unable to load " + url);
}
})
}
}
$.c42.load("jquery.inc.pack.js"); // always include client side include
// //////////////////////////////////////////////////////////////////////////////
//
// Authentication extensions
// The following library extension handles standard Code 42 authentication
// via REST.
//
// $c42.authData - This is your authentication session data
// $c42.authToken() - Call this to perform token based authentication
// $c42.auth() - Call this to perform username/password authentication
// $c42.deauth() - Deauthorize a session
//
// //////////////////////////////////////////////////////////////////////////////
/**
* Authentication constants
*/
$.c42.AUTH_COOKIE_KEY = "user";
$.c42.AUTH_HEADER_TOKEN = "TOKEN ";
$.c42.AUTH_HEADER_BASIC = "BASIC ";
$.c42.AUTH_REST_URL = "/rest/auth";
/**
* You can check the authData from an authentication request using this object
*/
$.c42.authData = {
set : function(data) {
// Set the authentication information
this.username = data.username;
this.userId = data.userId;
this.authToken = data.authToken;
this.orgId = data.orgId;
this.viewAllOrgs = data.viewAllOrgs;
this.sysAdmin = data.sysAdmin;
this.orgAdmin = data.orgAdmin;
this.orgManager = data.orgManager;
this.partner = data.partner;
// $.c42.load("jquery.c42.inspect.js");
// $.c42.inspect.alert(this, 7);
}
};
/**
* Check if session is authorized. If we have cookie authentication enabled,
* then we can check for the C42 authentication token. If the session is not
* authenticated, we redirect to the unauthorized URL.
*
* This method is synchronous
*
* @param unathorizedURL -
* URL to redirect to if session is unauthorized
* @param isAdmin42 -
* true if we need to verify that in the CrashPlan org only admins
* are allowed
*/
$.c42.authToken = function(unauthorizedURL, isAdmin42) {
$.c42.load('jquery.cookie.js'); // need cookie lib
if (!unauthorizedURL) {
throw new Error("c42.auth() unauthorizedURL undefined");
}
// Check if we have a valid cookie - if not, redirect to unauthorizedURL
var key = $.cookie($.c42.AUTH_COOKIE_KEY);
if (key) {
var auth = $.c42.AUTH_HEADER_TOKEN + key;
$.ajax( {
url : $.c42.AUTH_REST_URL,
type : "GET",
async : false,
dataType : "json",
contentType : "application/json",
beforeSend : function(req) {
$.c42.setAuthHeader(req, auth);
},
success : function(data, textStatus) {
$.c42.authData.set(data);
if (isAdmin42 && $.c42.authData.orgId == 42
&& !$.c42.authData.orgAdmin) {
document.location.href = unauthorizedURL; // unauthorized
// for admin42 requirement
}
},
error : function(request, status, error) {
$.c42.logError("Unauthorized " + request + ":" + status + ":"
+ error);
document.location.href = unauthorizedURL; // unauthorized
}
})
} else { // no cookie set - go to unauthorized URL
document.location.href = unauthorizedURL; // unauthorized
}
};
/**
* Attempt to authenticate a username and password. Asynch. Requires callback
* functions.
*
* @param username
* Username to auth
* @param password
* @param successCb
* Callback function if auth is successful
* @param errorCb
* Callback function if auth fails
*/
$.c42.auth = function(username, password, successCb, errorCb, isAdmin42) {
$.c42.load('base64.js'); // include base64 library
$.c42.load('jquery.cookie.js'); // need cookie lib
var auth = username + ":" + password;
auth = $.c42.AUTH_HEADER_BASIC + Base64.encode(auth);
errorCallback = function(xhr, status, error) {
if (errorCb) {
errorCb(xhr, status, error);
return;
}
if (xhr.status == 503) {
if (xhr.getResponseHeader("Retry-After")) {
alert("LDAP lookup is slow. Try again in a minute.");
} else {
alert("Login service is unavailable. (503)");
}
} else if (xhr.status == 401) {
alert("Your email address and/or password is invalid. Please try again.");
} else if (xhr.status == 404) {
alert("Login service is unavailable. (404)");
} else {
alert('Error during login: statusCode: ' + xhr.status);
}
};
$.ajax( {
url : $.c42.AUTH_REST_URL,
type : "GET",
dataType : "json",
contentType : "application/json",
beforeSend : function(req) {
$.c42.setAuthHeader(req, auth);
},
success : function(data, textStatus) {
// Set the authentication information
$.c42.authData.set(data);
if (isAdmin42 && !$.c42.authData.orgAdmin
&& $.c42.authData.orgId == 42) {
errorCallback();
return;
}
// Set the authentication cookie
$.cookie($.c42.AUTH_COOKIE_KEY, data.authToken, $.c42
.authCookieOptions()); // set the cookie for auto-auth
if (successCb) {
successCb(data, textStatus);
}
},
error : errorCallback
});
};
/**
* De-authorize / Sign out! Calling this will remove the authorization cookie.
* and then redirect user to an unauthorized landing url
*/
$.c42.deauth = function(unauthorizedURL) {
$.c42.load('jquery.cookie.js'); // need cookie lib
// Set the cookie to null and set to expire in the past (Firefox seems to need the expire portion).
var date = new Date();
date.setTime(0); // Jan 1, 1970
$.cookie($.c42.AUTH_COOKIE_KEY, null, {path:'/', expires:date});
if (unauthorizedURL != undefined) {
document.location.href = unauthorizedURL; // redirect to unauthorized
// url
}
return false;
}
/**
* Provide this as the 'beforeSend' function to auto-login on REST services
*
* @param req
* The request object
*/
$.c42.authBeforeSend = function(req) {
$.c42.load('jquery.cookie.js'); // need cookie lib
var authToken = $.cookie($.c42.AUTH_COOKIE_KEY);
if (authToken) {
var auth = $.c42.AUTH_HEADER_TOKEN + authToken;
$.c42.setAuthHeader(req, auth);
}
}
/**
* Private method for setting the Authorization header
*/
$.c42.setAuthHeader = function(req, auth) {
req.setRequestHeader("Authorization-Challenge", "false"); // no challenge!
if ((navigator.userAgent.match(/iPhone/i))
|| (navigator.userAgent.match(/iPod/i))) {
// Safari on iPhone/iPod barfs on the "Authorization" header so use
// "Auth" instead.
// http://www.devilx.net/2009/10/23/iphone-safari-and-xmlhttprequest-authorization-headers
req.setRequestHeader("Auth", auth);
} else {
req.setRequestHeader("Authorization", auth);
}
}
/**
* Provides options so the cookie expires 30 minutes from now
*/
$.c42.authCookieOptions = function() {
var date = new Date();
date.setTime(date.getTime() + (30 * 60 * 1000)/* 30 minutes */);
$.c42.AUTH_COOKIE_OPTIONS = {
path : '/',
expires : date
};
// alert("Expires: " + date);
return $.c42.AUTH_COOKIE_OPTIONS;
}
/**
* Refresh the expiration date on the auth cookie for 30 minutes from now
*/
$.c42.authCookieRefresh = function() {
$.c42.load('jquery.cookie.js'); // need cookie lib
var authToken = $.cookie($.c42.AUTH_COOKIE_KEY);
if (authToken) {
var options = $.c42.authCookieOptions();
// alert("Auth cookie will expire at: " + options.expires);
$.cookie($.c42.AUTH_COOKIE_KEY, authToken, options);
}
}
// Refresh the cookie expire time every time we load this script
$.c42.authCookieRefresh();
// //////////////////////////////////////////////////////////////////////////////
//
// AJAX Transport extensions
//
// $.c42.load : Load a script ONCE and only ONCE
// $.c42.postJSON : Post object as JSON
//
// //////////////////////////////////////////////////////////////////////////////
/**
* Post an object as a JSON payload to a remote URL
*
* @param obj
* The object containing custom Ajax options and 'data' to JSONize
*/
$.c42.postJSON = function(obj) {
$.c42.load('json2.js'); // make sure that we load the correct json lib
var block = {}
block.url = obj.url
block.success = obj.success
if (obj.error) {
block.error = obj.error
}
block.timeout = 30000 /* Default value of 30000ms = 30 sec */
if (obj.timeout) {
block.timeout = obj.timeout
}
block.type = "POST"
block.contentType = "application/json"
block.dataType = "json"
// Note the use of an external JSON parser here. If JQuery is passed a raw
// data object it will attempt to
// "serialize" it, which basically amounts to an encoding resembling a GET
// query string. There wasn't any
// obvious way to make it use JSON for the encoding, so we had to import an
// external processor. The "json"
// string in the syscall indicates the expected return type and not the
// expected encoding for the outgoing
// request.
block.data = JSON.stringify(obj.data)
$.ajax(block)
}
/**
* Post an object as a JSON payload to a remote URL
*
* @param obj
* The object containing custom Ajax options and 'data' to JSONize
*/
$.c42.putJSON = function(obj) {
$.c42.load('json2.js'); // make sure that we load the correct json lib
var block = {}
block.url = obj.url
block.success = obj.success
if (obj.error) {
block.error = obj.error
}
block.timeout = 30000 /* Default value of 30000ms = 30 sec */
if (obj.timeout) {
block.timeout = obj.timeout
}
block.type = "PUT"
block.contentType = "application/json"
block.dataType = "json"
// Note the use of an external JSON parser here. If JQuery is passed a raw
// data object it will attempt to
// "serialize" it, which basically amounts to an encoding resembling a GET
// query string. There wasn't any
// obvious way to make it use JSON for the encoding, so we had to import an
// external processor. The "json"
// string in the syscall indicates the expected return type and not the
// expected encoding for the outgoing
// request.
block.data = JSON.stringify(obj.data)
$.ajax(block)
}
/**
* Encapsulate all of the values in a form in a single object
*
* @param id
* The id of the form to select from
* @return data object containing element id,value pairs
*/
$.c42.getFormData = function(id) {
// Select all input or select elements underneath the named form
var selector = "form#" + id + " input,select,textarea";
var data = {};
$(selector).each(function(i) {
var name = this.id
if (!name) {
name = $(this).attr('name');
}
if (name) {
data[name] = $(this).val();
}
});
return data
}
// //////////////////////////////////////////////////////////////////////////////
//
// URL manipulation and redirect extensions
// The following library extension handles standard Code 42 URL manipulation
//
// $.c42.redirect
// $.c42.redirectSSL
// $.c42.getResourceName
// //////////////////////////////////////////////////////////////////////////////
/**
* Redirect to a relative URL on a new host IF the required hostname is present
*/
$.c42.redirect = function(requiredHostname, targetHostname) {
if (document.location.host.indexOf(requiredHostname) != -1) {
var l = document.location;
var url = l.protocol + "//" + targetHostname + l.pathname;
if (l.hash)
url += l.hash;
if (l.search)
url += l.search;
document.location = url;
}
}
/**
* Redirect to an HTTPS url if you match the current hostname
*
* @param requiredHostname
* @return
*/
$.c42.redirectSSL = function(requiredHostname, targetHostname) {
var l = document.location;
if (l.host.indexOf(requiredHostname) != -1) {
if (l.protocol.indexOf("http:") != -1) {
var host = targetHostname == undefined ? l.host : targetHostname;
var url = "https://" + host + l.pathname;
if (l.hash)
url += l.hash;
if (l.search)
url += l.search;
document.location = url;
}
}
}
/**
* Redirect to an HTTPS url if you match the current hostname TODO - Refactor to
* take in a protocol parameter switch
*
* @param requiredHostname
* @return
*/
$.c42.redirectNONSSL = function(requiredHostname, targetHostname) {
var l = document.location;
if (l.host.indexOf(requiredHostname) != -1) {
if (l.protocol.indexOf("https:") != -1) {
var host = targetHostname == undefined ? l.host : targetHostname;
var url = "http://" + host + l.pathname;
if (l.hash)
url += l.hash;
if (l.search)
url += l.search;
document.location = url;
}
}
}
/**
* Returns just the resource portion of the URL
*/
$.c42.getResourceName = function() {
var file_name = document.location.href;
var end = (file_name.indexOf("?") == -1) ? file_name.length : file_name
.indexOf("?");
end = (file_name.indexOf("#") == -1) ? end : file_name.indexOf("#"); // chop
// anchors
// too
return file_name.substring(file_name.lastIndexOf("/") + 1, end);
}
// //////////////////////////////////////////////////////////////////////////////
//
// This is the Code 42 Software application extension to jQuery
// for safe string operations. i.e. if the string is null or undefined
//
// //////////////////////////////////////////////////////////////////////////////
$.c42.safe = function(value) {
return value ? value : " ";
}
$.c42.safeTimestamp = function(ts) {
return ts ? ts.substring(0, 16).replace("T", " ") : " ";
}
$.c42.safeDate = function(ts, formatStr) {
if (ts) {
if (!formatStr)
formatStr = "m/d/Y"; // "m/d/Y h:iA";
var d = Date.parseISO8601(ts);
return d.format(formatStr);
}
return " ";
}
$.c42.safeDateTime = function(ts) {
return $.c42.safeDate(ts, "m/d/Y h:iA");
}
// //////////////////////////////////////////////////////////////////////////////
//
// This is the Code 42 Software application extension to jQuery
// for status. Requires base.css and std.css
//
// //////////////////////////////////////////////////////////////////////////////
/**
* Sets a normal informational message
*/
$.c42.setStatus = function(text) {
$("#status").removeClass("status-error");
$.c42._setStatus(text);
}
/**
* Sets an error message
*/
$.c42.setStatusError = function(text) {
$("#status").addClass("status-error");
$.c42._setStatus(text, true);
}
/**
* Close the status element
*/
$.c42.closeStatus = function() {
$("#status").slideUp("fast");
}
/**
* Private helper function for actually setting the status text Hides the status
* after a few seconds by default.
*/
$.c42._setStatus = function(text, isError) {
// Display the status widget
if ($("#status").length > 0) {
$("#status").html(
text + "
Click to close");
if (!$("#status").is(":visible")) {
$("#status").slideDown("fast");
}
}
}
/**
* Initialize status element if it exists Attach a click handler to close the
* status
*/
$(document).ready(function() {
if ($("#status").length > 0) { // status element exists
$("#status").click($.c42.closeStatus);
}
});
// //////////////////////////////////////////////////////////////////////////////
//
// This is the Code 42 Software application extension to jQuery
// for page navigation controls. Requires base.css and std.css
//
// //////////////////////////////////////////////////////////////////////////////
$.c42.nav = {};
/**
* Selected nav items are set by matching the start of the resource with the nav
* item. For example, anything starting with 'download' will match the nav item
* 'download'.
*/
$.c42.nav.init = function() {
var resourceName = $.c42.getResourceName();
$("#nav").find("a").each(function() {
// Toggle selected state
if (resourceName.indexOf(this.id) == 0) { // starts with id anchor
$("#" + this.id).parent("li").addClass("selected");
}
});
// Assign click, hover support
$("#nav > ul > li").each(function() {
var anchor = $(this).find("a:first");
$.c42.nav.setClick(this, anchor);
$.c42.nav.setHover(this);
});
// Assign home click support
$.c42.nav.setClick($("#home"));
// Customize search box for safari
if ($.browser.safari) {
$("#search-input").attr("results", "5");
}
}
/**
* Selected menu items are set by matching resource
*/
$.c42.nav.menuInit = function() {
var resourceName = $.c42.getResourceName();
var el = $(".menu").find("a[href='" + resourceName + "']:first");
if (el) { // match the element whose href is the current document
el.parent("li").addClass("selected");
}
// Add hover support
// Add click 'anywhere support
$(".menu").find("li").each(function() {
var anchor = $(this).find("a:first");
$(this).hover(function() {
$(this).addClass("hover");
anchor.addClass("hover");
}, function() {
$(this).removeClass("hover");
anchor.removeClass("hover");
});
if (!$(this).hasClass("ignore")) {
$.c42.nav.setClick(this, anchor);
}
});
}
/**
* Set an item click to the same href as an anchor
*/
$.c42.nav.setClick = function(item, anchor) {
var href = anchor == undefined ? "index.html" : $(anchor).attr("href");
$(item).click(function() {
top.location.href = href;
});
}
/**
* Assign a hover over effect for list items
*/
$.c42.nav.setHover = function(item) {
if (!$(item).hasClass("selected")) {
$(item).hover(function() {
$(item).addClass("hover");
}, function() {
$(item).removeClass("hover");
});
}
}
/**
* Always returns an integer object (or whatever the default is).
* @param name - query param name
* @param defValue - default value
*/
$.c42.getQueryInt = function(name, defValue) {
$.c42.load('jquery.query.js');
var value = $.query.get(name);
if (value && value != true && /\d/.test(value)) {
return parseInt(value);
}
return defValue;
}
/**
* Always returns a String object (or whatever the default is).
* @param name - query param name
* @param defValue - default value
* @param allowEmpty - if value is empty, return empty instead of the default
*/
$.c42.getQueryString = function(name, defValue, allowEmpty) {
$.c42.load('jquery.query.js');
var value = $.query.get(name);
if (value == true) {
// The query param value is an empty string
return allowEmpty ? '' : defValue;
}
if (value) {
return value;
}
return defValue;
}
/**
* Always returns a boolean object (or whatever the default is).
* @param name - query param name
* @param defValue - default value
*/
$.c42.getQueryBoolean = function(name, defValue) {
$.c42.load('jquery.query.js');
var value = $.query.get(name);
if (value === true) {
// The query param value is an empty string
return defValue;
}
if (value) {
// no type conversions in this check.
return value === 'true';
}
return defValue;
}