/* -*- javascript -*-
 *
 * Copyright (c) 2010, Janrain, Inc. All rights reserved.
 */

/*jslint nomen: false,
         regexp: false,
         browser: true,
         plusplus: false,
         white: false,
         onevar: false
*/

(function (WIN) {
    var ABSOLUTE_PATH_RE, ABSOLUTE_URL_FRAG_RE, ABSOLUTE_URL_RE, ACTIVEX, CDN_BASE, COMMON_UI_PARAMS, Config, DOC, IE6, IMAGES, LOADING_STRINGS, MOBILE, QUIRKS_MODE, REGEXP_ESCAPE_RE, REGEXP_SPECIAL, RPXNOW, SCHEME, SECURE, applicationUrlPrefixCallbacks, custProto, cx, divPlusIFrame, escapable, genSigninUrl, oid1Proto, oid2Proto, qbody, registerXdCallback, setStyle, showIFrameOnLoad, socialImageCSS, socialImageURL, withAppUrlPrefix, xdCall, getHiddenDiv, addListener, addClickListener, bind, isArray, isBoolean, isFunction, isNull, isNumber, isObject, isString, isUndefined, indexOf, contains, log, getViewportHeight, getViewportWidth, getDocumentHeight, getDocumentWidth, parseQueryString, regexpEscape, CookieUserStateStore, _hiddenDiv, createElement, getFrameName;

    if ((typeof WIN.RPXNOW !== "undefined") && WIN.RPXNOW) {
        return; // Don't clobber already if loaded
    }
    RPXNOW = {};

    RPXNOW.RPX_URL_PREFIX = "https:\/\/rpxnow.com\/";

    DOC = WIN.document;
    ACTIVEX = typeof WIN.ActiveXObject !== "undefined";
    QUIRKS_MODE = DOC.compatMode !== 'CSS1Compat';

    IE6 = (function () {
        var agent = WIN.navigator && WIN.navigator.userAgent,
        m = agent.match(/MSIE\s([^;]*)/),
        c;
	if (m&&m[1]) {
	    c = 0;
            return (6 === parseFloat(m[1].replace(/\./g, function() {
                return (c++ === 1) ? '' : '.';
            })));
        } else {
	    return false;
	}
    }());

    MOBILE = (function () {
        var ua = '',
        devices = new RegExp('iphone|ipod|ipad|android|palm|blackberry|windows ce');
        try {ua = WIN.navigator.userAgent.toLowerCase();} catch(e) {}
        return ua.search(devices) !== -1;
    }());

    SECURE = DOC.location.protocol === 'https:';
    SCHEME = WIN.location.protocol.replace(/[:]$/, '');
    CDN_BASE = (SECURE ?
                'https://s3.amazonaws.com/static.rpxnow.com' :
                'http://cdn.rpxnow.com');

    
    IMAGES = {
        bg_social: [
            'rel/img/5d24af2656cb7d5bfc959225ed93f78e.png',
            'rel/img/48225b514e12863809a27dbbd77e3bc9.gif'],
        bg_auth: [
            'images/bg_auth.png/072e699dadea176ace38e1f39c8b1c6f.png',
            'images/bg_auth.gif/59b67dc6128043d53a89808c2e08678a.gif'],
        lb_close: [
            'rel/img/43f137c98b7a6562f6ad5f076fce376b.png',
            'images/close_ie.png/6e03cf22aa59b4844d0b78df3bc6787d.png'],
        lb_corners: [
            'images/corners.png/47464b01c238fc81a5a537859e37e930.png',
            'images/corners.gif/a5384cf51db4d3e85f3c4f95c546a9c5.gif'],
        lb_border: [
            'images/border.png/01c40882d59520662e8cebae63d73a2c.png',
            'images/border.gif/a7f3bf49b8e1d17d66a870e5b7d3b2b9.gif'],
        auth_background: [
            'images/background.png/ce2da388a4e0fa1ef809b2c4cc4d5139.png',
            'images/background.png/ce2da388a4e0fa1ef809b2c4cc4d5139.png']
    };


    cx = new RegExp('[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]', 'g');
    escapable = new RegExp("[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]", 'g');
    REGEXP_SPECIAL = ['/','.','*','+','?','|','(',')','[',']','{','}','\\'];
    REGEXP_ESCAPE_RE = new RegExp('(\\' + REGEXP_SPECIAL.join('|\\') + ')', 'g');
    ABSOLUTE_URL_RE = new RegExp('^https?://([a-z0-9]([\\-a-z0-9]*[a-z0-9])?\\.)*[a-z0-9]([\\-a-z0-9]*[a-z0-9])?(:[0-9]+)?(/[^?#]*(\\?[^#]*)?)?$', 'i');
    ABSOLUTE_URL_FRAG_RE = new RegExp('^https?://([a-z0-9]([\\-a-z0-9]*[a-z0-9])?\\.)*[a-z0-9]([\\-a-z0-9]*[a-z0-9])?(:[0-9]+)?(/[^?#]*(\\?[^#]*)?)?(#.*)?$', 'i');
    ABSOLUTE_PATH_RE = new RegExp('^/[^?#]*(\\?[^#]*)?$');

    qbody = function () {
        return QUIRKS_MODE ? DOC.body : DOC.documentElement;
    };

    createElement = function(name) {
        return DOC.createElement(name);
    };

    socialImageURL = function(name) {
        var path = IMAGES[name][IE6 ? 1 : 0];
        return CDN_BASE + "/" + path;
    };

    socialImageCSS = function(name) {
        return "url('" + socialImageURL(name) + "')";
    };

    setStyle = function(elem, attrs) {
        var key, style = elem.style;
        for (key in attrs) {
            if (Object.hasOwnProperty.call(attrs, key)) {
                style[key] = attrs[key];
            }
        }
    };

    _hiddenDiv = null;
    getHiddenDiv = function () {
        if (!_hiddenDiv) {
            _hiddenDiv = createElement("div");
            _hiddenDiv.setAttribute('id', 'FB_HiddenContainer');
            setStyle(_hiddenDiv, {
                position: 'absolute',
                top: '-10000px',
                left: '-10000px',
                width: '0',
                height: '0'
            });
            DOC.body.insertBefore(_hiddenDiv, DOC.body.firstChild);
        }
        return _hiddenDiv;
    };

    addListener = function (name, elem, listener) {
        if (ACTIVEX) {
            elem.attachEvent('on' + name, listener);
        } else {
            elem.addEventListener(name, listener, false);
        }
    };

    addClickListener = function (elem, listener) {
        addListener('click', elem, listener);
    };

    bind = function (obj, fname) {
        return function () {
            obj[fname].apply(obj, arguments);
        };
    };

    isArray = function(obj) {
        return (Object.prototype.toString.apply(obj) === '[object Array]');
    };

    isBoolean = function(obj) {
        return (typeof obj === 'boolean');
    };

    isFunction = function(obj) {
        return Object.prototype.toString.apply(obj) === '[object Function]';
    };

    isNull = function(obj) {
        return obj === null;
    };

    isNumber = function(obj) {
        return typeof obj === 'number' && isFinite(obj);
    };

    isObject = function(obj) {
        return (obj && (typeof obj === 'object' || isFunction(obj))) || false;
    };

    isString = function(obj) {
        return typeof obj === 'string';
    };

    isUndefined = function(obj) {
        return typeof obj === 'undefined';
    };

    indexOf = function (arr, val) {
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] === val) {
                return i;
            }
        }
        return -1;
    };

    contains = function (arr, val) {
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

    log = function(msg) {
        if (WIN.console) {WIN.console.log(msg);}
    };

    getViewportHeight = function() {
        if( isNumber(WIN.innerHeight) ) {
            //Non-IE
            return WIN.innerHeight;
        } else {
            //IE 6+ in 'standards compliant mode'
            return qbody().clientHeight;
        }
    };

    getViewportWidth = function() {
        if( isNumber(WIN.innerWidth) ) {
            //Non-IE
            return WIN.innerWidth;
        } else {
            //IE 6+ in 'standards compliant mode'
            return qbody().clientWidth;
        }
    };

    getDocumentHeight = function() {
        return Math.max(qbody().scrollHeight, getViewportHeight());
    };

    getDocumentWidth = function() {
        return Math.max(qbody().scrollWidth, getViewportWidth());
    };

    parseQueryString = function (url_str) {
        var _query_regex = new RegExp("([^=]+)=([^&]*)&?", "g"),
        match = null,
        query_str = url_str.match(/^[^?]*(?:\?([^#]*))?(?:$|#.*$)/)[1],
        result = {};
        // This regex has mutable state changed by the exec call.
        // Re-create it each time getQueryStringValue is called
        while (!isNull(match = _query_regex.exec(query_str))) {
            (result[decodeURIComponent(match[1])] =
             decodeURIComponent(match[2]));
        }
        return result;
    };

    regexpEscape = function(text) {
        return text.replace(REGEXP_ESCAPE_RE, '\\$1');
    };

    /* -*- javascript -*- */

var _toJSON = function (key, holder) {
    var i, k, length, partial, quote, v, meta, value, c;
    meta = { // table of character substitutions
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"': '\\"',
        '\\': '\\\\'
    };
    value = holder[key]; // Produce a string from holder[key].
    quote = function(string) {
        // If the string contains no control characters, no quote
        // characters, and no backslash characters, then we can safely
        // slap some quotes around it.  Otherwise we must also replace
        // the offending characters with safe escape sequences.
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function(a) {
            c = meta[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    };
    // What happens next depends on the value's type.
    switch (typeof value) {
    case 'string':
        return quote(value);

    case 'number':

        // JSON numbers must be finite. Encode non-finite
        // numbers as null.

        return isFinite(value) ? String(value) : 'null';

    case 'boolean':
    case 'null':

        // If the value is a boolean or null, convert it to a
        // string. Note: typeof null does not produce 'null'. The case
        // is included here in the remote chance that this gets fixed
        // someday.

        return String(value);

        // If the type is 'object', we might be dealing with an object
        // or an array or null.

    case 'object':

        // Due to a specification blunder in ECMAScript, typeof null
        // is 'object', so watch out for that case.

        if (!value) {
            return 'null';
        }

        // Make an array to hold the partial results of stringifying
        // this object value.

        partial = [];

        // Is the value an array?

        if (Object.prototype.toString.apply(value) === '[object Array]') {

            // The value is an array. Stringify every element. Use
            // null as a placeholder for non-JSON values.

            length = value.length;
            for (i = 0; i < length; i += 1) {
                partial[i] = _toJSON(i, value) || 'null';
            }

            // Join all of the elements together, separated with
            // commas, and wrap them in brackets.

            v = partial.length === 0 ? '[]' :
                '[' + partial.join(',') + ']';
            return v;
        }


        for (k in value) {
            if (Object.hasOwnProperty.call(value, k)) {
                v = _toJSON(k, value);
                if (v) {
                    partial.push(quote(k) + ':' + v);
                }
            }
        }

        // Join all of the member texts together, separated with
        // commas, and wrap them in braces.

        v = partial.length === 0 ? '{}': '{' + partial.join(',') + '}';
        return v;
    }
    throw new Error("What happened?.");
},

toJSON = function (value) {
    // Make a fake root object containing our value under the key of
    // ''.  Return the result of stringifying the value.
    return _toJSON('', {'': value});
},

parseJSON = (function () {

    // This is a function that can parse a JSON text, producing a
    // JavaScript data structure. It is a simple, recursive descent
    // parser. It does not use eval or regular expressions, so it can
    // be used as a model for implementing a JSON parser in other
    // languages.

    // We are defining the function inside of another function to
    // avoid creating global variables.

    var at,     // The index of the current character
    ch,     // The current character
    escapee = {
        '"':  '"',
        '\\': '\\',
        '/':  '/',
        b:    '\b',
        f:    '\f',
        n:    '\n',
        r:    '\r',
        t:    '\t'
    },
    text,

    error = function (m) {

        // Call error when something is wrong.

        throw {
            name:    'SyntaxError',
            message: m,
            at:      at,
            text:    text
        };
    },

    next = function (c) {

        // If a c parameter is provided, verify that it matches the
        // current character.

        if (c && c !== ch) {
            error("Expected '" + c + "' instead of '" + ch + "'");
        }

        // Get the next character. When there are no more characters,
        // return the empty string.

        ch = text.charAt(at);
        at += 1;
        return ch;
    },

    number = function () {

        // Parse a number value.

        var number,
        string = '';

        if (ch === '-') {
            string = '-';
            next('-');
        }
        while (ch >= '0' && ch <= '9') {
            string += ch;
            next();
        }
        if (ch === '.') {
            string += '.';
            while (next() && ch >= '0' && ch <= '9') {
                string += ch;
            }
        }
        if (ch === 'e' || ch === 'E') {
            string += ch;
            next();
            if (ch === '-' || ch === '+') {
                string += ch;
                next();
            }
            while (ch >= '0' && ch <= '9') {
                string += ch;
                next();
            }
        }
        number = +string;
        if (isNaN(number)) {
            error("Bad number");
        } else {
            return number;
        }
    },

    string = function () {

        // Parse a string value.

        var hex,
        i,
        string = '',
        uffff;

        // When parsing for string values, we must look for " and \
        // characters.

        if (ch === '"') {
            while (next()) {
                if (ch === '"') {
                    next();
                    return string;
                } else if (ch === '\\') {
                    next();
                    if (ch === 'u') {
                        uffff = 0;
                        for (i = 0; i < 4; i += 1) {
                            hex = parseInt(next(), 16);
                            if (!isFinite(hex)) {
                                break;
                            }
                            uffff = uffff * 16 + hex;
                        }
                        string += String.fromCharCode(uffff);
                    } else if (typeof escapee[ch] === 'string') {
                        string += escapee[ch];
                    } else {
                        break;
                    }
                } else {
                    string += ch;
                }
            }
        }
        error("Bad string");
    },

    white = function () {

        // Skip whitespace.

        while (ch && ch <= ' ') {
            next();
        }
    },

    word = function () {

        // true, false, or null.

        switch (ch) {
        case 't':
            next('t');
            next('r');
            next('u');
            next('e');
            return true;
        case 'f':
            next('f');
            next('a');
            next('l');
            next('s');
            next('e');
            return false;
        case 'n':
            next('n');
            next('u');
            next('l');
            next('l');
            return null;
        }
        error("Unexpected '" + ch + "'");
    },

    value,  // Place holder for the value function.

    array = function () {

        // Parse an array value.

        var array = [];

        if (ch === '[') {
            next('[');
            white();
            if (ch === ']') {
                next(']');
                return array;   // empty array
            }
            while (ch) {
                array.push(value());
                white();
                if (ch === ']') {
                    next(']');
                    return array;
                }
                next(',');
                white();
            }
        }
        error("Bad array");
    },

    object = function () {

        // Parse an object value.

        var key,
        object = {};

        if (ch === '{') {
            next('{');
            white();
            if (ch === '}') {
                next('}');
                return object;   // empty object
            }
            while (ch) {
                key = string();
                white();
                next(':');
                if (Object.hasOwnProperty.call(object, key)) {
                    error('Duplicate key "' + key + '"');
                }
                object[key] = value();
                white();
                if (ch === '}') {
                    next('}');
                    return object;
                }
                next(',');
                white();
            }
        }
        error("Bad object");
    };

    value = function () {

        // Parse a JSON value. It could be an object, an array, a
        // string, a number, or a word.
        white();
        switch (ch) {
        case '{':
            return object();
        case '[':
            return array();
        case '"':
            return string();
        case '-':
            return number();
        default:
            return ch >= '0' && ch <= '9' ? number() : word();
        }
    };

    // Return the json_parse function. It will have access to all of
    // the above functions and variables.

    return function (source, reviver) {
        var result;

        text = source;
        at = 0;
        ch = ' ';
        result = value();
        white();
        if (ch) {
            error("Syntax error");
        }

        // If there is a reviver function, we recursively walk the new
        // structure, passing each name/value pair to the reviver
        // function for possible transformation, starting with a
        // temporary root object that holds the result in an empty
        // key. If there is not a reviver function, we simply return
        // the result.

        return typeof reviver === 'function' ? (function walk(holder, key) {
            var k, v, value = holder[key];
            if (value && typeof value === 'object') {
                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = walk(value, k);
                        if (v !== undefined) {
                            value[k] = v;
                        } else {
                            delete value[k];
                        }
                    }
                }
            }
            return reviver.call(holder, key, value);
        }({'': result}, '')) : result;
    };
}());

    /* -*- javascript -*- */

var XHR = {
    fetch: function (url, callbacks) {
        var req = this._newRequest();
        req.onreadystatechange = function () {
            if (req.readyState !== 4) {
                return;
            }
            switch (req.status) {
            case 200:
            case 304:
                callbacks.success(req.responseText);
                return;
            case -1: // request was explicitly aborted
                return;
            default:
                break;
            }
            callbacks.failure(req.status, req.statusText, req.responseText);
        };
        req.open("GET", url, true);
        req.send(null);
    },

    _newRequest: function () {
        if( isUndefined(WIN.XMLHttpRequest)) {
            try {
                return new WIN.ActiveXObject("Msxml2.XMLHTTP.6.0");
            } catch(e1) {}

            try {
                return new WIN.ActiveXObject("Msxml2.XMLHTTP.3.0");
            } catch(e2) {}

            try {
                return new WIN.ActiveXObject("Msxml2.XMLHTTP");
            } catch(e3) {}

            try {
                return new WIN.ActiveXObject("Microsoft.XMLHTTP");
            } catch(e4) {}

            throw new Error("This browser does not support XMLHttpRequest.");
        } else {
            return new WIN.XMLHttpRequest();
        }
    }
};

    /* -*- javascript -*- */

var Cookies = {
    _cached: null,

    // Public Static
    get: function (name) {
        return this._parsedCookies()[name];
    },

    remove: function (name, options) {
        //set options
        options = options || {};
        options.expires = new Date(0);

        //set cookie
        return this.set(name, "", options);
    },

    set: function (name, value, options) {
        var text = (encodeURIComponent(name) + "=" +
                    encodeURIComponent(value));

        if (isObject(options)) {
            //expiration date
            if (options.expires instanceof Date) {
                text += "; expires=" + options.expires.toGMTString();
            }

            //path
            if ((options.path) && options.path !== "") {
                text += "; path=" + options.path;
            }

            //domain
            if (isString(options.domain) && options.domain !== "") {
                text += "; domain=" + options.domain;
            }

            //secure
            if (options.secure === true){
                text += "; secure";
            }
        }
        DOC.cookie = text;
        return text;
    },

    // Private Static
    _parsedCookies : function () {
        var results, text, parts, i, name, value, match, part, len;
        if (isNull(this._cached)) {
            results = this._cached = {};
            text = DOC.cookie;

            if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)){
                parts = text.split(/;\s/g);
                name = null;
                value = null;
                match = null;
                part = null;

                for (i = 0, len = parts.length; i < len; i++) {
                    part = parts[i];
                    match = part.match(/([^=]+)=(.*)/i);
                    if (match instanceof Array){
                        try {
                            name = decodeURIComponent(match[1].replace(/\+/g, ' '));
                            value = decodeURIComponent(match[2].replace(/\+/g, ' '));
                            results[name] = value;
                        } catch (e){}
                    }
                }
            }
        }

        return this._cached;
    }
};

    /* -*- javascript -*- */

var QueryBuilder = function (dict) {
    var key;
    this.pairs = [];
    if (dict && typeof dict === "object") {
        for (key in dict) { if (dict.hasOwnProperty(key)) {
            this.add(key, dict[key]);
        }}
    }
};

QueryBuilder.prototype = {
    add: function (key, value) {
        var enc = encodeURIComponent;
        if (!isUndefined(value) && !isNull(value)) {
            this.pairs.push([enc(key), enc(value)].join("="));
        }
    },
    toString: function () {
        return this.pairs.join("&");
    }
};



    /* -*- javascript -*- */

var HiddenForm = function (action, prepop) {
    var formElem, submitElem, hiddenElem, key;
    formElem = this.formElem = createElement('form');
    submitElem = this.submitElem = createElement('input');
    submitElem.setAttribute('type', 'submit');
    formElem.appendChild(submitElem);

    hiddenElem = getHiddenDiv();
    hiddenElem.appendChild(formElem);

    this.inputElems = {};

    if (action) {
        this.setAction(action);
    }

    if (prepop && prepop instanceof Object) {
        for (key in prepop) { if (prepop.hasOwnProperty(key)) {
            this.setHidden(key, prepop[key]);
        }}
    }
};

HiddenForm.prototype = {
    setAction: function (url) {
        this.formElem.setAttribute('action', url);
    },

    setMethod: function (method) {
        this.formElem.setAttribute('method', method);
    },

    setTarget: function (name) {
        this.formElem.setAttribute('target', name);
    },

    setHidden: function (name, value) {
        var inputElem,
        inputElems = this.inputElems;

        if (isNull(value)) {
            this.deleteHidden(name);
            return;
        }
        inputElem = inputElems[name];
        if (!inputElem) {
            inputElem = inputElems[name] = createElement('input');
            inputElem.setAttribute('type', 'hidden');
            inputElem.setAttribute('name', name);
            this.formElem.appendChild(inputElem);
        }
        inputElem.setAttribute('value', value);
    },

    deleteHidden: function (name) {
        var inputElems = this.inputElems,
        inputElem = inputElems[name];
        if (inputElem) {
            this.formElem.removeChild(inputElem);
            inputElems.slice(indexOf(inputElems, inputElem), 1);
        }
    },

    submit: function () {
        this.submitElem.click();
    },

    submitTo: function (w) {
        this.setTarget(w.getName());
        this.submit();
    }
};

    /* -*- javascript -*- */

/**
 * A hidden iframe.
 */
var HiddenIframe = function () {
    var div, name, hiddenElem, src;
    // We do this innerHTML bs because in IE setting the name attr
    // of an iframe elem doesn't seem to allow targeting by that
    // name.
    div = createElement('div');
    name = this.name = getFrameName();
    hiddenElem = getHiddenDiv();
    hiddenElem.appendChild(div);
    src = IE6 ? RPXNOW.RPX_URL_PREFIX + 'blank.html' : '';
    div.innerHTML = ("<iframe src=\"" + src + "\" name=\"" + name +
                     '" id="' + name + '"><\/iframe>');

    this.iframeElem = DOC.getElementById(name);
    if (ACTIVEX) {
        WIN.frames[name].name = name;
    }
};

HiddenIframe.prototype = {
    // Public
    getName: function () {
        return this.name;
    },

    setSrc: function (src) {
        this.iframeElem.setAttribute("src", src);
    },

    addOnloadListener: function (cb) {
        var ifrm = this.iframeElem;

        if (ACTIVEX) {
            ifrm.onreadystatechange = function () {
                if (ifrm.readyState === "complete") {
                    cb();
                }
            };
        } else {
            ifrm.addEventListener('load', cb, false);
        }
    }
};

    /* -*- javascript -*-
 *
 * A browser popup window.
 */
var PopupWindow = function (width, height, name) {
    this.width = width;
    this.height = height;
    this.window = null;
    if (isUndefined(name)) {
        this.name = getFrameName();
    } else {
        this.name = name;
    }
    this.closeListeners = [];
};

PopupWindow.prototype = {
    // Public
    show: function (url) {
        var coords, that, ii;
        if (!this.window) {
            coords = this._getCenteredCoords();

            this.window = WIN.open(url || "", this.name,
                                   "width=" + this.width + ",height=" +
                                   this.height +
                                   ",status=1,location=1,resizable=yes" +
                                   ",left=" + coords[0] +",top=" +
                                   coords[1] + ",scrollbars=yes");
            that = this;
            ii = WIN.setInterval(function() {
                if (that.window.closed){
                    WIN.clearInterval(ii);
                    that._onClose();
                }
            }, 500);
        }
    },

    setLocation: function (url) {
        if (this.window) {
            this.window.location.href = url;
        }
    },

    setSize: function (width, height) {
        this.width = width;
        this.height = height;
        if (this.window) {
            this.window.outerWidth = width;
            this.window.outerHeight = height;
        }
    },

    close: function () {
        if (this.window) {
            this.window.close();
        }
    },

    addCloseListener: function (listener) {
        this.closeListeners.push(listener);
    },

    removeCloseListener: function (listener) {
        this.closeListeners.splice(indexOf(this.closeListeners, listener),1);
    },

    getName: function () {
        return this.name;
    },

    focus: function () {
        this.window.focus();
    },

    // Private
    _getCenteredCoords: function () {
        var xPos, yPos, parentSize, parentPos;
        // Computes the coordinates of the new window, so as to center
        // it over the parent frame
        xPos = null;
        yPos = null;

        if (ACTIVEX) {
            xPos = WIN.event.screenX - (this.width/2) + 100;
            yPos = WIN.event.screenY - (this.height/2) - 100;
            //yPos = WIN.event.screenY + 15;
        } else {
            parentSize = [WIN.outerWidth, WIN.outerHeight];
            parentPos = [WIN.screenX, WIN.screenY];
            xPos = parentPos[0] +
                Math.max(0, Math.floor((parentSize[0] - this.width) / 2));
            //yPos = parentPos[1] +
            // Math.max(0, Math.floor((parentSize[1] - (this.height*1.25))/2));
            yPos = WIN.screenY + 15;
        }
        return [xPos, yPos];
    },

    _onClose: function () {
        var i, listener;
        for (i = 0; i < this.closeListeners.length; i++) {
            listener = this.closeListeners[i];
            listener();
        }
    }
};

    /* -*- javascript -*-
 *
 * A lightbox style overlay
 */
var LightBox = function (contents, width, height) {
    var bod, borderCommon, btm, btmLt, btmRt, closeImg, containerElem, cornerCommon, elems, i, lightboxElem, mid, point4, top, topLt, topRt, transElem;

    contents.style.position = 'relative';

    // this div just fills it's container with a translucent gray
    transElem = createElement('div');
    transElem.className = "rpxnow_lightbox_trans";
    setStyle(transElem, {
        backgroundColor: "black",
        position: "absolute",
        top : 0,
        left: 0,
        width: "100%",
        height: "100%"
    });

    if (ACTIVEX) {
        transElem.style.filter = "alpha(opacity=40)";
    } else {
        point4 = '0.4';
        setStyle(transElem, {
            opacity: point4,
            KhtmlOpacity: point4,
            MozOpacity: point4
        });
    }

    // The close button
    closeImg = createElement('img');
    closeImg.src = socialImageURL('lb_close');
    closeImg.alt = 'close';

    setStyle(closeImg, {
        position: 'absolute',
        top: '-4px',
        right: '-4px',
        cursor: 'pointer',
        width: '34px',
        height: '34px'
    });

    // This is the element that centers the contents
    containerElem = this.containerElem = createElement('div');
    containerElem.className = "rpxnow_lightbox_container";

    setStyle(containerElem, {
        position: 'absolute',
        display: 'block',
        padding: '10px',
        width: (width + (IE6 && QUIRKS_MODE ? 20 : 0)) + 'px',
        height: (height + (IE6 && QUIRKS_MODE ? 20 : 0)) + 'px'
    });

    cornerCommon = {
        position: 'absolute',
        width: '20px',
        height: '20px',
        backgroundImage: socialImageCSS('lb_corners')
    };
    borderCommon = {
        position: 'absolute',
        backgroundImage: socialImageCSS('lb_border')
    };

    topLt = createElement('div');
    setStyle(topLt, cornerCommon);
    setStyle(topLt, {top: '0', left: '0'});

    top = createElement('div');
    setStyle(top, borderCommon);
    setStyle(top, {
        top: '0', left: '20px', width: (width - 20) + 'px', height: '20px'
    });

    topRt = createElement('div');
    setStyle(topRt, cornerCommon);
    setStyle(topRt, {
        top: '0', right: '0',
        backgroundPosition: 'top right'
    });

    mid = createElement('div');
    setStyle(mid, borderCommon);
    setStyle(mid, {
        top: '20px', left: '0',
        width: (width + 20) + 'px',
        height: (height - 20) + 'px'
    });

    btmLt = createElement('div');
    setStyle(btmLt, cornerCommon);
    setStyle(btmLt, {
        bottom: '0', left: '0',
        backgroundPosition: '0 -20px'
    });

    btm = createElement('div');
    setStyle(btm, borderCommon);
    setStyle(btm, {
        bottom: '0', left: '20px',
        width: (width - 20) + 'px', height: '20px'
    });

    btmRt = createElement('div');
    setStyle(btmRt, cornerCommon);
    setStyle(btmRt, {
        bottom: '0', right: '0',
        backgroundPosition: '-20px -20px'
    });

    elems = [topLt,top,topRt,btmLt,btm,btmRt,mid,contents,closeImg];
    for (i = 0; i < elems.length; i++) {
        containerElem.appendChild(elems[i]);
    }

    // This is the outer-most element that fills the. screen
    lightboxElem = this.lightboxElem = createElement('div');
    lightboxElem.className = "rpxnow_lightbox";

    setStyle(lightboxElem, {
        position: IE6 ? "absolute" : "fixed",
        display: "none", top: 0, left: 0, zIndex: 10000
    });

    lightboxElem.appendChild(transElem);
    lightboxElem.appendChild(containerElem);

    bod = DOC.body;
    if (bod.firstChild) {
        bod.insertBefore(lightboxElem, bod.firstChild);
    } else {
        bod.appendChild(lightboxElem);
    }

    addListener('resize', WIN, bind(this, 'resize'));
    addListener('click', closeImg, bind(this, 'hide'));

    if (IE6) {
        WIN.attachEvent('onscroll', bind(this, 'scroll'));
    }
    this.closeListeners = [];
};

LightBox.currentInstance = null;
LightBox.close = function () {
    LightBox.currentInstance.hide();
};

LightBox.prototype = {
    show: function () {
        if (LightBox.currentInstance === null) {
            LightBox.currentInstance = this;
        } else {
            return false;
        }

        this.containerElem.style.visibility = 'hidden';
        this.resize();
        if (IE6) {
            this.scroll();
        }
        this.lightboxElem.style.display = "block";
        return true;
    },

    hide: function () {
        var i, listener;

        this.lightboxElem.style.display = "none";
        LightBox.currentInstance = null;

        for (i = 0; i < this.closeListeners.length; i++) {
            listener = this.closeListeners[i];
            listener();
        }
    },

    addCloseListener: function (listener) {
        this.closeListeners.push(listener);
    },

    removeCloseListener: function (listener) {
        this.closeListeners.splice(indexOf(this.closeListeners, listener),1);
    },

    isVisible: function () {
        return (this.lightboxElem.style.display === "block");
    },

    resize: function () {
        var vpWidth, vpHeight, elem, rs;
        vpWidth = getViewportWidth();
        vpHeight = getViewportHeight();
        setStyle(this.lightboxElem, {
            width: vpWidth + "px",
            height: vpHeight + "px"
        });
        elem = this.containerElem;
        rs = function () {
            var padding = IE6 && QUIRKS_MODE ? 0 : 10;
            setStyle(elem, {
                top: (((vpHeight - elem.offsetHeight)/2) + padding) + "px",
                left: (((vpWidth - elem.offsetWidth)/2) + padding) + "px",
                visibility: 'visible'
            });
        };
        if (elem.style.visibility === 'hidden') {
            WIN.setTimeout(rs, 0);
        } else {
            rs();
        }
    },

    scroll: function () {
        var qb = qbody();
        setStyle(this.lightboxElem, {
            top: qb.scrollTop + 'px',
            left: qb.scrollLeft + 'px'
        });
    }
};

    /* -*- javascript -*- */
var BaseProvider, CustomProvider, OpenID2Provider, OpenID1Provider, IDNAtoASCII, defaultCallback, queryArgs, flags, stayInWindow;

queryArgs = parseQueryString(WIN.location.href);
flags = (queryArgs.flags || "").split(',');
stayInWindow = indexOf(flags, 'stay_in_window') > -1;

IDNAtoASCII = function (hostname) {
  var a = document.createElement("a");
  a.href = "http://" + hostname + "/";
  return a.hostname;
};

BaseProvider = function (name, popupWidth, popupHeight) {
    this.name = name;
    this.popupWidth = popupWidth;
    this.popupHeight = popupHeight;
    this.base_params = {};
};

BaseProvider.prototype = {
    _start: function (immediate, token_url, callback, params) {
        var ii, pw, dest, ifrm, key, qb;
        if (!immediate) {
            CookieUserStateStore.setLastLoginTab(this.providerName());
        }
        qb = new QueryBuilder(this.base_params);
        qb.add('flags', queryArgs.flags);
        qb.add('language_preference', queryArgs.language_preference);
        qb.add('openid_proxy_url', queryArgs.openid_proxy_url);
        qb.add('token_url', token_url);
        qb.add('bp_channel', queryArgs.bp_channel);
        qb.add('capture_domain', queryArgs.capture_domain);
        if (!immediate) {
            qb.add('display', 'popup');
        }

        if (params) {
            for (key in params) {
                if (Object.hasOwnProperty.call(params, key)) {
                    qb.add(key, params[key]);
                }
            }
        }
        pw = null;
        ii = null;
        if (!MOBILE || stayInWindow) {
            qb.add('xdReceiver', RPXNOW.config.xdReceiver);
            qb.add('callback', registerXdCallback(function(resp) {
                if (!isNull(pw)) {
                    WIN.clearInterval(ii);
                    pw.close();
                }
                callback(resp);
            }));
        }
        
        dest = this.startUrl() + '?' + qb.toString();
        if (immediate) {
            ifrm = new HiddenIframe();
            ifrm.setSrc(dest);
        } else {
            if (MOBILE && !stayInWindow) {
                WIN.top.location.href = dest;
            } else {
                pw = new PopupWindow(this.popupWidth,
                                     this.popupHeight,
                                     this.name);
                pw.show(dest);
                ii = WIN.setInterval(function () {
                    if (pw.window.closed) {
                        WIN.clearInterval(ii);
                        callback({stat: 'fail',
                                  err: {code: -1}
                                 });
                    }
                }, 1000);
            }
        }
    },

    startUrl: function () {
        return RPXNOW.config.appUrlPrefix + this.name + "/start";
    },

    providerName: function () {
        return this.name;
    }
};

defaultCallback = function(callback) {
    return function (resp) {
        if (resp.stat === 'ok') {
            if (isUndefined(callback.success)) {
                WIN.setTimeout(function() {
                    if (stayInWindow) {
                        WIN.location.href = resp.redirectUrl;
                    } else {
                        WIN.top.location.href = resp.redirectUrl;
                    }
                }, 1);
            } else {
                callback.success(resp.redirectUrl);
            }
        } else {
            if (isUndefined(callback.error)) {
                callback(resp.err);
            } else {
                callback.error(resp.err);
            }
        }
    };
};

/******  OpenID1Provider  *********/
OpenID1Provider = function (providerName, prefix, suffix, isSubdomain, popupWidth, popupHeight)
{
    BaseProvider.call(this, 'openid', popupWidth, popupHeight);
    this.prefix = prefix;
    this.suffix = suffix;
    this.isSubdomain = isSubdomain;
    this._immediate = {};
    this._providerName = providerName;
};

oid1Proto = OpenID1Provider.prototype = new BaseProvider();
oid1Proto.providerName = function() {
    return this._providerName;
};

oid1Proto.immediate = function (token_url) {
    var last, that, ident;
    if (ACTIVEX) {
        return;
    }
    last = CookieUserStateStore.getLastSuccessful();
    if (last.providerName === this.providerName()) {
        ident = this._inputToUrl(last.userInput);
        that = this;
        this._start(true, token_url, function (resp) {
            if (resp.stat === 'ok') {
                that._immediate.input = last.userInput;
                that._immediate.url = resp.redirectUrl;
            }
        }, {openid_identifier: ident,
            immediate: true});
    }
};

oid1Proto.start = function (user_input, token_url, callback, params) {
    if (!isNull(this._immediate.url) &&
        user_input === this._immediate.input) {
        if (stayInWindow) {
            WIN.location.href = this._immediate.url;
        } else {
            WIN.top.location.href = this._immediate.url;
        }
        return;
    }

    CookieUserStateStore.setLastUserInput(user_input);
    var ident = this._inputToUrl(user_input);
    var cb = defaultCallback(callback);

    if (isNull(params) || isUndefined(params)) params = {}
    params['openid_identifier'] = ident;

    this._start(false, token_url, cb, params);
};

oid1Proto._inputToUrl = function (user_input) {
    var parts, part, i;
    if (this.prefix.length > 0 || this.suffix.length > 0) {
        if (this.isSubdomain) {
            user_input = user_input.toLowerCase();
            parts = user_input.split('.');
            for (i = 0; i < parts.length; i++) {
                part = parts[i];
                part = part.replace(/[\x00-\x2C\x2E\x2F\x3A-\x60\x7B-\x7F]/g,'');
                parts[i] = part.replace(/(^-|-$)/g, '');
            }
            user_input = IDNAtoASCII(parts.join('.'));
        } else {
            user_input = user_input.replace(/^\s+|\s+$/g, "");
            user_input = encodeURI(user_input);
            user_input = user_input.replace('?', '%3F').replace('#', '%23');
        }
    }
    return this.prefix + user_input + this.suffix;
};

/******  OpenID2Provider  *********/
OpenID2Provider = function (providerName,
                            identifierSelectUrl,
                            popupWidth,
                            popupHeight)
{
    BaseProvider.call(this, 'openid', popupWidth, popupHeight);
    this._providerName = providerName;
    this.identifierSelectUrl = identifierSelectUrl;
    this.immediateUrl = null;
};

oid2Proto = OpenID2Provider.prototype = new BaseProvider();
oid2Proto.providerName = function() {
    return this._providerName;
};

oid2Proto.immediate = function (token_url) {
    if (ACTIVEX) {
        return;
    }

    var last = CookieUserStateStore.getLastSuccessful();
    if (last.providerName === this.providerName()) {
        var identifier = Cookies.get(this.providerName() + '_identifier');
        if (identifier) {
            var that = this;
            this._start(true, token_url, function (resp) {
                if (resp.stat === 'ok') {
                    that.immediateUrl = resp.redirectUrl;
                }
            }, {openid_identifier: identifier,
                immediate: true});
        }
    }
};

oid2Proto.start = function (token_url, callback, params) {
    if (!isNull(this.immediateUrl)) {
        if (stayInWindow) {
            WIN.location.href = this.immediateUrl;
        } else {
            WIN.top.location.href = this.immediateUrl;
        }
        return;
    }
    var identifier = Cookies.get(this.providerName() + '_identifier');
    if (!identifier) {
        identifier = this.identifierSelectUrl;
    }

    if (isNull(params) || isUndefined(params)) params = {}
    params['openid_identifier'] = identifier;

    var cb = defaultCallback(callback);
    this._start(false, token_url, cb, params);
};

oid2Proto.startFresh = function (token_url, callback, force_reauth) {
    var cb = defaultCallback(callback);
    this._start(false, token_url, cb, {
        openid_identifier: this.identifierSelectUrl,
        force_reauth: force_reauth
    });
};

/******  CustomProvider  *********/
CustomProvider = function (name, width, height) {
    BaseProvider.call(this, name, width, height);
};
custProto = CustomProvider.prototype = new BaseProvider();

custProto.start = function (token_url, callback, params) {
    var cb = defaultCallback(callback);
    this._start(false, token_url, cb, params);
};

custProto.startFresh = function (token_url, callback, force_reauth, params) {
    var cb = defaultCallback(callback);
    if (isNull(params) || isUndefined(params)) {
        params = { force_reauth: force_reauth };
    } else {
        params.force_reauth = force_reauth;
    }
    this._start(false, token_url, cb, params);
};

/******  CookieUserStateStore  *********/
CookieUserStateStore = {
    setLastLoginTab: function (login_tab) {
        var options = {
            expires: new Date(),
            path: '/'
        };
        options.expires.setDate(options.expires.getDate()+3650);
        Cookies.set('login_tab', login_tab, options);
    },

    setLastUserInput: function (user_input) {
        var options = {
            expires: new Date(),
            path: '/'
        };
        options.expires.setDate(options.expires.getDate()+3650);
        Cookies.set('user_input', user_input, options);
    },

    getLastSuccessful: function () {
        var result = {
            providerName: Cookies.get('expected_tab'),
            userInput: Cookies.get('expected_user_input'),
            userName: null
        };

        var welcome = Cookies.get('welcome_info');
        if (!isNull(welcome)) {
            try {
                var parsed = parseJSON(welcome);
                if (parsed[0] === "welcome_user") {
                    result.userName = parsed[1].name;
                }
            } catch(e) {}
        }
        return result;
    }
};

RPXNOW.Auth = {
    CustomProvider: CustomProvider,
    OpenID2Provider: OpenID2Provider,
    OpenID1Provider: OpenID1Provider,
    CookieUserStateStore: CookieUserStateStore,

    /****** Provider Intances ********/
    providers: {
        // OpenID 1
        aol:         new OpenID1Provider(
            'aol', 'http://openid.aol.com/', '', false, 514, 550),
        blogger:     new OpenID1Provider(
            'blogger', '', '', false, 800, 600),
        livejournal: new OpenID1Provider(
            'livejournal', 'http://', '.livejournal.com/', true, 800, 600),
        netlog:      new OpenID1Provider(
            'netlog', 'http://netlog.com/', '', false, 800, 600),
        openid:      new OpenID1Provider(
            'openid', '','', false, 800, 600),
        wordpress:   new OpenID1Provider(
            'wordpress', 'http://', '.wordpress.com/', true, 800, 600),

        // OpenID 2
        flickr:   new OpenID2Provider(
            'flickr', 'https://me.yahoo.com/', 500, 500),
        google:   new OpenID2Provider(
            'google', 'https://www.google.com/accounts/o8/id', 450, 500),
        hyves:    new OpenID2Provider(
            'hyves', 'http://hyves.nl/', 800, 600),
        myopenid: new OpenID2Provider(
            'myopenid', 'http://myopenid.com/', 800, 600),
        paypal:    new OpenID2Provider(
            //'paypal', 'https://openid.paypal-ids.com/', 800, 600),
            'paypal', 'https://www.paypal.com/webapps/auth/server', 800, 600),
        verisign: new OpenID2Provider(
            'verisign', 'http://pip.verisignlabs.com/', 800, 600),
        yahoo:    new OpenID2Provider(
            'yahoo', 'https://me.yahoo.com/', 500, 500),

        // Custom
        facebook:     new CustomProvider('facebook',   500, 500),
        live_id:      new CustomProvider('liveid',     900, 600),
        linkedin:     new CustomProvider('linkedin',   750, 550),
        myspace:      new CustomProvider('myspace',    800, 500),
        twitter:      new CustomProvider('twitter',    800, 500),
        salesforce:   new CustomProvider('salesforce', 800, 500),
        foursquare:   new CustomProvider('foursquare', 950, 550),
        vzn:          new CustomProvider('vzn',        600, 450),
        orkut:        new CustomProvider('orkut',      800, 600),
        mixi:   	  new CustomProvider('mixi',       950, 550)
    }
};


    RPXNOW.Util = {
        // classes
        LightBox: LightBox,
        PopupWindow: PopupWindow,
        QueryBuilder: QueryBuilder,

        // functions
        addListener: addListener,
        addClickListener: addClickListener,
        bind: bind,
        indexOf: indexOf,
        parseQueryString: parseQueryString
    };

    RPXNOW._xdCallbacks = [];
    registerXdCallback = function (callback) {
        var idx, callbackName;
        idx = RPXNOW._xdCallbacks.length;
        callbackName = "RPXNOW._xdCallbacks[" + idx + "]";
        RPXNOW._xdCallbacks.push(
            function (json) {
                var idx, parsed;
                idx = indexOf(RPXNOW._xdCallbacks, this);
                RPXNOW._xdCallbacks[idx] = null;

                parsed = parseJSON(json);
                callback(parsed);
            });

        return idx;
    };

    xdCall = function (action, args, callback) {
        var callbackName, faction, form, iframe, prefix;
        callbackName = registerXdCallback(callback);
        prefix = RPXNOW.config.appUrlPrefix.replace(/^https?/, SCHEME);
        faction = prefix + "jsapi/v3/" + action;
        form = new HiddenForm(faction, args);
        form.setHidden('xdReceiver', RPXNOW.config.xdReceiver);
        form.setHidden('callback', callbackName);

        iframe = new HiddenIframe();
        form.submitTo(iframe);
    };


    divPlusIFrame = function (div, name, width, height, bg_name) {
        div.innerHTML = ('<iframe src="' +
                         (IE6 ? RPXNOW.RPX_URL_PREFIX + 'blank.html' : '') +
                         '" name="' + name +
                         '" id="' + name +
                         '" style="width: ' + width + 'px; height: ' + height +
                         'px; background: transparent; position: absolute; ' +
                         'top: 0; left: 0; visibility: hidden; ' +
                         'display: none;" scrolling="no" frameBorder="0" ' +
                         'allowTransparency="true"><\/iframe>');

        setStyle(div, {
            backgroundColor: 'transparent',
            backgroundImage: socialImageCSS(bg_name),
            backgroundRepeat: 'no-repeat',
            backgroundPosition: 'top left',
            fontSize: '17px',
            textAlign: 'center',
            width: width + 'px',
            height: height + 'px',
            overflow: 'hidden',
            fontFamily: "'lucida grande', Helvetica, Verdana, sans-serif"
        });
    };

    showIFrameOnLoad = function (ifrm, span) {
        if (ACTIVEX) {
            ifrm.onreadystatechange = function(){
                if (ifrm.readyState === "complete"){
                    span.style.visibility = 'hidden';
                    ifrm.style.visibility = 'visible';
                    WIN.setTimeout(function() {
                        ifrm.style.display = 'block';
                    }, 1500);
                }
            };
        } else {
            ifrm.addEventListener('load', function () {
                ifrm.style.display = 'block';
                WIN.setTimeout(function () {
                    span.style.visibility = 'hidden';
                    ifrm.style.visibility = 'visible';
                }, 1);
            }, false);
        }
    };

    /* -*- javascript -*- */
var Social = RPXNOW.Social = {
    publishActivity: function (activity, options) {
        var loadingSpan;
        if (isNull(activity.title)) {
            activity.setTitle(DOC.title);
        }

        var name = getFrameName();
        var div = createElement('div');
        var lightbox = new LightBox(div, 423, 280);
        divPlusIFrame(div, name, 423, 280, 'bg_social');

        if (RPXNOW.config.hasError()) {
            var spacingDiv = createElement('div');
            setStyle(spacingDiv, {lineHeight: '280px',
                                  padding: '0px 13px',
                                  whiteSpace: 'nowrap'});
            spacingDiv.appendChild(DOC.createTextNode('Error: ' +
                                                      RPXNOW.config.error));

            div.appendChild(spacingDiv);
        } else {
            div.style.lineHeight = '280px';
            loadingSpan = createElement('span');
            loadingSpan.appendChild(DOC.createTextNode('Loading...'));
            div.appendChild(loadingSpan);
        }

        var ifrm = DOC.getElementById(name);

        // Parse config options
        // maintain backwards compatibility
        if (isFunction(options)) {
            options = { finishCallback: options };
        } else if (isUndefined(options)) {
            options = {};
        } else if (!isObject(options)) {
            throw new Error("options must be an associative array");
        } else {
            if (!isUndefined(options.finishCallback) &&
                !isFunction(options.finishCallback)) {
                throw new Error("finishCallback must be a function");
            }
            if (!isUndefined(options.exclusionList) &&
                !isArray(options.exclusionList)) {
                throw new Error("exclusionList must be an array");
            }
            if (!isUndefined(options.urlShortening) &&
                !isBoolean(options.urlShortening)) {
                throw new Error("urlShortening must be a boolean");
            }
            if (!isUndefined(options.postTruncation) &&
                !isBoolean(options.postTruncation)) {
                throw new Error("postTruncation must be a boolean");
            }
            if (!isUndefined(options.prependName) &&
                !isBoolean(options.prependName)) {
                throw new Error("prependName must be a boolean");
            }
            if (!isUndefined(options.primaryKey)) {
                if (!isString(options.primaryKey)) {
                    throw new Error("primaryKey must be a string");
                }
                if (isUndefined(options.timestamp)) {
                    throw new Error("timestamp required for primaryKey");
                }
                if (!isNumber(options.timestamp)) {
                    throw new Error("timestamp must be an integer");
                }
                if (isUndefined(options.signature)) {
                    throw new Error("signature required for primaryKey");
                }
                if (!isString(options.signature)) {
                    throw new Error("signature must be a string");
                }
            }
        }

        // If the user supplied a callback function to be called
        // when publishing is finished, register it now and track
        // the callback identifier throughout the publishing
        // process.
        var callbackName = null;
        if (!isFunction(options.finishCallback)) {
            options.finishCallback = function(unused) {};
        }

        // Wrap the user-specified callback.  Once the user clicks
        // 'Publish', the response will be a cross-domain callback
        // to this function; the function will call the users
        // callback AND set the location of the iframe to the page
        // that shows the results of the publish operation.
        var wrapped = function (json) {
            var cb = function () {
                options.finishCallback(json.publishResults);
            };

            if (!isNull(RPXNOW.config.tokenCallbackUrl) &&
                json.authTokens.length > 0) {
                var tokensForm = new HiddenForm(RPXNOW.config.tokenCallbackUrl);
                tokensForm.setHidden('tokens', json.authTokens);

                var tokensFrame = new HiddenIframe();
                tokensForm.setTarget(tokensFrame.getName());
                tokensFrame.addOnloadListener(cb);

                tokensForm.setMethod("POST");
                tokensForm.submit();
            } else {
                WIN.setTimeout(cb, 1);
            }
        };

        callbackName = registerXdCallback(wrapped);

        if (!RPXNOW.config.hasError()) {
            var sessionCB = function (resp) {
                var prefix, action, frm;
                prefix = RPXNOW.config.appUrlPrefix.replace(/^https?/, SCHEME);
                action = prefix + "social/publish_activity";
                frm = new HiddenForm(action);
                frm.setMethod("POST");
                frm.setHidden("activity", toJSON(activity));
                frm.setHidden("sessid", resp.sessid);
                frm.setHidden("appId", RPXNOW.config.appId);
                frm.setHidden("language_preference", RPXNOW.language_preference);
                if (!isUndefined(options.exclusionList)) {
                    // Pass a list of providers to be exluded from the widget
                    frm.setHidden("exclusionList", options.exclusionList.join());
                }
                if (!isUndefined(options.urlShortening)) {
                    // Are we using url shortening?
                    frm.setHidden("urlShortening", options.urlShortening);
                }
                if (!isUndefined(options.postTruncation)) {
                    // Are we to truncate posts that are too long for a provider?
                    frm.setHidden("postTruncation", options.postTruncation);
                }
                if (!isUndefined(options.prependName)) {
                    // Are we to prepend user names to actions (for Facebook)?
                    frm.setHidden("prependActor", options.prependName);
                }
                if (!isUndefined(options.primaryKey)) {
                    // Should we infer user's providers from the mappings?
                    frm.setHidden("primaryKey", options.primaryKey);
                    frm.setHidden("timestamp", options.timestamp);
                    frm.setHidden("signature", options.signature);
                }
                // Include the wrapped user callback name and XD
                // receiver URLs in the request so the 'submit' action
                // can render an XD response to invoke the callback.
                frm.setHidden("xdReceiver", RPXNOW.config.xdReceiver);
                frm.setHidden("callback", callbackName);
				
				// Set loginCallbackUrl for login events
                frm.setHidden("loginCallbackUrl", RPXNOW.config.loginCallbackUrl);

                frm.setTarget(name);

                frm.submit();
                showIFrameOnLoad(ifrm, loadingSpan);
            };

            if (RPXNOW.config.noXdReceiver) {
                // If the RP doesn't host a XD receiver, don't acquire
                // the session.  We won't be able to communicate with
                // the parent page, so we won't be able to close the
                // widget or call the finishCallback.
                sessionCB({ sessid: null});
            } else {
                xdCall('popup_session', {}, sessionCB);
            }
        }

        lightbox.show();

        return false;
    },

    clearSocialCookies: function (dest) {
        if (!isUndefined(dest)) {
            if (!isNull(dest) && !ABSOLUTE_URL_RE.test(dest)) {
                throw new Error("The dest must be an absolute URL");
            }
            var domainRe = new RegExp("^https?://" +
                                      regexpEscape(DOC.location.host) +
                                      "/");
            if (!isNull(dest) && !domainRe.test(dest)) {
                throw new Error("dest host does not match the current page.");
            }
        }
        var action = RPXNOW.config.appUrlPrefix + "social/logout";
        var form = new HiddenForm(action);
        form.setHidden("appId", RPXNOW.config.appId);

        var iframe = new HiddenIframe();
        form.setTarget(iframe.getName());

        if (!isUndefined(dest)) {
            iframe.addOnloadListener(function () {
                WIN.top.location.href = dest;
            });
        }

        form.submit();
        return false;
    }
};

Social.Activity = function (share_display, action, url) {
    if (!isString(share_display)) {
        throw new Error("a non-null share_display string is required.");
    }
    this.share_display = share_display;

    if (!isString(action)) {
        throw new Error("a non-null action string must be provided.");
    }
    this.action = action;

    if (!isString(url) || !ABSOLUTE_URL_FRAG_RE.test(url)) {
        throw new Error("a valid url must be provided.");
    }
    this.url = url;
    this.provider_urls = {};

    this.title = null;
    this.description = null;
    this.user_generated_content = null;

    this.properties = {};
    this.action_links = [];
    this.media = null;
};

Social.Activity.prototype = {
    setTitle: function (title) {
        if (!isString(title)) {
            throw new Error("title must be a string");
        }

        this.title = title;
    },

    setDescription: function (description) {
        if (!isString(description)) {
            throw new Error("description must be a string");
        }

        this.description = description;
    },

    setUserGeneratedContent: function (user_generated_content) {
        if (!isString(user_generated_content)) {
            throw new Error("user_generated_content must be a string");
        }
        this.user_generated_content = user_generated_content;
    },

    addActionLink: function (text, url) {
        if (!isString(url) || !ABSOLUTE_URL_FRAG_RE.test(url)) {
            throw new Error("a valid url must be provided.");
        }
        this.action_links.push({"text": text, "href": url});
    },

    addTextProperty: function (name, text) {
        if (!isString(name) || !isString(text)) {
            throw new Error("The name and text must be strings");
        }
        this.properties[name] = text;
    },

    addLinkProperty: function (name, text, url) {
        if (!isString(name) || !isString(text)) {
            throw new Error("The name and text must be strings");
        }
        if (!isString(url) || !ABSOLUTE_URL_FRAG_RE.test(url)) {
            throw new Error("a valid url must be provided.");
        }
        this.properties[name] = {"text": text, "href":url};
    },

    setMediaItem: function (mediaItem) {
        this.media = mediaItem.toMediaArray();
    },

    addProviderUrl: function (provider, url) {
        if (!isString(provider)) {
            throw new Error("a valid provider must be provided.");
        }
        if (!isString(url) || !ABSOLUTE_URL_FRAG_RE.test(url)) {
            throw new Error("a valid url must be provided.");
        }
        this.provider_urls[provider] = url;
    }
};

Social.Mp3MediaItem = function (src, title, artist, album) {
    if (!isString(src) || !ABSOLUTE_URL_RE.test(src)) {
        throw new Error("The src must be a valid url");
    }
    this.type = "mp3";
    this.src = src;

    if (isString(title)) {
        this.title = title;
    }

    if (isString(artist)) {
        this.artist = artist;
    }

    if (isString(album)) {
        this.album = album;
    }
};

Social.Mp3MediaItem.prototype = {
    toMediaArray: function () {
        return [this];
    },

    setTitle: function (title) {
        this.title = title;
    },

    setArtist: function (artist) {
        this.artist = artist;
    },

    setAlbum: function (album) {
        this.album = album;
    }
};

Social.VideoMediaItem = function (video_src, preview_img, video_link, video_title) {
    if (!isString(video_src) || !ABSOLUTE_URL_RE.test(video_src)) {
        throw new Error("video_src must be a valid url");
    }

    if (!isString(preview_img) || !ABSOLUTE_URL_RE.test(preview_img)) {
        throw new Error("preview_img must be a valid url");
    }

    this.type = "video";
    this.video_src = video_src;
    this.preview_img = preview_img;

    if (isString(video_link)) {
        if (!ABSOLUTE_URL_FRAG_RE.test(video_link)) {
            throw new Error("video_link must be a valid url if provided");
        }
    }

    if (isString(video_title)) {
        this.video_title = video_title;
    }
};

Social.VideoMediaItem.prototype = {
    toMediaArray: function () {
        return [this];
    },

    setVideoLink: function (video_link) {
        if (!isString(video_link) || !ABSOLUTE_URL_FRAG_RE.test(video_link)) {
            throw new Error("video_link must be a valid url");
        }
    },

    setVideoTitle: function (video_title) {
        this.video_title = video_title;
    }
};

Social.FlashMediaItem = function (swfsrc, imgsrc,
                                  width, height,
                                  expanded_width, expanded_height) {
    if (!isString(swfsrc) || !ABSOLUTE_URL_RE.test(swfsrc)) {
        throw new Error("swfsrc must be a valid url");
    }

    if (!isString(imgsrc) || !ABSOLUTE_URL_RE.test(imgsrc)) {
        throw new Error("preview_img must be a valid url");
    }

    this.type = "flash";
    this.swfsrc = swfsrc;
    this.imgsrc = imgsrc;

    if (isString(width) || isNumber(width)) {
        this.width = '' + width;
    }
    if (isString(height) || isNumber(height)) {
        this.height = '' + height;
    }
    if (isString(expanded_width) || isNumber(expanded_width)) {
        this.expanded_width = '' + expanded_width;
    }
    if (isString(expanded_height) || isNumber(expanded_height)) {
        this.expanded_height = '' + expanded_height;
    }
};

Social.FlashMediaItem.prototype = {
    toMediaArray: function () {
        return [this];
    },

    setWidth: function (width) {
        if (!isString(width) && !isNumber(width)) {
            throw new Error("width must be a number");
        }
        this.width = '' + width;
    },

    setHeight: function (height) {
        if (!isString(height) && !isNumber(height)) {
            throw new Error("height must be a number");
        }
        this.height = '' + height;
    },

    setExpandedWidth: function (expanded_width) {
        if (!isString(expanded_width) && !isNumber(expanded_width)) {
            throw new Error("expanded_width must be a number");
        }
        this.expanded_width = '' + expanded_width;
    },

    setExpandedHeight: function (expanded_height) {
        if (!isString(expanded_height) && !isNumber(expanded_height)) {
            throw new Error("expanded_height must be a number");
        }
        this.expanded_height = '' + expanded_height;
    }
};

Social.ImageMediaCollection = function () {
    this.images = [];
};

Social.ImageMediaCollection.prototype = {
    toMediaArray: function () {
        return this.images;
    },

    addImage: function (src, href) {
        if (this.images.length >= 5) {
            throw new Error("Cannot have more than 5 images.");
        }

        if (!isString(src) || !ABSOLUTE_URL_RE.test(src)) {
            throw new Error("src must be a valid url");
        }

        if (!isString(href) || !ABSOLUTE_URL_FRAG_RE.test(href)) {
            throw new Error("href must be a valid url");
        }

        this.images.push({"type":"image", "src": src, "href":href});
    }
};


    Config = function () {
        this.appId = null;
        this.xdReceiver = null;
        this.appUrlPrefix = null;
        this.error = null;
        this.tokenCallbackUrl = null;
        this.loginCallbackUrl = null;
    };

    Config.prototype = {
        initialize: function (appId, xdReceiver, tokenCallbackUrl, loginCallbackUrl) {
            this.appId = appId;
            this.xdReceiver = xdReceiver;
            this.tokenCallbackUrl = tokenCallbackUrl;
            this.loginCallbackUrl = loginCallbackUrl;
        },
        setError: function (msg) {
            this.error = msg;
        },
        hasError: function () {
            return !isNull(this.error);
        }
    };

    if ((isUndefined(RPXNOW.config)) || !RPXNOW.config) {
        RPXNOW.config = new Config();
    }

    appUrlPrefixCallbacks = [];
    withAppUrlPrefix = function (cb) {
        if (isNull(RPXNOW.config.appUrlPrefix)) {
            appUrlPrefixCallbacks.push(cb);
        } else {
            cb();
        }
    };

    RPXNOW.loadAndRun = function (moduleNames, callback) {
        var xdReceiver = RPXNOW.config.xdReceiver;
        var cbs = {
            success: function (unused) {
                withAppUrlPrefix(callback);
            },
            failure: function() {
                RPXNOW.config.setError("unable to load " + xdReceiver);
                withAppUrlPrefix(callback);
            }
        };
        if (contains(moduleNames, 'Social')) {
            // verify that xdReceiver is at least a url that returns a 200
            XHR.fetch(xdReceiver, cbs);
        } else {
            cbs.success();
        }
    };

    RPXNOW.init = function (params, maybeXdReceiver) {
	var appId, xdReceiver, domainRe, cbs, url, script, prefix,
        rpxUrlPrefix = null,
        tokenCallbackUrl = null,
        loginCallbackUrl = null,
        l = DOC.location;

        // XXX: once we get our test customers updated, we will get
        // rid of maybeXdReceiver param.

        if (isObject(params)) {
            appId = params.appId;
            xdReceiver = params.xdReceiver;
            rpxUrlPrefix = params.rpxUrlPrefix;
			tokenCallbackUrl = params.tokenCallbackUrl;
            loginCallbackUrl = params.loginCallbackUrl ? params.loginCallbackUrl : null;
	} else { // eventually we will remove this.
	    appId = params;
	    xdReceiver = maybeXdReceiver;
	}

        if (isUndefined(xdReceiver)) {
            xdReceiver = l.protocol + "//" + l.host + "/rpx_xdcomm.html";

        } else {
            if (ABSOLUTE_URL_RE.test(xdReceiver)) {
                domainRe = new RegExp("^https?://" +
                                          regexpEscape(l.host) + "/");
                if (!domainRe.test(xdReceiver)) {
                    throw new Error("RPXNow:init: " +
                                    "xdReceiver host does not match.");
                }
            } else if (ABSOLUTE_PATH_RE.test(xdReceiver)) {
                xdReceiver = l.protocol + "//" + l.host + xdReceiver;
            } else {
                throw new Error("RPXNow:init: xdReceiver must be either an absolute URL or a relative URL path starting with /.");
            }
        }

        if (rpxUrlPrefix) {
            // allows us to override DEFAULT in development
            RPXNOW.RPX_URL_PREFIX = rpxUrlPrefix;
        }

        RPXNOW.config.initialize(appId, xdReceiver, tokenCallbackUrl, loginCallbackUrl);


        // Make a JSONP call to get the URL prefix for appId
        prefix = RPXNOW.RPX_URL_PREFIX.replace(/^https?/, SCHEME);
        url = prefix + 'jsapi/v3/base_url';
        url += '?appId=' + appId;
        url += '&xdReceiver=' + WIN.escape(xdReceiver);

        script = createElement('script');
        script.src = url;
        script.type = "text/javascript";
        DOC.body.appendChild(script);
    };

    RPXNOW._base_cb = function(success, data) {
        if (success) {
            RPXNOW.config.appUrlPrefix = data;
        } else {
            RPXNOW.config.setError(data);
        }
        // once we have the appUrlPrefix, loop through
        // callbacks waiting for it.
        var cb = appUrlPrefixCallbacks.shift();
        while (isFunction(cb)) {
            cb();
            cb = appUrlPrefixCallbacks.shift();
        }
    };


    // From old widget
    RPXNOW.loaded = false;
    RPXNOW.show = function() {
        RPXNOW.show_on_load = true;
    };
    RPXNOW.always_open = false;
    RPXNOW.overlay = false;
    RPXNOW.language_preference = 'en';
    RPXNOW.default_provider = null;
    RPXNOW.lso_submit_action = null;
    RPXNOW.token_url = null;
    RPXNOW.realm = null;
    RPXNOW.domain = null;
    RPXNOW.flags = null;
    RPXNOW.bp_channel = null;
    RPXNOW.email = null;
    RPXNOW.openid_proxy_url = null;
    RPXNOW.ssl = null;
    RPXNOW._frame_count = (new Date()).getTime();

    getFrameName = function () {
        var c = RPXNOW._frame_count += 1;
        return "janrain_" + c;
    };

    COMMON_UI_PARAMS =
        ['token_url', 'language_preference', 'user_identifier', 'flags',
         'bp_channel', 'default_provider', 'email', 'openid_proxy_url'];

    genSigninUrl = function(token_url, domain) {
        var host, rp_id, query, scheme, signin_url, ofi;
        if (!ABSOLUTE_URL_RE.test(token_url)) {
            log(token_url);
            log("Error - token_url must be an absolute URL with no fragment.");
        }

        host = RPXNOW.RPX_URL_PREFIX.split("/")[2];
        rp_id = null;

        if (RPXNOW.rp_id) {
            rp_id = RPXNOW.rp_id;
        } else if (RPXNOW.domain) {
            host = RPXNOW.domain;
        } else if (RPXNOW.realm) {
            if (RPXNOW.realm.match(/\./)) {
                host = RPXNOW.realm;
            } else {
                host = RPXNOW.realm + "." + host;
            }
        } else if (domain) {
            host = domain;
        }

        query = new QueryBuilder();
        query.add('token_url', token_url);
        
        scheme = isNull(RPXNOW.ssl) ? SCHEME : (RPXNOW.ssl ? 'https' : 'http');
        signin_url = scheme + "://" + host + "/openid/embed?";

        if (rp_id) {
            query.add('rp_id', rp_id);
        }

        for (ofi = 0; ofi < COMMON_UI_PARAMS.length; ofi++) {
            var fieldname = COMMON_UI_PARAMS[ofi];
            if (fieldname !== 'token_url' && RPXNOW[fieldname]) {
                query.add(fieldname, RPXNOW[fieldname]);
            }
        }

        return signin_url + query.toString();
    };


    LOADING_STRINGS = {
        "bg":"Зареждане",
        "cs":"Načítání",
        "da":"Indlæser",
        "de":"Lade",
        "el":"loading",
        "en":"Loading",
        "es":"Cargando",
        "et":"Laetakse",
        "fi":"Ladataan",
        "fr":"Chargement",
        "hr":"Učitavanje",
        "hu":"Betöltés",
        "it":"Caricamento",
        "ja":"読み込んでいます",
        "ko":"로딩",
        "nl":"Laden",
        "no":"Laster",
        "pl":"Loading",
        "pt":"Carregando",
        "pt-BR":"Carregando",
        "ro":"Încărcare",
        "ru":"Загрузка",
        "sr":"Učitavam",
        "sv-SE":"Laddar",
        "uk":"Завантаження",
        "vi":"Đang tải",
        "zh":"载入中",
        "zh-CHT":"載入中"
    };

    addListener('load', WIN, function() {
        var i, arrElements, oRegExp, oElement, hookPopupToRpxLink;

        hookPopupToRpxLink = function(element) {
            var token_url, domain, href, domain_match;

            href = element.href;
            domain_match = href.match(/https?:\/\/([^\/]+)/);

            if (!RPXNOW.token_url) {
                token_url = parseQueryString(href).token_url;
            }
            if (domain_match !== null) {
                domain = domain_match[1];
            }

            element.onclick = function () {
                RPXNOW.show(token_url, domain);
                return false;
            };
        };

        RPXNOW.show = function (token_url, domain) {
            var width, height, name, div, loadingSpan, lightbox, loading_str, ifrm, url;

            if (LightBox.currentInstance !== null) {
                return false;
            }

            if (isUndefined(token_url)) {
                token_url = RPXNOW.token_url;
            }

            loading_str = LOADING_STRINGS[RPXNOW.language_preference];
            if (!loading_str) {
                loading_str = LOADING_STRINGS.en;
            }

            width = 373;
            height = 265;
            name = getFrameName();
            div = createElement('div');
            lightbox = new LightBox(div, width, height);
            divPlusIFrame(div, name, width, height, 'bg_auth');
            div.style.lineHeight = height + 'px';
            loadingSpan = createElement('span');
            loadingSpan.appendChild(DOC.createTextNode(loading_str + '...'));
            div.appendChild(loadingSpan);
            ifrm = DOC.getElementById(name);
            ifrm.style.marginTop = '12px';

            showIFrameOnLoad(ifrm, loadingSpan);
            lightbox.show();
            url = genSigninUrl(token_url, domain);

            ifrm.contentWindow.location.replace(url);

            if (!token_url) {
                log("Error - RPXNOW.token_url is undefined.");
            }
            return false;
        };

        arrElements = DOC.getElementsByTagName("a");
        oRegExp = new RegExp("(^|\\s)rpxnow(\\s|$)");
        for (i = 0; i < arrElements.length; i++) {
            oElement = arrElements[i];
            if(oRegExp.test(oElement.className)) {
                hookPopupToRpxLink(oElement);
            }
        }

        if (RPXNOW.show_on_load || RPXNOW.always_open) {
            RPXNOW.show();
        }
    });

    WIN.RPXNOW = RPXNOW;
}(this));

