window.utility = {};
/* Fix for isNaN not retuning 'true' when the object is an array */
(function() {
	var oldNaN = window.isNaN;
	window.isNaN = function(i) {
		if(typeof i == 'object' && i instanceof Array)
			return true;
		return oldNaN(i);
	};
})();

/* HACK FOR GOOGLE ANALYTICS - When we go to jquery 1.2 we can use a real plugin... */
(function() {
	$(function() {
		/* Don't request analytics unless @ openid.trustbearer.com
		   Also, don't use analytics to store referrer (relying party) on the login page. */
		if(document.location.hostname != "openid.trustbearer.com" || document.location.pathname.match(/login\.html/)) return;
		var gaURL = (document.location.protocol == 'https:') ? 'https://ssl.' : 'http://www.';
		gaURL += 'google-analytics.com/ga.js';
		$("body").append($.create('script', {type: 'application/javascript', src: gaURL}));
		var interval = setInterval(function() {
			if(!window._gat) return;
			clearInterval(interval);
			var tracker = _gat._getTracker("UA-996436-3");
			tracker._initData();
			tracker._trackPageview();
		});
	});
})();

(function() {
	var defaultTbError = tbError;
	function alterError(e) {
		if(e.longform.match(/already enrolled/i)) {
			e.shortform = 'CertificateAlreadyEnrolled';
		} else if(e.longform.match(/not.*enrolled/i)) {
			e.shortform = 'CertificateNotEnrolled';
		} else if(e.longform.match(/cert.*not.*trusted/i)) {
			e.shortform = 'CertificateNotTrusted';
		} else if(e.longform.match(/key.*mismatch/i)) {
			e.shortform = 'KeyMismatch';
		} else if(e.longform.match(/username.*registered/i)) {
			e.shortform = 'UserExists';
		} else if(e.longform.match(/(bad|not).*config/i)) {
			e.shortform = 'ApplicationNotConfigured';
		} else if(e.longform.match(/cert.*size/i)) {
			e.shortform = 'UnsupportedCertificateSize';
		}
	}
	window.tbError = function(e, tokenId) {
		if(e.component == 'TBVerifyKey')
			alterError(e);
		/* QUICK CHECK FOR TBLIVE OBJECT EXISTENCE */
		var killObject;
		var ret = defaultTbError(e, tokenId);
		return ret;
	};
	openIdVerifyError = function(e, suggestedComponent, tokenId) {
		var component;
		if(!e.component || e.component == "none")
			component = suggestedComponent;
		return tbUtil.defaultExceptionHandler(e, component, tokenId);
	}
 })();
 
(function() {
    /* Revived form old tblive */
    function tbShowStacked(level, width, height, contents, force, displayTag, iframe) {
        return tbShowDiv(width, height, contents, force, displayTag, iframe, level);
    }
    
	function tbShowStackedProgressDialog(content, level) {
		//return -1; // Currently disabled... always return something...
		var progressDialog = $.create('img', {src: xmlrpc.indicator_image});
		var div = $(
		    "<div class='center'>" +
		        "<p>Loading &hellip; this may take a moment</p>" +
		        "<img src='" + xmlrpc.indicator_image + "' />" +
		    "</div>"
		);
		progressDialog = tbApplyTemplate(div);
		level = level !== null && level || -1;
		return tbShowStacked(level, 421, 189, progressDialog, 'Progress');
	}
	window.tbShowStackedProgressDialog = tbShowStackedProgressDialog;

	function getSplitUrlForTemplate(url) {
		/* Splits the URL at word boundaries that are follows by a / # ? or & .. doesn't remove it from the string */
		var displayURLItem = url.split(/\b(?=[\/#?&])/);
		if(!$.browser.msie) { // If ! IE use zwsp (&#8203;) \u200B to split
			displayURLItem = [ displayURLItem.join('\u200B') ];
		} else {
			var newItem = [];
			for(var i = 0, len = displayURLItem.length; i < len; i++) {
				newItem.push(displayURLItem[i]);
				newItem.push('wbr');
				newItem.push([]);
			}
			displayURLItem = newItem;
		}
		return displayURLItem;
	}
	window.getSplitUrlForTemplate = getSplitUrlForTemplate;
	function loadNewPage(url, replace) {
		var displayURLItem = getSplitUrlForTemplate(url);
		var additional = $.create('span', [
			'Loading: ',
			'a', {href: url, 'class':'pageName'}, displayURLItem
		]);
		if(replace)
			window.location.replace(url);
		else
			window.location = url;
	}
	window.loadNewPage = loadNewPage;
})();

(function() {
	var queryCache = {};
	function populateQueryCache(query) {
		var vars = query.split('&');
		var data = {};
		for(var i=0;i<vars.length;i++) {
			var pair = vars[i].split('=');
			/* REPLACE + with SPACE DUE TO ENCODING SPEC */
			if(pair[1]) {
				pair[1] = pair[1].replace(/[+]/g, ' ');
			}
			data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
		}
		queryCache[query] = data;
	}
	function getQuery(name) {
		var query = window.location.search.substring(1);
		if(!queryCache[query])
			populateQueryCache(query);
		return queryCache[query][name];
	}
	window.getQuery = getQuery;
})();

// Email address regex validator given by RFC-822.
(function() {
	var sQtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
	var sDtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
	var sAtom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
	var sQuotedPair = '\\x5c[\\x00-\\x7f]';
	var sDomainLiteral = '\\x5b(?:' + sDtext + '|' + sQuotedPair + ')*\\x5d';
	var sQuotedString = '\\x22(?:' + sQtext + '|' + sQuotedPair + ')*\\x22';
	var sDomain_ref = sAtom;
	var sSubDomain = '(?:' + sDomain_ref + '|' + sDomainLiteral + ')';
	var sWord = '(?:' + sAtom + '|' + sQuotedString + ')';
	var sDomain = sSubDomain + '(?:\\x2e' + sSubDomain + ')*';
	var sLocalPart = sWord + '(?:\\x2e' + sWord + ')*';
	var sAddrSpec = sLocalPart + '\\x40' + sDomain; // complete RFC822 email address spec
	var sValidEmail = '^' + sAddrSpec + '$'; // as whole string
	window.emailRegex = new RegExp(sValidEmail);
})();

/**
 * Helper function to easily store a cookie.
 *
 * @param key The name of the cookie.
 * @param value Data to store in the cookie.
 * @param expires Expiration time as a Date object.
 * @param path Domain path for access to this cookie.
 */
function setCookie(key, value, expires, path) {
	if(!expires) {
		expires = new Date();
		expires.setMonth(expires.getMonth() + 11);
	}
	path = path || "/";
	expires = expires.toGMTString();
	key = encodeURIComponent(key);
	value = encodeURIComponent(value);
	document.cookie = key + '=' + value + ';expires=' + expires + ";path=" + path;
}

/**
 * Helper function to easily retrieve a cookie.
 *
 * @param key The name of the cookie.
 */
function getCookie(key) {
	var ret = document.cookie.match(encodeURIComponent(key) + "=([^;]*)");
	ret = ret && ret[1];
	if(ret) return decodeURIComponent(ret);
}

/**
 * Helper function to easily delete a cookie.
 *
 * @param key The name of the cookie.
 */
function deleteCookie(key) {
	setCookie(key, "", new Date(0));
}
$(function() {
	/* TEMPORARY UNTIL HOOKUP ESTABLISHED */
	var div = $("#signin > .signinArea");
	var cancelLocation = getQuery('cancelURL');
	var defaultOptions = {
		application: 'openid',
		rawReturn: true
	};
    function getCurrentUser(next, data, state) {
        // get the current user name from the PHP session
        $.post("getCurrentUser.php", {}, function (user) {
            next(user);
        }, "json");
    }
    /* Pulled in from webkit */
    function getFriendlyName(tokenId) {
        if (tokenId.match("Virtual")) {
            try {
                var t = tb.connect(tokenId);
                return t.getName();
            } catch(e) {}
        }
        return tokenId.match(/^(.*?)[ 0-9]*$/)[1];
    }
    function enrollSecondaryToken(next, user, state) {
        var options = state.options;
        // session expired on the server; failure
        if(!user || !user.result) {
            if(state.progressID) tbHideDiv(state.progressID);
            // FIXME
            // Insert user-friendly error message here.
            alert("Couldn't link second token, sorry.");
            return;
        }
        // set to enroll under that existing user name
        options.checkedEnroll = true;
        options.autoEnroll = true;
        options.enroll = {username: user.result};
        options.autoEnrollOptions = {
            adminAuth: {
                skipConfirmation : true
            },
            request: {
                subject: "CN=" + window.savedUsername,
                issuer: "CN=" + window.savedUsername
            }
        };
        // enroll with the existing user name
        tbVerifyKey(state.tokenId, options, function(ret) {
            next(ret);
        });
    }
    function handleSecondaryVerifyKeyResults(next, ret, state) {
        if(state.progressID) tbHideDiv(state.progressID);
        tbCall("TokenDisconnect", state.tokenId);
        if (!ret) {
            // FIXME
            // Insert user-friendly error message here.
            alert("Couldn't link second token, sorry.");
            return;
        }
        // XXX
        // This isn't very beneficial to the user.
        // Are there any conceivable non-error situations where this could happen?
        // If so, the error messages should be better.
        if(ret.error)
            return openIdVerifyError(ret.error, "TBVerifyKey", state.tokenId);
        if (!ret.result || !ret.result.key) {
            alert("Unable to get key for the session.");
            return;
        }
        next(ret.result.key);
    }
    function linkEnrolledSecondaryToken(next, session, state) {
        // send the new TB Access session ID up to the server
        // so that it can add the current PHP session user data to the TB Access session
        /* Grab TokenName from connected device */
        var tokenNameBase = getFriendlyName(state.tokenId);
        var tokenName;
        $.post("tokenMgmt.php", {"operation": "list"}, function (ret) {
            var tokens = ret && ret.result;
            if (!tokens) {
                window.generateWarning("Failed to retrieve token list in order to create a unique name");
                /* at this pt, things fall apart since user enrolled but no token linked */
                return;
            }
            var num = 0;
            while (true) {
                num++;
                tokenName = tokenNameBase + " " + num;
                /* Scan through array, if match found, construct a new name */
                if (-1 == tokens.indexOf(tokenName))
                    break;
            }
            state.tokenName = tokenName;
            $.post("generateLinkSession.php", {key: session, tokenName: tokenName}, function (ret) {
                next(ret);
            }, "json");
        }, "json");
    }
    function handleLinkResults(next, ret, state) {
        if (!ret || !ret.result) {
            // invalid return value from the server
            alert("Unexpected or malformed result from generateLinkSession.php script.\n" +
                  "Unable to retrieve current session data for multiple linking.");
            return;
        }
        if (ret.result != "success") {
            if (ret.error) {
                alert("Server could not link this token to your existing OpenID account:\n" + ret.error);
            } else {
                alert("Unspecified error on the server side.  Please contact TrustBearer support.");
            }
            return;
        }
        // successfully enrolled secondary token
        $(".tokenName").val("");
        var tokenName = state.tokenName;
        makeTokenRow(tokenName);
        $(".userEnrolled").show().text('successfully linked new token "' + getTokenNameDisplay(tokenName) + '"');
        window.tokens_all_gone = false;
    }
    var linkSecondaryAccountChain = [
        getCurrentUser,
        enrollSecondaryToken,
        handleSecondaryVerifyKeyResults,
        linkEnrolledSecondaryToken,
        handleLinkResults
    ];
    /* Link a secondary account */
    function linkSecondaryAccount(tokenId, options, progressID) {
        tbUtil.ChainCall(linkSecondaryAccountChain, null, {tokenId: tokenId, options:options, progressID: progressID});
    }
    function enrollToken(next, data, state) {
        var options = state.options;
        // linking the first token for this user
        options.autoEnroll = true;
        options.autoEnrollOptions = {
            adminAuth: {
                skipConfirmation : true
            },
            request: {
                subject: "CN=" + window.savedUsername,
                issuer: "CN=" + window.savedUsername
            }
        };
        tbVerifyKey(state.tokenId, options, function(ret) {
            next(ret);
        });
    }
    function handleEnrollResults(next, ret, state) {
        if(state.progressID) tbHideDiv(state.progressID);
        tbCall("TokenDisconnect", state.tokenId);
        if(!ret) {   /* User cancelled... */
            return;
        }
        if(ret.error) {
            return openIdVerifyError(ret.error, "TBVerifyKey", state.tokenId);
        }
        if(!ret.result || !ret.result.key) { /* No key retreived ?? */
            alert("Failed to obtain a session key");
            return;
        }
        /* POST TO PAGE TO UNLOCK OpenID */
        $.post("applySessionKey.php", {key: ret.result.key}, function(ret) {
            next(ret);
        }, "json");
    }
    function handleNewSessionResults(next, ret, state) {
        function newlink(url) {
            return ['a', {href: url}, [ url ] ];
        }
        /* After this post completes to make sure it goes through */
        if(ret && ret.result
            && getQuery('userexpected')
            && !getQuery('userexpected').match(/auth\/2\.0\/identifier_select/)
            && getQuery('userexpected').toLowerCase() != ret.result.url.toLowerCase()
            && getQuery('userexpected').toLowerCase() != ret.result.sslurl.toLowerCase()) {
            $("#container").find('.error').remove().end().append($.create('div', {'class':'error'}, [
                "Error: Token associated with: ",
                'span', newlink(ret.result.url),
                'br', [],
                "and ", 'span', newlink(ret.result.sslurl),
                'br', [],
                "Expecting: ", 'span', newlink(getQuery('userexpected')),
                'br', []
            ]));
            return;
        }
        if (state.options.enroll && !(document.location + "").match(/signin=true/)) {
            window.addToken(state.options);
        } else {
            /* Query data overrides dashboard */
            var returnTo = getQuery('resumeURL') || 'dashboard.html';
            if(state.options.enroll) {
                returnTo = 'dashboard.html?enrolled=true';
            }
            loadNewPage(returnTo);
        }
    }
    var linkPrimaryAccountChain = [
        enrollToken,
        handleEnrollResults,
        handleNewSessionResults
    ];
    function linkPrimaryAccount(tokenId, options, progressID) {
        tbUtil.ChainCall(linkPrimaryAccountChain, null, {tokenId: tokenId, options:options, progressID: progressID});
    }
	var progressID;
	/*
	 * Generalised verification function.
	 *
	 * Taylor modified this function to do multi-token linking as well.
	 * The following option fields were added:
	 *     .doSecondaryLink :: boolean - set to TRUE if linking another token
	 */
    function performVerifyOp(options) {
        var tokenId = options.token;
        var cert = options.cert;
        if(!tokenId) {
            /* BAIL? */
            return;
        }
        progressID = tbShowStackedProgressDialog();
        try {
            /*
            tbCall("TokenConnect", tokenId);
            */
            options = $.extend({
                keytag: "TB_AUTH"
            }, options, defaultOptions);
            // check to see if we're going to link another token
            if (options.doSecondaryLink == true) {
                return linkSecondaryAccount(tokenId, options, progressID);
            }
            options.tokenName = getFriendlyName(tokenId) + " 0";
            return linkPrimaryAccount(tokenId, options, progressID);
        }
        catch(e) {
            if(progressID) tbHideDiv(progressID);
            tbError(e);
        }
    }
	var checkingPlugin = false;
	function ensurePluginExists(callback) {
		if(checkingPlugin) return true;
		if($.browser.msie) { /* Force the plugin always after this point... */
			setCookie('tbEnabled','true');
		}
		checkingPlugin = true;
		if(!xmlrpc.delayedPlugin) {
			tbCheckVersion(false, function(hasPlugin) {
				if(hasPlugin) callback();
			});
			return false;
		}
		xmlrpc.delayedPlugin = false;
		$("body").append(getTBLivePluginObject());
		/* Wait for a bit for the plugin object to be loaded, then check...
		 * if it shows up at any time after the point, hide the dialog
		 * and resume... */
		var dialogUp;
		function pluginExists() {
			var tb = getTBLive();
			return tb && tb.version;
		}
		var divID = null;
		var tries = 1 + 4 * 2;
		var intervalID;
		var foreverTry = false;
		/* For 4 seconds try to see if the plugin comes into existence */
		function tryPluginCheck() {
			tries--;
			if(!foreverTry && tries < 0 || pluginExists()) {
				clearInterval(intervalID);
				if(divID !== null)
					tbHideDiv(divID);
				divID = null;
				setTimeout(function() {
					tbCheckVersion(false, pluginCheck);
				}, 10);
				return;
			}
			if(!foreverTry && !divID)
				divID = tbShowStackedProgressDialog("Checking for Add-on...");
		}
		function pluginCheck(hasPlugin) {
			if(!hasPlugin && !foreverTry) { /* Hang around and keep trying to check the plugin */
				foreverTry = true;
				intervalID = setInterval(tryPluginCheck, 250);
			}
			if(!hasPlugin) return;
			/* Has the plugin.. now we can mark the TB enabled cookie */
			setCookie('tbEnabled','true');
			if(callback) callback();
		}
		intervalID = setInterval(tryPluginCheck, 250);
		return false;
	}
    function collectToken(options, callback) {
        // Make sure the plugin exists (some refactoring may need to be done)
        if(!ensurePluginExists(function() { collectToken(options, callback); }))
            return false;
        //TokenConnect(options.noPIN && {noPIN:true} || {}, function(success, token, err) {
        options.noPIN = true;
        TokenConnect(options, function(success, token, err) { //For Wave Branding!!!
            if (!success) {
                if (err)
                    return tbError(err);
                return;
            }
            options.token = token.tokenId;
            callback(options);
        })
    }
	function createDelayLock(func, delay) {
		var delayed = false;
		delay = delay || 3000;
		return function() {
			if(delayed) return;
			delayed = true;
			setTimeout(function() { delayed = false; }, delay);
			return func.apply(this, arguments);
		};
	}
	/* BEGIN Sign-In */
	function performSignIn() {
		var options = {
			userMustExist: true /* User must exist */
		};
		collectToken(options, performVerifyOp);
		return false;
	}
	performSignIn = createDelayLock(performSignIn);
	window.performSignIn = performSignIn;

	// analogous to the above performSignIn(), except sets the flag
	// for linking another token instead of requiring a unique username
	function performSecondaryLink() {
		var options = {
			doSecondaryLink: true
		};
		collectToken(options, performVerifyOp);
		return false;
	}
	performSecondaryLink = createDelayLock(performSecondaryLink);
	window.performSecondaryLink = performSecondaryLink;

	/* BEGIN ENROLL */

	function collectSignupInput() {
		deleteCookie('username');
		deleteCookie('email');
		var username = $.trim($(".username").val());
		var email = $.trim($(".email").val());
		window.savedUsername = username;
		window.savedEmail = email;
		if((!username || username.length == 0) && (!email || email.length == 0)) {
			window.generateWarning('Enter a username and email address.');
			return false;
		} else if (!username || username.length == 0) {
			window.generateWarning('Enter a username.');
			return false;
		} else if (username.length > 64) {
			window.generateWarning('Your username is too long, please limit it to 64 characters.');
			return false;
		} else if (!email || email.length == 0) {
			window.generateWarning('Please enter an email address.');
			return false;
		} else if (email.length > 2048) {
			window.generateWarning('Your email address is too long, please limit it to 2048 characters.');
			return false;
		}

		var badchar = username.match(/[^\w-]/);
		if(badchar) {
			window.generateWarning('Username contains an invalid character: ' + badchar[0]);
			return false;
		}
		if(!emailRegex.test(email)) {
			window.generateWarning('Invalid email address entered.');
			return false;
		}

		return {
			email: email,
                username: username
				};
	}

	/**
	 * Checks to determine if an OpenID username has already by taken.
	 *
	 * @param username The name for which to check availability.
	 * @param callback Function to execute, passing result as a parameter.
	 */
	function checkUserExists(username, callback) {
		tbUtil.jsonRpcCall('OpenIDEnrollProcessor.userExists', [ username ], function(ret) {
			if(!ret || ret.result) {
				tbUtil.exceptionHandler({
					xmlrpc: true,
					component: "TBVerifyKey",
					shortform: "UserExists",
					longform: "A user already exists with the given username: [" + username + "]"
				});
			}
			if(callback) callback(!ret || ret.result);
		});
	}
	function performEnroll(doContinue) {
		var data = collectSignupInput();
		if(!data) return false;
		if(!doContinue) {
			return checkUserExists(data.username, function(ret) {
				if(ret) return;
				performEnroll(true);
			});
		}
		var username = data.username;
		var email = data.email;
		var token = data.token;
		var options = {
			checkedEnroll: true, /* User must NOT exist */
			enroll: {
				username: username,
				process: {
					/* Values to associate/enroll w/ the account instantly */
					OpenIDEnrollProcessor: {
						nickname: username,
						email: email,
						token: token
					}
				}
			},
			autoEnrollOptions: { request: { username: username } }
		};
		collectToken(options, performVerifyOp);
		return false;
	}
	var lockedPerformEnroll = createDelayLock(performEnroll);

    /* BEGIN HOOKUP OF FUNCTIONS */
    $(".signinButton").click(function() {
        if($('body:first').is('.page_enroll')) {
            // Special behavior for front page
            loadNewPage('landing.html?signin=true');
            return false;
        }
        performSignIn();
        return false;
    });
	$(".signupButton").click(function() {
	    if($('body:first').is('.page_enroll')) {
			// Special behavior for front page
			var data = collectSignupInput();
			if(!data) return false;
            setCookie('username', $('.username').val());
            loadNewPage('landing.html?' + $.param(data));
			return false;
		}
		lockedPerformEnroll();
		return false;
	});

	// register callback for hitting the "Cancel" button
	// if cancelLocation exists (OpenID parameter "cancelURL"), user is redirected there
	$(".cancelLogin").click(function() {
		if(cancelLocation) {
			loadNewPage(cancelLocation);
		} else if(getQuery('resumeURL')) {
			var resume = getQuery('resumeURL');
			if(resume.match(/\/handleSaml\.php/)) {
				history.go(-1);
				return;
			}
			if(resume.match(/\?/))
				resume += '&cancel=true';
			else
				resume += '?cancel=true';
			loadNewPage(resume);
		} else {
			loadNewPage('index.html');
		}
		return false;
	});

	// register callback for changing the PIN
	$(".changePinButton").click(function() {
		function doChange() {
			collectToken({ noPIN : true, tokenFilter: function(token) {
                    if (token.isTPM) return false;
                    return true;
                }},
                function(options) {
				    var token = options.token;
				    tbChangePin(token, null, options);
			});
		}
		if(ensurePluginExists(doChange))
			doChange();
	});

	// register callback for adding another token
	$(".addTokenButton").click(performSecondaryLink);

    function updateOpenIDURL(user) {
        var url = formatOpenID("");
        $(".openidurl .urlhere").text(url);
        if (user) {
            $(".openidurl .name").text(user);
        }
    }
	window.updateOpenIDURL = updateOpenIDURL;
	var updating = false;

	// register callback for when user changes usename in field,
	// update the OpenID URL that is displayed
	$("#signup .username, .for_signup .username").keydown(function() {
		if(updating) return;
		updating = true;
		var self = $(this);
		setTimeout(function() {
			updating = false;
			updateOpenIDURL(self.val());
		}, 100);
	});
	function doUpdate() {
		if(!openIDURLFormat) return setTimeout(doUpdate, 200);
		updateOpenIDURL($(".username").val());
	}
	$(doUpdate);
	
	// register callback for changing the PIN
	$(".changePinButton").click(function() {
		function doChange() {
			collectToken({ noPIN : true }, function(options) {
				var token = options.token;
				tbChangePin(token);
			});
		}
		if(ensurePluginExists(doChange))
			doChange();
	});
	window.ensurePluginExists = ensurePluginExists;
	window.collectToken = collectToken;
    
	
});

$(function() {
    /* Fill in old values of username/email */
    var oldEmail = getQuery('email');
    var oldUsername = getQuery('username');
    if(oldUsername) {
        $(".username").val(oldUsername);
        // Be sure to hook up the new URL
        updateOpenIDURL(oldUsername);
    }
    if(oldEmail) $(".email").val(oldEmail);
});
var openIDURLFormat;
$.get("getOpenIDURL.php", {IDENTITY: "%%"}, function(ret) {
	openIDURLFormat = ret;
});

function formatOpenID(identity) {
	return openIDURLFormat && openIDURLFormat.replace(/%%/g, identity) || "";
}
/* Generic accessor to the backend */
function jsonOpenIDCall(action, paramArray, extraData, callback) {
	if(typeof extraData == 'function') {
		callback = extraData;
		extraData = null;
	}
	var data = {action:action, parameters: $.toJSON(paramArray)};
	if(extraData)
		data = $.extend(data, extraData);
	var request = {
		url: 'daemonAccess.php',
		type: 'POST',
		dataType: 'json',
		success: callback,
		error: function(xmlhttp,err, ex) {
			if(xmlhttp.status == 200)
				err = 'Invalid data returned, server may be down';
			callback({error: err});
		},
		async: true,
		data: data
	};
	$.ajax(request);
}

(function() {
	var loggedIn = false;
	var loginCallbacks = [];
	function onLogin(callback) {
		if(loggedIn)
			callback();
		else
			loginCallbacks.push(callback);
	}
	function setLoggedIn() {
		loggedIn = true;
		for(var i = loginCallbacks.length - 1; i >= 0; i--) {
			loginCallbacks[i]();
		}
	}
	window.onLogin = onLogin;
	window.setLoggedIn = setLoggedIn;
})();

/* UI Hookup */
$(function() {
	// Landing page doesn't need this hookup
	if($("body").is(".page_landing"))
		return;
	function setLoginData(statusOrUser, url) {
		$(".login-message-cr").text(statusOrUser);
		$(".your-openid input").attr({'value' : url || statusOrUser});
	}
	var divID = null;
	if($(".dash").length > 0) // Only "verify" @ the dashboard
		divID = tbShowStackedProgressDialog();
	$.getJSON("getCurrentUser.php", function(ret) {
		if(divID !== null) tbHideDiv(divID);
		loginImg();
		if(!ret.result) {
			setLoginData("You Are Not Logged In; Please Sign-In or Sign-Up");
			if($(".dash").length > 0) // At dashboard but not logged in.. push back
				loadNewPage('index.html', true);
		} else {
			if(getQuery('enrolled') == 'true') {
				$(".userEnrolled").show().text('"' + ret.result + '" has been successfully enrolled');
			}
			if($(".page_enroll").length > 0) { // At homepage, jump to dash
				loadNewPage('dashboard.html', true);
				return;
			}
			setLoggedIn();
			setLoginData(ret.result + " logged in", formatOpenID(ret.result));
			window.currentUser = ret.result;
		    updateOpenIDURL(window.currentUser);
		}
	});
	var loginImg = function() {
		$('.login-message-cr p').each(function(){
			$(this).before('<img src="assets/images/openid/info.png" width="16px" height="16px" align="left">');
		});
	};
});

utility.logout = function(callback) {
	tbShowStackedProgressDialog("Logging Out...", -3);
	deleteCookie('openid_server');
	if(typeof callback == 'function') return callback(ret);
	loadNewPage('index.html', true);
};

/* ACCOUNT MANAGEMENT */
$(function() {
	$(".deleteAccountButton").click(function() {
		var ok = confirm("Do you want to delete your account?");
		if(!ok) return;
		jsonOpenIDCall('obliterateUser', [], {NeedsTBAccessID:'true'}, function(ret) {
			if(ret.error)
				return tbUtil.exceptionHandler(ret.error, "TBOpenID");
			utility.logout();
		});
	});
});

/**
 * Register to do the logout action.  If there are no tokens associated with the account,
 * the user will be prompted on logout.  This is just to make sure that they really want
 * to abandon their account.
 */
$(function() {
	$(".signoutButton").click(function() {
		utility.logout();
	});
});

/* NEW SIMPLE LOGIN PAGE HOOKUP */
$(function() {
	if($(".relying-party").length == 0) return;
	var target = getQuery('site');
	if(!target) target = "UNSPECIFIED";
	$(".relying-party").text(target);
});

$(function() {
	if(getQuery('noIdentifier') != 'true') return;
	$(".noIdentifierWarning").show();
});

/* {{{ Functions for token management. */
$(function() {
    function getTokenNameDisplay(tokenName) {
        return tokenName.replace(/( [0-9]+)$/, '');
    }
    window.getTokenNameDisplay = getTokenNameDisplay;
    function getTokenNameId(tokenName) {
        return tokenName.replace(/[^A-Za-z0-9_]/g, '_');
    }
    window.getTokenNameId = getTokenNameId;
		/*!
		 * Adds a new token to the system on the backend.  Requires the
		 * "tokenName" input box to be on the current page, otherwise don't call
		 * this function!  We do a post() in here, so this function will return
		 * immediately without doing anything visible.
		 *
		 * \param options Hash of options, commonly passed along from the
		 *                performVerifyOp function, in specific we are
		 *                interested in the "resumeURL" field, which tells us
		 *                where to return in the browser when this function is
		 *                done.  If unset, no navigation takes place.
		 */
		function addToken(options) {
			var token = options.tokenName;
			if (token) {
				$.post("tokenMgmt.php", { operation: "add", tokenname: token },
					function (ret) {
						if (!ret || ret.error) {
							alert("Sorry, failed to add token.");
						}
						/* Query data overrides dashboard */
						var returnTo = getQuery('resumeURL') || 'dashboard.html';
						if(options.enroll) {
							returnTo = 'dashboard.html?enrolled=true';
						}
						loadNewPage(returnTo);
					}, "json");
			}
		}
		var warningCounter = 0;
		function generateWarning(msg) {
		    hideMsg = null;
		    clearTimeout(hideMsg);
			warningCounter++;
			/* 'block' display to make sure it stays the same when hiding */
			var div = $(".message");
			div.removeClass('error, success');
			div.fadeIn().addClass('error').text(msg);
			/*
			var div = $.create('p', {'class':'warning', style: {'display':'block'}}, [
				'img', {src:"assets/images/openid/warning.gif",align:"left"},
				msg
			]);
			div = $('.warningArea').append(div).children(':last');
            */
			hideMsg = setTimeout(function() { 
			    div.fadeOut(2000); 
			}, 8000);
		}

		function makeTokenRow(tokenName) {
		    if (tokenName.indexOf("Trusted Platform Module") != -1) {
    		    var row =   "<tr>" + 
    			                "<td class='name'>" + getTokenNameDisplay(tokenName) + "</td>" +
    			                "<td>" + 
    			                "<input type='button' class='smallBtn remove' value='Remove Account'/>" + 
    			                "</td>" +
    			            "</tr>";
		    } else {
    		    var row =   "<tr>" + 
    			                "<td class='name'>" + getTokenNameDisplay(tokenName) + "</td>" +
    			                "<td>" + 
    			                "<input type='button' class='smallBtn remove' value='Remove'/>" + 
    			                "<input type='button' value='Change PIN' class='changePinButton smallBtn'>" +
    			                "</td>" +
    			            "</tr>";
		    }
            row = $(row);
			$("#token-table").append(row);
			$(".remove", row).click(function() {
				removeToken(tokenName, row);
			});
			
			$(".changePinButton").click(function() {
        		function doChange() {
        			collectToken({ noPIN : true }, function(options) {
        				var token = options.token;
        				tbChangePin(token);
        			});
        		}
        		if(ensurePluginExists(doChange))
        			doChange();
        	});
        	
		}

		/**
		 * Fetches the token list from the server and sticks it onto the
		 * dashboard.
		 */
		function updateTokenListing() {
			$(".userDevices").html("<table id='token-table' cellpadding=\"0\" cellspacing=\"0\"></table>");
			$.post("tokenMgmt.php", {"operation": "list"}, function (ret) {
					if (!ret || ret.error) {
						alert("Sorry, could not retrieve token listing from server.");
					}
					else {
						var tokens = ret.result;
						for (var i = 0; i < tokens.length; i += 1) {
							makeTokenRow(tokens[i]);
						}
					}
				}, "json");
		}

		/**
		 * Prepares to remove the given token.
		 * 
		 * mjs -03-18-10- modified to display more appropriately for single token (TPM) use.
		 *
		 * @param token The token name to remove.
		 */
		function removeToken(tokenName, row) {
			$.post("tokenMgmt.php", {"operation": "list"}, function (ret) {
				if (!ret || ret.error) {
					alert("Sorry, could not remove token.");
				}
				else {
					var tokens = ret.result;
					var numTokens = tokens.length;
					
					// mjs
					if (tokenName.indexOf("Trusted Platform Module") != -1) {
						var confirmLastMessage = "This action will unbind your TPM from your OpenID account. After doing so, you WILL NOT be able to sign in again.  This identity will become unusable and inaccessible.";
						var confirmRemove = "Are you sure want to close this account and abondon this identity?";
					} else {
						var confirmLastMessage = "This is your last token.  You will be signed out automatically after removing this device.  After doing so, you WILL NOT be able to sign in again.  This username will become unusable.";
						var confirmRemove = "Are you sure you want to remove the token '" + getTokenNameDisplay(tokenName) + "'?"; 
					}
						
					if (numTokens > 1 || confirm(confirmLastMessage)) {
						if (confirm(confirmRemove)) {
							$.post("tokenMgmt.php",
									{"operation": "remove", "tokenName": tokenName},
									function (ret) {
										if (!ret || ret.error) {
											alert("Sorry, couldn't remove the token.");
										}
										else if (numTokens > 1) {
											row.remove();
											$(".userEnrolled").show().text('successfully removed token "' + getTokenNameDisplay(tokenName) + '"');
										}
										else {
											utility.logout();
										}
							}, "json");
						}
					}
				}
			}, "json");
		}

		/* only update the token listing at the dashboard */
		if($(".dash").length > 0) updateTokenListing();

		/* add the functions to the global namespace */
		window.updateTokenListing = updateTokenListing;
		window.generateWarning = generateWarning;
		window.addToken = addToken;
		window.removeToken = removeToken;
        window.makeTokenRow = makeTokenRow;

		/* adds an explanatory message row to the table when the user
		 * hovers over the token name field */
	});
/* }}} */
