/* Copyright (c) 2010 Topspin Media, Inc.  All rights reserved. */

/* Source: topspin_purchase.js, as of Mon Oct 04 13:46:13 -0700 2010 */


/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSConfig.js, last modified at Mon Oct 04 13:29:24 -0700 2010 */
TSConfig = {
    APP_URL:     "http://app.topspin.net/",
    SSL_APP_URL: "https://app.topspin.net/",
    STORE_URL:   "https://store.topspin.net/",
    FLASH_URL:   "http://app.topspin.net/flash/",
    IMAGE_URL:   "http://app.topspin.net/images/",
    CDN_URL:     "http://cdn.topspin.net/",
    BEACON_URL:  "http://px.topspin.net/ev?env=production"
};
if ("https:" == document.location.protocol) TSConfig.APP_URL = TSConfig.SSL_APP_URL;

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSPFFlash.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
/*	SWFObject v2.2 <http://code.google.com/p/TSswfobject/>
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
if(!window.TSswfobject) {
  var TSswfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in TSswfobject){TSswfobject[X]=null}TSswfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
}
/* END SWFOBJECT */

/* TSFLASH */
/**
 * Functions for embedding Flash elements on the page.
 * Flash movies currently in use must be recompiled to use the Flash methods.
 * Uses TSswfobject 2.0.
 */
TSFlash = {
    /**
     * Embeds a flash movie on the page. Requires a full path to the SWF, a container element,
     * and an options object. At a minimum, you need to pass options.width, options.height,
     * options.instance (a unique ID for the movie). If options.facing is not supplied, this
     * defaults to 'fan' facing, using fan redirects & min version standards. The other valid
     * option is currently 'artist'. Other optional params include options.redirectURL -- a custom
     * URL to redirect to that doesn't correspond to the stock fan- or artist- facing URL;
     * options.redirectParams -- appended to the end of the redirect URL.
     * If params are not supplied we will supply a high quality
     *
     * @param    swfName   the full path to the swf file to be embedded.
     * @param    container the element that the Flash movie will be embedded in.
     * @param    options   an object with options. Minimum requirement is width, height,
     *                     instanceName and loadParams.
     * @requires swfbject  TSswfobject 2.0 is required for embeds.
     */
    embed: function(swfName, container, options) {
        if (!options) {
            return;
        }
        if (typeof(options.params) == "undefined") {
		    options.params = {
		        quality: "high",
		        wmode: TSFlash.isLinux() ? "" : "transparent",
		        allowscriptaccess: "always",
		        menu: false
		    };
        }
        // redirect, etc have been pulled as TSswfobject 2.0 does not currently support it.
        // Variables should still be passed in but it's pulledfor lightness of code.
        TSswfobject.embedSWF(swfName, container, options.width, options.height, TSFlash.minVersion[options.facing], null, options.variables, options.params);
    },
    /**
     * Allows flash to call out to resize its parent container
     *
     * @param target        the id of element to be redimensioned
     * @param height        the new desired height
     * @param jsIdentifier  JS Identifier for widget making call
     */
    redimensionMovie: function(target, height, jsIdentifier){
        // TODO: convert to a JSON model of CSS atribs & values & change that way
        var elem = document.getElementById(target);
        height = parseInt(height) + 23;
        elem.style.height = height + "px";
        elem.parentNode.style.height = height + "px";
    },
    /**
     * Checks to see if minimum version of flash player is present for the supplied facing.
     * Assumes "fan" if no facing is supplied.
     *
     * @param  facing   the facing that needs to be checked for min flash version.
     * @return boolean  true if sufficient flash player version exists, false if not.
     */
    checkMinVersion: function(facing) {
        if (typeof(facing) == 'undefined') {
            facing = 'fan';
        }
        var version = TSswfobject.getFlashPlayerVersion();
        var reqVersions = TSFlash.minVersion[facing].split('.');
        var compatibleVersion = false;
        if (parseInt(version.major) > parseInt(reqVersions[0])) {
            // MAJ > MAJ -- compat
            compatibleVersion = true;
        } else {
            // check the minor version
            if (parseInt(version.minor) > parseInt(reqVersions[1]) && parseInt(version.major) == parseInt(reqVersions[0])) {
                // MAJ == MAJ; MIN > MIN -- compat
                compatibleVersion = true;
            } else {
                // check the revision
                if (parseInt(version.release) >= parseInt(reqVersions[2]) && parseInt(version.minor) == parseInt(reqVersions[1])) {
                    // MAJ == MAJ, MIN == MIN, REV > REV -- compat
                    compatibleVersion = true;
                }
            }
        }
        return compatibleVersion;
    },
    /**
     * Minimum flash versions needed for different areas of the site.
     */
    minVersion: {
        artist:"9.0.115",
        fan:"9.0.115"
    },
    isLinux: function() {
        var exp = /Linux/;
        return (exp.exec(navigator.platform));
    },
    /**
     * Javascript functions used by Flash players to transact. This may be moved to Widgets
     * eventually.
     */
    PlayerHooks: {
        /**
         * Transact is the primary player hook for flash and kicks off the purchase flow.
         *
         * @param JSONData  JSON string containing transactionMode (share or download),
         *                  event info, and APIDs to transact upon.
         */
        transact: function(JSONData) {
            var jsonObj = eval( "(" + JSONData + ")");
            (!jsonObj.jsIdentifier) ? ts_load_purch_flow(jsonObj.purchases, jsonObj.transactionMode) : ts_load_purch_flow(jsonObj.purchases, jsonObj.transactionMode, jsonObj.jsIdentifier);
        }
    }
};
/* END TSFLASH */

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSPF.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
// Do not execute the contents of this file if TSPurchase has already been
// initialized; this is done to prevent redundant processing when this file
// has been included more than once on the same page.
if (!window.TSPurchase) {
    /**
     * Namespace that wraps all TSPF (Topspin Purchase Flow) code.
     */
    var TSPF = {};
    /**
     * Topspin Purchase Flow Configuration - Default settings are here, can change various global settings for purchase flow.
     * Overide all purchase button configs here.
     */
    TSPF.Config = {
        theme : "",
        highlightColor : "",
        customStylesheetURL : "",
        keepShoppingLink : true
    };
    /**
     * Topspin Utilities - Generic dom utilities that can be reused
     */
    TSPF.Util = {
        /**
         * The DOM namespace covers basic DOM manipulation functions such as insertions and search functions.
         */
        DOM: {
            /**
             * Adds an event handler function as an event listener to a DOM element
             *
             * @param element  DOM element to which the event handler should be added
             * @param eventType  Event type excluding "on" prefix
             * @param handler  Event handler function
             */
            addEventHandler: function (element, eventType, handler) {
                if (typeof element.addEventListener != "undefined") {
                    element.addEventListener(eventType, handler, false); // DOM2
                } else if (typeof element.attachEvent != "undefined") {
                    element.attachEvent("on"+eventType, handler); // IE
                }
            },
            /**
             * Removes an event handler function from a DOM element
             *
             * @param element  DOM element from which the event handler should be removed
             * @param eventType  Event type excluding "on" prefix
             * @param handler  Event handler function
             */
            removeEventHandler: function (element, eventType, handler) {
                if (typeof element.removeEventListener != "undefined") {
                    element.removeEventListener(eventType, handler, false); // DOM2
                } else if (typeof element.detachEvent != "undefined") {
                    element.detachEvent("on"+eventType, handler); // IE
                }
            }
        },
        /**
         * Creates a link element that points to a stylesheet and appends to document head
         *
         * @param id  DOM element id
         * @param url  URL to stylesheet used foir href
         */
        createStyleSheet: function(id, url) {
            var styleTag = document.createElement('link');
            styleTag.media = 'screen';
            styleTag.rel = 'stylesheet';
            styleTag.type = 'text/css';
            styleTag.id = id; //'topspin_style_addition';
            styleTag.href = url; //TSConfig.CDN_URL + "stylesheets/widget.css";
            var headNode = document.getElementsByTagName('head')[0];
            var headChild = headNode.firstChild;
            headNode.insertBefore(styleTag, headChild);
        }
    };

    /**
    // Topspin Buy Button - Parses embed codes, adds events and beacons, and loads PF iframe
     */
    TSPF.BuyButton = function() {
        /**
         * private var used for cloing img elements in insertBeaconImg
         */
        var genericImgElement;

        /**
         * Returns a hash of key value pairs parsed from the provided URL string
         *
         * @param url  The URL string to parse
         * @return Hash of parsed query string parameters
         */
        function getQueryParams(url) {
            url = url || window.location.href;
            // Strip the url string trailing and leading whitespace
            url = url.replace(/^\s+/, '').replace(/\s+$/, '');
            // Separate out the query string from the rest of the url
            var match = url.match(/([^?#]*)(#.*)?$/);
            if (!match) {
                return {};
            }

            var paramsHash = {};
            var pairs = match[1].split("&");
            for (var i = 0; i < pairs.length; i++) {
                var pair = pairs[i].split("=");
                if (pair[0]) {
                    var key = decodeURIComponent(pair[0]);
                    var value = pair[1];
                    if (value) {
                        value = decodeURIComponent(value);
                    }
                    if (key in paramsHash) {
                        if (!(paramsHash[key] instanceof Array)) {
                            paramsHash[key] = [paramsHash[key]];
                        }
                        paramsHash[key].push(value);
                    } else {
                        paramsHash[key] = value;
                    }
                }
            }
            return paramsHash;
        }

        /**
         * Inserts a new beacon img element into the document using the provided parameters
         *
         * @param Hash of parameters used to construct the beacon img src URL
         */
        function insertBeaconImg(params) {
            if (!genericImgElement) {
                genericImgElement = document.createElement('img');
                genericImgElement.className = "invisible_image";
            }
            var beaconImg = genericImgElement.cloneNode(true);
            beaconImg.src = TSConfig.BEACON_URL +
                            "&et=0&es=2&c=" + params['cId'] +
                            "&u=" + encodeURIComponent(window.location.href.toString()) +
                            "&timestamp=" + new Date().getTime();
            var bodyNode = document.getElementsByTagName('body')[0];
            bodyNode.appendChild(beaconImg);
        }

        /**
         * Used to bind the params to the onclick handler
         *
         * @params purchase button params object
         * @return anonymous function to fire off TSPF.BuyButton.load
         */
        function createStoreOnClick (params) {
            var onclick = function () { TSPF.BuyButton.load(params); return false; };
            return onclick;
        }

        /**
         * Initializes any buy buttons found on the page
         *
         * @return The artist ID associated with the last buy button; otherwise null
         */
        function initBuyButtons() {
            var artistId;
            // Cycle through all anchor tags on the page; looking first for any anchors linking to a topspin
            // purchase landing page (these anchors are reconfigured to open the purchase flow within the page),
            // and second for any anchors that contain onclick event handlers that load the purchase flow. For all
            // anchors found of either type, a tracking pixel will be inserted into the page for the campaign associated
            // with the anchor, and the artistId will be set.
            var allAnchors = document.getElementsByTagName('a');
            for (var i=0; i < allAnchors.length; i++) {
                var params = null;
                var anchor = allAnchors[i];
                // TODO: Need to make this check less brittle
                if (anchor.href && anchor.href.indexOf('.topspin.net/store/') >= 0) {
                    params = getQueryParams(anchor.href);
                    anchor.onclick = createStoreOnClick(params);
                    //TSPF.Util.DOM.addEventHandler(anchor, "click", createStoreOnClick(params));
                    //anchor.href = "javascript:void(0);";
                } else {
                    var onclickStr = new String(anchor.onclick);
                    if (onclickStr.indexOf('TSPF.BuyButton.load') >= 0) {
                        params = eval(onclickStr.substring(onclickStr.indexOf('TSPF.BuyButton.load') + 'TSPF.BuyButton.load'.length, onclickStr.length-1));
                    }
                }
                if (params) {
                    insertBeaconImg(params);
                    if (!artistId && params["aId"]) {
                        artistId = params["aId"];
                    }
                }
            }
            return artistId;
        }

        /**
         * Handles initial set up for purchase flow related elements included on the page
         */
        function init() {
            if (!document.getElementById('topspin_style_addition')) {
                // Insert the custom TS CSS style node into the head
                TSPF.Util.createStyleSheet('topspin_style_addition', TSConfig.CDN_URL + "stylesheets/widget.css");
                // Initialize all buy buttons on the page
                var artistId = initBuyButtons();
                // If no artist ID was found in the buy buttons, attempt to unearth one from the
                // query params set on the topspin_purchase.js script tag
                if (!artistId) {
                    var scriptNodes = document.getElementsByTagName('script');
                    for (var i=0; i < scriptNodes.length; i++) {
                        var url = scriptNodes[i].src;
                        if (url.indexOf("topspin_purchase.js") >= 0) {
                            var urlParams = getQueryParams(url);
                            if (urlParams["aId"]) {
                                artistId = urlParams["aId"];
                            }
                        }
                    }
                }
                // Initialize the shopping cart for the page
                if (artistId) {
                    TSCart.init(artistId);
                }

                // Open the purchase flow immediately if the appropriate parameters have been passed
                // in the query string of the window URL
                var windowParams = getQueryParams();
                if (windowParams.aId && windowParams.cId && windowParams.wId) {
                    TSPF.BuyButton.load(windowParams);
                    return;
                }
                if (windowParams.aId && windowParams.tspf_action) {
                    TSPF.BuyButton.load(windowParams);
                    return;
                }
            }
        }

        function embedPurchaseFlow(params, containerId) {
            var artistId    = params["artistId"] || 1; // TODO: Remove this hack
            var campaignId  = params["campaignId"] || "";
            var action      = params["action"];

            // Theme can be overridden via JS config
            var theme = TSPF.Config.theme || params["theme"] || "";
            // HighlightColor can be overridden via JS config
            var highlightColor = TSPF.Config.highlightColor || params["highlightColor"] || "";

            var src = params["src"] || "";
            var width = 480;
            var height = 570;

            var paypalReturnURLBase = TSConfig.SSL_APP_URL + "api/v2/shop";

            var options = {
                facing: 'fan',
                variables: {
                    baseUrl: TSConfig.SSL_APP_URL.replace(/\/$/,''),
                    cdnUrl: TSConfig.CDN_URL.replace(/\/$/,''),
                    ppUrl: paypalReturnURLBase,
                    artistId: artistId,
                    campaignId: campaignId,
                    theme : theme,
                    highlightColor : highlightColor,
                    movieId: containerId,
                    action:action,
                    src: src,
                    pci: true
                },
                params: {
                    quality: "high",
                    allowscriptaccess: "always",
                    menu: "false",
                    bgcolor: "#000000"
                },
                width: width,
                height: height,
                instance: containerId
            };

            this.renderFrame(options);
        }

        function loadPurchaseFlow(params) {
            // If this is Safari Mobile, open new window with mobile PF
            var isMobileSafari = navigator.userAgent.match(/iPhone|iPod|iPad/i);
            if (isMobileSafari) {
                var pfURL = TSConfig.STORE_URL +
                            "purchase_flow/configure?campaign_id=" + params["campaignId"];
                if (TSPF.Cart.orderId) {
                    pfURL += "&order_id=" + TSPF.Cart.orderId + "&token=" + TSPF.Cart.token;
                }
                pfURL += "&parent_url=" + encodeURI(window.location.href);
                window.open(pfURL, "ts_purchase");
                return false;
            }

            var lightBox = TSPF.LightBox.render();
            var lightBoxWrapper = lightBox.parentNode;

            TSPF.BuyButton.embedPurchaseFlow(params, lightBox.id);

            function adblockCheck() {
                var abCheck = lightBoxWrapper.getElementsByTagName('a');
                var hideFlag = false;
                for (var i=0;i<abCheck.length;i++) {
                    if (abCheck[i].className.indexOf('abp-obj') >= 0) {
                        hideFlag = true;
                    }
                }
                if (hideFlag) {
                    for (var i=0;i<abCheck.length;i++) {
                        if (hideFlag && abCheck[i].className.indexOf('abp-obj') == -1) {
                            abCheck[i].style.left = "0px";
                            abCheck[i].style.cssFloat = "left";
                            abCheck[i].style.textAlign = "left";
                            abCheck[i].parentNode.style.textAlign = "left";
                        }
                    }
                }
            }
            setTimeout(adblockCheck, 100);
        }

        function renderFrame(options) {
            var lightBox = document.getElementById("ts_ol_pflow");

            //var clientUrl = encodeURIComponent( document.location.href );
            var iframeURL;
            if(options.variables.action) {
                // going to a specific action
                iframeURL = TSConfig.STORE_URL + "purchase_flow/" + options.variables.action + "?";
            } else if (!options.variables.campaignId) {
                // There is no campaign to configure; load the cart directly
                iframeURL = TSConfig.STORE_URL + "purchase_flow/cart?";
            } else {
                // configuration page
                iframeURL = TSConfig.STORE_URL + "purchase_flow/configure?" +
                            "campaign_id=" + options.variables.campaignId + "&";
            }

            iframeURL += "artist_id=" + options.variables.artistId +
                         "&theme=" + options.variables.theme +
                         "&highlight_color=" + encodeURIComponent(options.variables.highlightColor);

            if (TSPF.Cart.orderId) {
                iframeURL += "&order_id=" + TSPF.Cart.orderId + "&token=" + TSPF.Cart.token;
            }

            if (TSPF.Config.customStylesheetURL !== "") {
                iframeURL += "&custom_stylesheet_url=" + encodeURIComponent(TSPF.Config.customStylesheetURL);
            }

            iframeURL += "&parent_url=" + encodeURI(window.location.href);

            TSPF.LightBox.renderFrame(iframeURL, lightBox);

            // Add a message receiver responsible for listening to messages sent back from the PF
            // iFrame to close the PF LightBox
            if (window.TSMessenger) {
                TSMessenger.addReceiver(
                    function(e, jsonData) {
                        if (jsonData && jsonData["tspf_action"] == "close_pf") {
                            TSMessenger.removeReceiver("closePFReceiver");
                            TSPF.LightBox.close();
                        }
                    },
                    TSConfig.STORE_URL.replace(/\/$/,""),
                    "closePFReceiver"
                );
            }
        }

        function showFrame() {
            var iFrame = document.getElementById("ts_pflow_frame");
            iFrame.style.display = "";
        }

        /**
         * Adds a campaign directly to the cart without displaying the purchase
         * flow. Note, the campaign being added must not require configuration.
         *
         * @param params  Hash of parameters to pass to the server. Note,
         *                campaign_id parameter is required.
         */
        function addToCart(params) {
            params.auto_add = true;
            params.artist_id = params.artist_id || TSPF.Cart.artistId;
            if (TSPF.Cart.orderId) {
                params.order_id = TSPF.Cart.orderId;
                params.token    = TSPF.Cart.token;
            }
            new Ajax.JSONRequest(TSConfig.STORE_URL + "purchase_flow/configure", {
                callbackParamName: "jsoncallback",
                parameters: params,
                onSuccess: function(transport) {
                    var token    = transport.responseJSON.token;
                    var order_id = transport.responseJSON.order_id;
                    //alert(token == TSPF.Cart.token)
                    TSPF.Cart.orderId = order_id;
                    TSPF.Cart.token   = token;
                    TSPF.Cart.reloadCallbackSWF();
                },
                onFailure: function(transport) {
                    //alert("fail" + transport.responseText);
                },
                onException: function(transport, e) {
                    //alert(transport.responseText + e);
                }
            });
            return false;
        }


        return {
            initialize: init,

            initializeBuyButtons: initBuyButtons,

            load: function(params) {
                TSPF.BuyButton.loadPurchaseFlow({artistId:params.aId, campaignId:params.cId, theme:params.theme, highlightColor:params.highlightColor, action:params.tspf_action});
            },

            embed: function(params, containerId) {
                // TODO: Eliminate this hackery for FB store V1
                var src = getQueryParams()["is_facebook"] ? "tsfb" : null;
                TSPF.BuyButton.embedPurchaseFlow({artistId:params.aId, campaignId:params.cId, theme:params.theme, highlightColor:params.highlightColor, src:src}, containerId);
            },
            embedPurchaseFlow: embedPurchaseFlow,
            loadPurchaseFlow: loadPurchaseFlow,
            renderFrame: renderFrame,
            postMessageCallback: function(event) {
                var msg = event.data
                if (msg === "addtocart"){
                    TSPF.Cart.setItemCount(1);
                }

                if (msg === "hide"){
                    TSPF.Cart.hide();
                }
            },

            addToCart: addToCart
        };
    }();

    /**
     * Topspin Light Box - Creates modal dialog/lightbox on an artist page.
     */
    TSPF.LightBox = {
        /**
         * Renders empty modal light box - temp
         *
         * @param id  The identifier used to create the light box media container
         * @param url  The iframe URL to load into the lightbox. This param is optional
         * @return The light box media container element
         */
        render: function (id, url, message) {
            if (TSCart.isReady()) {
                TSCart.hide();
            }

            id      = id || 1;
            message = message || "KEEP SHOPPING";

            TSPF.LightBox.showOverlay();

            var wrapper = document.getElementById('ts_ol_pflow');
            if (wrapper != null) {
                wrapper.parentNode.removeChild(wrapper);
            }
            wrapper = document.createElement('div');
            wrapper.id = "ts_ol_pflow";
            wrapper.className = "ts_download_form";

            var tBar = document.createElement('div');
            tBar.id = "ts_pflow_control";
            var tBarLink = document.createElement('a');
            tBarLink.href = "javascript:void(0);";
            var closeURL = TSConfig.CDN_URL + "images/widgetSupport/purchFlow/close1.gif";
            tBarLink.innerHTML = "<img src='"+closeURL+"' border='0' alt='Close'/>";
            TSPF.Util.DOM.addEventHandler(tBarLink, "click", TSPF.LightBox.close);
            tBar.appendChild(tBarLink);

            // setup Keep Shopping link
            if (TSPF.Config.keepShoppingLink) {
                var keepShoppingLink = document.createElement('a');
                keepShoppingLink.id = "keep_shopping";
                keepShoppingLink.href = "javascript:void(0);";
                keepShoppingLink.innerHTML = message;
                TSPF.Util.DOM.addEventHandler(keepShoppingLink, "click", TSPF.LightBox.close);
                tBar.appendChild(keepShoppingLink);
            }

            var contentBox = document.createElement('span');
            contentBox.id = "pf_alt_" + id;
            contentBox.className = "TSFlash_alt";
            contentBox.style.display = "block";
            contentBox.style.position = "absolute";
            contentBox.style.fontSize = "12px";
            contentBox.style.fontFamily = "Verdana, Arial, Helvetica";
            contentBox.style.backgroundColor = "transparent";
            contentBox.style.width = "100%";
            contentBox.style.border = "none";
            contentBox.style.color = "#ffffff";
            contentBox.innerHTML = "";

            wrapper.appendChild(tBar);
            wrapper.appendChild(contentBox);

            var yOffset;
            if (window.pageYOffset) {
                // Not IE
                yOffset = window.pageYOffset;
            } else if (document.documentElement && document.documentElement.scrollTop) {
                // IE strict mode
                yOffset = document.documentElement.scrollTop;
            } else {
                // IE other
                yOffset = document.body.scrollTop;
            }
            wrapper.style.top = 80 + parseInt(yOffset) + "px";

            var clientWidth = document.body.clientWidth;
            if (!clientWidth && document.documentElement && document.documentElement.clientWidth) {
                // Fallback for IE
                clientWidth = document.documentElement.clientWidth;
            }
            wrapper.style.left = clientWidth/2 - 240 + "px";

            var bodyNode = document.getElementsByTagName('body')[0];
            bodyNode.insertBefore(wrapper, bodyNode.firstChild.nextSibling);

            // Declare event handlers within global scope so they can be referenced when removing
            TSLightBoxResizeHandler = function () {
                TSPF.LightBox.centerPane("ts_ol_pflow", 212);
                TSPF.LightBox.redimensionOverlay();
            };
            TSPF.Util.DOM.addEventHandler(window, "resize", TSLightBoxResizeHandler);

            if (url) {
                TSPF.LightBox.renderFrame(url, wrapper);
            }

            return contentBox;
        },
        /**
         * Closes a modal light box
         *
         * @param id  The identifier used to create the light box media container
         */
        close: function (id) {
            var pf = document.getElementById("ts_ol_pflow");
            TSPF.Util.DOM.removeEventHandler(window, "resize", TSLightBoxResizeHandler);
            TSLightBoxResizeHandler = null;

            TSPF.LightBox.hideOverlay();
            pf.style.display = "none";

            if (TSCart.isReady()) {
                TSCart.hide();
                TSCart.reloadCallbackSWF();
            }
        },
        renderFrame: function (url, lightbox) {
            lightBox = lightbox || document.getElementById("ts_ol_pflow");

            // TODO: Move the styles off to the CSS file (except for "display")
            var iFrame = document.createElement("iframe");
            iFrame.id                = "ts_pflow_frame";
            iFrame.name              = "ts_pflow_frame";
            iFrame.style.display     = "none";
            iFrame.style.width       = "480px";
            iFrame.style.height      = "570px";
            iFrame.style.background  = "#000";
            iFrame.style.borderWidth = "0";
            iFrame.frameBorder       = "0"; // For IE
            //iFrame.allowTransparency = "true"; // For IE

            TSPF.Util.DOM.addEventHandler(iFrame, "load", TSPF.LightBox.showFrame);

            // Append the new iFrame
            iFrame = lightBox.appendChild(iFrame);
            // Set the iFrame src after the element has been appended to avoid
            // some src url caching issues in IE
            iFrame.src = url;
        },
        showFrame: function () {
            var iFrame = document.getElementById("ts_pflow_frame");
            iFrame.style.display = "";
        },
        /**
        * Various methods below to getDimensions, hide and show overlays, and center. All are used to display lightbox
        * TODO jdavis, document these and move dimension getters and centering methods to Util where appropriate.
        **/
        getOverlayDimensions: function() {
            var windowWidth;
            var windowHeight;
            if (window.innerWidth) {
                // Not IE
                windowWidth = window.innerWidth;
                windowHeight = window.innerHeight;
            } else if (document.documentElement && document.documentElement.clientWidth) {
                // IE Strict Mode
                windowWidth = document.documentElement.clientWidth;
    		    windowHeight = document.documentElement.clientHeight;
            } else {
                // Everyone else
                windowWidth = document.body.clientWidth;
                windowHeight = document.body.clientHeight;
            }

            var bodyWidth;
            var bodyHeight;
            if (window.innerHeight && window.scrollMaxY) {
                // Not IE
                bodyWidth = window.innerWidth + window.scrollMaxX;
                bodyHeight = window.innerHeight + window.scrollMaxY;
            } else if (document.body.scrollHeight > document.body.offsetHeight) {
                // All but IE mac
                bodyWidth = document.body.scrollWidth;
                bodyHeight = document.body.scrollHeight;
            } else {
                // Everyone else
                bodyWidth = document.body.offsetWidth;
                bodyHeight = document.body.offsetHeight;
            }

            var overlayWidth  = (windowWidth  > bodyWidth)  ? windowWidth  : bodyWidth;
            var overlayHeight = (windowHeight > bodyHeight) ? windowHeight : bodyHeight;

            /*
             * Returns the largest element on the page (in terms of pixel height)
             * regardless of display, position, or visibility CSS style settings
             */
            function getMaxElementHeight() {
                var arr = document.getElementsByTagName("*");
                var max = 0;

                function getDimensions(element) {
                    var display = element.style.display;
                    if (display != 'none' && display != null) { // Safari bug
                        return {width: element.offsetWidth, height: element.offsetHeight};
                    }
                    // All *Width and *Height properties give 0 on elements with display none,
                    // so enable the element temporarily
                    var els = element.style;
                    var originalVisibility = els.visibility;
                    var originalPosition = els.position;
                    var originalDisplay = els.display;
                    els.visibility = 'hidden';
                    els.position = 'absolute';
                    els.display = 'block';
                    var originalWidth = element.clientWidth;
                    var originalHeight = element.clientHeight;
                    els.display = originalDisplay;
                    els.position = originalPosition;
                    els.visibility = originalVisibility;
                    return { width: originalWidth, height: originalHeight };
                }

                for (var i=0; i < arr.length; i++) {
                    var h = getDimensions(arr[i]).height;
                    if (h > max) { max = h; }
                }

                return max;
            }

            if (bodyHeight == 0 || bodyHeight == windowHeight) {
                overlayHeight = getMaxElementHeight();
            }

            var overlayDimensions = {
                width: overlayWidth,
                height: overlayHeight
            };
            return overlayDimensions;
        },
        redimensionOverlay: function() {
            var overlay = document.getElementById('TSOverlayBGDiv');
            var overlayDims = TSPF.LightBox.getOverlayDimensions();
            overlay.style.height = overlayDims.height + "px";
            overlay.style.width = overlayDims.width + "px";
        },
        showOverlay: function() {
            TSPF.LightBox.hideFlash();
            var overlay = document.getElementById('TSOverlayBGDiv');

            if (!overlay) {
                overlay = document.createElement('div');
                overlay.id = "TSOverlayBGDiv";
                overlay.innerHTML = "&nbsp;";

                var dBody = document.getElementsByTagName('body')[0];
                dBody.appendChild(overlay, dBody);
            }

            // NOTE: Setting the dimensions for the overlay is primarily for < IE6;
            //       this step should not be required for browsers that support
            //       fixed positioned elements.
            TSPF.LightBox.redimensionOverlay();

            overlay.style.display = "block";
        },
        hideOverlay: function() {
            var overlay = document.getElementById('TSOverlayBGDiv');
            if (overlay) {
                overlay.style.display = "none";
            }
            TSPF.LightBox.showFlash();
        },
        showFlash: function() {
             var flashNodes = document.getElementsByTagName('object');
             for (var i=0;i<flashNodes.length;i++) {
                 flashNodes[i].parentNode.style.textIndent = "0px";
                 if (flashNodes[i].style.visibility == "hidden") {
                     flashNodes[i].style.visibility = "visible";
                 }
             }
         },
         hideFlash: function() {
             var flashNodes = document.getElementsByTagName('object');
             for (var i=0;i<flashNodes.length;i++) {
                 var flashNode = flashNodes[i];
                 if (flashNode.style.visibility != "hidden" && flashNode.id != "pf_alt_1") {
                     flashNode.style.visibility = "hidden";
                     flashNode.parentNode.style.textIndent = "-9999px";
                 }
             }
        },
        centerPane : function(elementId, offset) {
            var ePane = document.getElementById(elementId);
            var val = (parseInt(document.body.clientWidth))/2 - offset + "px";
            ePane.style.left = val;
        },
        paneScroll: function(elementId, offset) {
            var ePane = document.getElementById(elementId);
            var ua = new String(navigator.userAgent);
            var rawVal;
            if (ua.indexOf("MSIE") > -1) {
                rawVal = parseInt(document.documentElement.scrollTop);
            } else {
                rawVal = parseInt(window.pageYOffset);
            }
            var val = offset + rawVal + "px";
            ePane.style.top = val;
        }
    };

    /**
     * Topspin Cart - Sets up mini cart on artist page and allows for custom configuration of this cart
     */
    TSPF.Cart = function() {
        // this will be updated via flash by setItemCount
        var itemCount = 0;

        /**
         * Utility method that returns the body element of the current document.
         */
        function getDocumentBody() {
            return document.body;
        }

        function createCallbackSWF(params) {
            params = params || {};

            var swfDiv = document.getElementById("TSCart_swf_div");
            if (!swfDiv) {
                var swfDivWrap = document.createElement("div");
                document.body.appendChild(swfDivWrap);
                swfDiv = document.createElement("div");
                swfDiv.id = "TSCart_swf_div";
                swfDivWrap.id = "TSCart_swf_div_wrap";
                swfDivWrap.style.height = "0";

                // move swf container off the page
                swfDivWrap.style.position = "absolute";
                swfDivWrap.style.left = "-9999px";
                // swfDivWrap.style.textIndent = "-9999px";

                swfDivWrap.appendChild(swfDiv);
            }

            var width = 1;
            var height = 1;
            var widgetURI = TSConfig.STORE_URL + "widgets/purchase/TSShopProxy.swf";

            var options = {
                facing: 'fan',
                variables: {
                    baseUrl:  TSConfig.STORE_URL.replace(/\/$/,''),
                    artistId: params.artistId || this.artistId,
                    orderId:  params.orderId,
                    token:    params.token
                },
                params: {
                    quality: "high",
                    allowscriptaccess: "always",
                    allowfullscreen: "true",
                    movie: widgetURI
                },
                width: width,
                height: height,
                instance: swfDiv.id
            };

            TSFlash.embed(widgetURI, swfDiv.id, options);
        }

        // prepend Cart to body contents
        function createCart() {
            if (isIE6()) {
                createIE6Cart();
            } else {
                var body = getDocumentBody();
                // loop through body child nodes, grab the first element
                for (var node in body.childNodes) {
                    if (body.childNodes[node].nodeType == 1) {
                        var first = body.childNodes[node];
                        break;
                    }
                }
                var headerWrap = document.createElement("div");
                headerWrap.id = "TSCart_header_wrap";
                headerWrap.style.display = "none";

                body.insertBefore(headerWrap, first);
                headerWrap.innerHTML = getCartHTML();
            }
        }

        function createIE6Cart() {
            //alert("fail, you are using IE...guess we'll have to make do!");
            var body = getDocumentBody();
            // inject cart + body contents into an ie6 wrapper
            var contents = body.innerHTML;

            var ie6Wrap = document.createElement("div");
            ie6Wrap.id = "TSCart_IE6_wrap";

            // empty body
            body.innerHTML = null;
            body.innerHTML = "\n";

            //set HTML style
            body.parentElement.style.height = "100%";
            body.parentElement.style.overflow = "hidden";
            //set BODY style
            body.style.height = "100%";
            body.style.overflow = "hidden";
            body.appendChild(ie6Wrap);

            contents = '<div id="TSCart_header_wrap" style="display:none;">' + getCartHTML() + '</div>' + contents;
            ie6Wrap.innerHTML = contents;
        }

        // someday we can throw away all this ie6 code
        function isIE6() {
            return window.XMLHttpRequest ? false : true;
            //return (typeof document.addEventListener != 'function');
        }

        function getCartHTML() {
            return '<div id="TSCart_header_container"><div id="TSCart_header">' +
                   '<a href="javascript:TSCart.loadPurchaseFlow();"><img src="' + TSConfig.CDN_URL + 'images/widgetSupport/purchFlow/cart.png" style="border:none;" /></a>' +
                   '<p><a href="javascript:TSCart.loadPurchaseFlow();">You have <span id="TSCart_num_items">0 items</span> in your cart&nbsp;' +
                   '|&nbsp;Check Out</a>' +
                   '</p></div></div>';
        }

        return {
            init: function(artistId) {
                this.artistId = artistId;
                this.orderId = "";
                this.token = "";
                createCallbackSWF({artistId:artistId, orderId:"", token:""});
                TSPF.Util.createStyleSheet('TSCart_css', TSConfig.CDN_URL + "stylesheets/TSCart.css");
                createCart();
            },
            isReady: function() {
                return this.artistId ? true : false;
            },
            hide: function() {
                var ele = document.getElementById("TSCart_header_wrap");
                ele.style.display = "none";
            },
            show: function() {
                var ele = document.getElementById("TSCart_header_wrap");
                ele.style.display = "block";
            },
            setItemCount: function(count) {
                itemCount = count || 0;
                this.update();
            },
            getItemCount: function() {
                return itemCount;
            },
            update: function(params) {
                this.orderId = params.order_id || "";
                this.token   = params.token    || "";

                var count = params.item_count || this.getItemCount();
                var ele = document.getElementById("TSCart_num_items");
                if (ele) {
                    var itemString = (count > 1) ? " items" : " item";
                    ele.innerHTML = count + itemString;
                }

                if (count <= 0) {
                    if (this.showWhenEmpty) {
                        this.show();
                        this.disableCartLink();
                    } else {
                        this.hide();
                    }
                } else {
                    if (this.showWhenEmpty) {
                        this.show();
                        this.enableCartLink(count);
                    } else {
                        this.show();
                    }
                }
            },
            disableCartLink: function() {
                var cartstring = document.getElementById("TSCart_header");
                var s = '<img src="' + TSConfig.CDN_URL + 'images/widgetSupport/purchFlow/cart.png" style="border:none;" />' +
                        '<p>Your cart is empty. Please select an item.&nbsp;' +
                        '</p></div>';
                cartstring.innerHTML = s;
            },
            enableCartLink: function(count) {
                var itemString = (count > 1) ? " items" : " item";
                itemString = count + itemString;
                var cartstring = document.getElementById("TSCart_header");
                var s = '<a href="javascript:TSPF.Cart.loadPurchaseFlow();"><img src="' + TSConfig.CDN_URL + 'images/widgetSupport/purchFlow/cart.png" style="border:none;" /></a>' +
                        '<p><a href="javascript:TSPF.Cart.loadPurchaseFlow();">You have <span id="TSCart_num_items">' + itemString + '</span> in your cart&nbsp;' +
                        '|&nbsp;Check Out</a>' +
                        '</p>';
                cartstring.innerHTML = s;
            },
            loadPurchaseFlow: function() {
                TSPF.BuyButton.loadPurchaseFlow({artistId:this.artistId});
            },
            reloadCallbackSWF: function(params) {
                params = params || {artistId:this.artistId, orderId:this.orderId, token:this.token};
                createCallbackSWF(params);
            },
            showWhenEmpty: false
        }
    }();

    if (window.location.href.indexOf("/store/facebook_iframe_purchase/") < 0) {
        TSPF.Util.DOM.addEventHandler(window, "load", TSPF.BuyButton.initialize);
    }
    // Some users may call the old public methods. Mapping for compatability.
    var TSPurchase = TSPF.BuyButton;
    var TSCart = TSPF.Cart;

} // End wrapping TSPurchase initialization check

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/JSON.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
// When the Prototype library is present it is necessary to use its toJSON() method
// instead of the native/json2 stringify() method, otherwise, due to namespace collision
// and differences in implementation of the toJSON() method in Prototype 1.6.x, things
// will get escaped and quoted twice (which causes stringified JSON to be unparseable)
if (typeof Prototype !== "undefined" && typeof JSON === "undefined") {
     JSON = {
         stringify: function (o) { return Object.toJSON(o); },
         parse:     function (s) { return String(s).evalJSON(); }
     };
} else {
    /*
     * http://www.JSON.org/json2.js
     */
    if(!this.JSON){this.JSON={};}(function(){function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\""','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;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);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());
}

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSMessenger.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
/**
 * TSMessenger is a singleton responsible for managing communication between windows/frames of
 * potentially different domains. It uses HTML5 window.postMessage as the preferred messaging
 * method, but also provides fallback support for IE 6 and 7.
 */
var TSMessenger = function () {
    /*
     * Boolean flag indicating whether the messaging cycle utilizes native postMessage
     * functionality, or falls back on utilizing URL hash update polling
     */
    var canHasPostMessage = typeof window.postMessage != "undefined";
    /*
     * Reference to the current message dispatcher handling incoming messages. This reference is
     * cached solely so the dispatcher can be removed when necessary. If native postMessage
     * calls are available for the browser, this will be a function object, otherwise it will be
     * an interval id.
     */
    var dispatcher;
    /* Array of all registered receivers for incoming messages */
    var receivers = [];
    /*
     * Incremented id offset value used to create message receiver ids when no id is specified
     * when instantiating a new receiver
     */
    var receiverIdOffset = 0;

    /*
     * The variables declared below are used when polling via setInterval, and are only required
     * in the absence of native postMessage support by the browser
     */
    var pollingDelay = 200;
    var messageIdPrefix = "tsm_";
    var messageIdRegexp = /^#(tsm_\d+)=/;
    var lastMessageId;


    /**
     * Sends a message to another window/frame
     *
     * @param message  The message data to send. This value should preferably be JSON parseable
     * @param targetFrame The window/frame object of the targeted message recipient, e.g., "parent"
     * @param targetURL  The window/frame URL of targeted message recipient
     */
    function send(message, targetFrame, targetURL) {
        if (!message || !targetFrame || !targetURL) {
            return;
        }

        try {
            message = JSON.stringify(message);
        } catch (parseError) {
            //alert(parseError);
        }

        if (canHasPostMessage) {
            // Parse a distilled target origin from the full target URL to match the requirements
            // of the postMessage API
            var targetOrigin = targetURL.match(/^http.?:\/\/[^\/\s]+/)[0];
            targetFrame.postMessage(message, targetOrigin);
        } else {
            if (targetURL.indexOf("#") != -1) {
                targetURL = targetURL.substring(0, targetURL.indexOf("#"));
            }
            var messageId = messageIdPrefix + new Date().getTime();
            targetFrame.location = targetURL+ "#" + messageId + "=" + encodeURIComponent(message);
        }
    }

    /**
     * Adds a new message receiver
     *
     * @param callback  Function to call when a message is received from the acceptedOrigin
     * @param acceptedOrigin  The acceptable originating URL of a frame sending a message
     * @param id  The unique id of this receiver. This param is optional, and primarily used when
     *            removing a receiver from the dispatch queue
     * @return The id of this receiver. The id is generated if no id param was specified, otherwise
     *         it will be identical to the id provided
     */
    function addReceiver(callback, acceptedOrigin, id) {
        id = id || "receiver_" + receiverIdOffset++;
        receivers.push({id:id, callback:callback, acceptedOrigin:acceptedOrigin});
        startReceiving();
        return id;
    }

    /**
     * Removes a message receiver
     *
     * @param id  The id of the receiver to remove from the dispatch cycle
     * @return Boolean value indicating if the receiver was removed
     */
    function removeReceiver(id) {
        var newReceivers = [];
        for (var i=0, len = receivers.length; i < len; i++) {
            if (receivers[i].id !== id) {
                newReceivers.push(receivers[i]);
            }
        }
        var removed = newReceivers.length < receivers.length;
        if (removed) {
            receivers = newReceivers;
        }
        return removed;
    }

    /**
     * Removes all registered receivers, and stops monitoring incoming messages
     */
    function clearReceivers() {
        stopReceiving();
        receivers = [];
    }

    /**
     * Initiates dispatching of received messages to registered receivers
     * for this frame/window
     */
    function startReceiving() {
        if (dispatcher) {
            return;
        }

        if (canHasPostMessage) {
            dispatcher = dispatchMessage;
            if (typeof window.addEventListener != "undefined") {
                window.addEventListener("message", dispatcher, false); // DOM2
            } else if (typeof window.attachEvent != "undefined") {
                window.attachEvent("onmessage", dispatcher); // IE
            }
        } else {
            // Clear out the current hash to prevent inadvertent dispatching of old messages, e.g.
            // this can happen after refreshing the page
            document.location.hash = "";

            dispatcher = setInterval(function() {
                var hash = document.location.hash;
                if (!hash) {
                    return;
                }
                // Check if there is a message id included in the URL hash, and if so, that it is
                // different than the last message id received.
                // This check, and the potential consequent update of the lastMessageId, must
                // happen atomically prior to invoking dispatchMessage; otherwise IE has a
                // proclivity to call dispatchMessage multiple times before registering that
                // lastMessageId has been updated
                var messageId = hash.match(messageIdRegexp);
                if (messageId && messageId[1] != lastMessageId) {
                    lastMessageId = messageId[1];
                } else {
                    return;
                }

                dispatchMessage({data:hash.substring(hash.indexOf("=") + 1)});

            }, pollingDelay);
        }
    }

    /**
     * Stops message dispatching to all registered receivers for this window/frame
     */
    function stopReceiving() {
        if (!dispatcher) {
            return;
        }

        if (canHasPostMessage) {
            if (typeof window.removeEventListener != "undefined") {
                window.removeEventListener("message", dispatcher, false); // DOM2
            } else if (typeof window.detachEvent != "undefined") {
                window.detachEvent("onmessage", dispatcher); // IE
            }
        } else {
            clearInterval(dispatcher);
        }
        dispatcher = null;
    }

    function dispatchMessage(message) {
        var receiversLength = receivers.length;
        if (receiversLength) {
            for (var i=0; i < receiversLength; i++) {
                var receiver = receivers[i];

                if (canHasPostMessage) {
                    // Ensure that the origin of the sender matches the expected origin of the 
                    // receiver, otherwise skip this receiver
                    if (message.origin.toLowerCase() !== receiver.acceptedOrigin.toLowerCase()) {
                        continue;
                    }
                }

                var jsonData = null;
                try {
                    jsonData = JSON.parse(decodeURIComponent(message.data));
                } catch (parseError) {
                    //alert(parseError);
                }

                receiver.callback(message, jsonData);
            }
        }
    }

    return {
        send           : send,
        addReceiver    : addReceiver,
        removeReceiver : removeReceiver,
        clearReceivers : clearReceivers,
        startReceiving : startReceiving,
        stopReceiving  : stopReceiving
    }
}();

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSJSONP.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
var TSJSONP = (function() {

    var idOffset = 0;
    var head = document.getElementsByTagName('head')[0];

    function sendRequest(request) {
        var id = "TSJSONPResponse_" + idOffset++;
        var url = request.url + (request.url.indexOf("?") == -1 ? "?" : "&");
        if (request.params) {
            request.params.callback = id;
            url += "json=" + encodeURIComponent(JSON.stringify(request.params));
        } else {
            url += "callback=" + id;
        }

        window[id] = function (jsonResponse) {
            head.removeChild(document.getElementById(id));
            window[id] = undefined;

            if (request.onSuccess) {
                request.onSuccess(jsonResponse);
            }
        };

        var transportElement      = document.createElement("script");
            transportElement.type = "text/javascript";
            transportElement.src  = url;
            transportElement.id   = id;

        head.appendChild(transportElement);
    }

    function Request(options) {
        if (!options || !options.url) {
            return;
        }
        this.url       = options.url;
        this.params    = options.params;
        this.onSuccess = options.onSuccess;

        sendRequest(this);
    }

    return {
        Request: Request
    }

})();

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSSSO.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
var TSSSO = (function(){

  function showLogin(returnpath) {
    returnpath = returnpath || encodeURI(window.location.href);

    var url = TSConfig.APP_URL + "auth/embed?parent_url=" + returnpath;
    TSPF.LightBox.render(undefined,url,"CANCEL");
  }

  function checkAccess(config,callback) {

    var extparams = "";
    if(config.products) {
      extparams += extparams == "" ? "?" : "&";
      extparams += "products=" + config.products.join(",");
    }

    if(config.campaigns) {
      extparams += extparams == "" ? "?" : "&";
      extparams += "campaigns=" + config.campaigns.join(",");
    }
  
    var url = TSConfig.APP_URL + "api/v1/access/authorized" + extparams;
    new TSJSONP.Request({
      url: url,
      onSuccess: function(response) {
        callback(response);
      }
    });
  }

  return ({
    showLogin: showLogin,
    checkAccess: checkAccess
  });

})();

/* Source: /var/www/apps/topspin/releases/20101004194722/public/javascripts/TSW.js, last modified at Mon Oct 04 12:48:19 -0700 2010 */
/*
 * Do not execute the contents of this file if TSW has already been initialized;
 * this is done to prevent redundant processing when this file has been included
 * more than once on the same page.
 */
if (!window.TSW) {
    var TSW = {};

    /**
     * Topspin Utilities - Generic dom utilities that can be reused
     */
    TSW.Util = {
        /**
         * The DOM namespace covers basic DOM manipulation functions such as insertions and search functions.
         */
        DOM: {
            /**
             * Adds an event handler function as an event listener to a DOM element
             *
             * @param element  DOM element to which the event handler should be added
             * @param eventType  Event type excluding "on" prefix
             * @param handler  Event handler function
             */
            addEventHandler: function (element, eventType, handler) {
                if (typeof element.addEventListener != "undefined") {
                    element.addEventListener(eventType, handler, false); // DOM2
                } else if (typeof element.attachEvent != "undefined") {
                    element.attachEvent("on"+eventType, handler); // IE
                }
            },
            /**
             * Removes an event handler function from a DOM element
             *
             * @param element  DOM element from which the event handler should be removed
             * @param eventType  Event type excluding "on" prefix
             * @param handler  Event handler function
             */
            removeEventHandler: function (element, eventType, handler) {
                if (typeof element.removeEventListener != "undefined") {
                    element.removeEventListener(eventType, handler, false); // DOM2
                } else if (typeof element.detachEvent != "undefined") {
                    element.detachEvent("on"+eventType, handler); // IE
                }
            }
        }
    };

    /**
     * Initializes any buy buttons found on the page
     *
     * @return The artist ID associated with the last buy button; otherwise null
     */
    function initWidget() {
        // var allE4M = document.getElementsByClassName('topspin-widget-email-for-media');
        var isMobileSafari = navigator.userAgent.match(/iPhone|iPod|iPad/i);
        if (isMobileSafari) {
            var allE4M = document.getElementsByClassName('topspin-widget-email-for-media');
            for (var j=0; j < allE4M.length; j++) {
                var str = allE4M[j].innerHTML;
                //find the dimensions, for iframe
                var w_match =str.match(/width="([0-9]+)"/);
                var h_match =str.match(/height="([0-9]+)"/);
                var w = w_match[1];
                var h = h_match[1];

                //find widget id
                // var e4m = ;
                var e4m_match = str.match(/(email_for_media)\/(\d+)/);
                var wid = (e4m_match && e4m_match.length > 1) ?  e4m_match[2] : null;

                //Get qs
                var params="";
                var paramNodes = allE4M[j].getElementsByTagName("param");
                if (paramNodes && paramNodes.length > 0) {
                    for(var i=0; i<paramNodes.length; i++) {
                        if (paramNodes[i].attributes['name'].value.toUpperCase() == 'FLASHVARS'){
                            var str = paramNodes[i].attributes['value'].value;
                            params = getQS(str);
                            break;
                        }
                    }
                }
                var src = TSConfig.APP_URL + "w/" + wid + "?w=" + w + "&h=" + h + "&m=1";
                src += (params!=="")? "&" + params:"";

                //Create the iframe son.
                var iframeNode = "<iframe src='" + src + "' frameborder='0' border='0' cellspacing='0' style='padding: 0px; border-style: none;width: " + w + "px; height: " + h + "px;'></iframe>";
                allE4M[j].innerHTML = iframeNode;
            }
        }
    }

    function getQS(str) {
        var url = str;
        // Strip the url string trailing and leading whitespace
        url = url.replace(/^\s+/, '').replace(/\s+$/, '');
        // Separate out the query string from the rest of the url
        var match = url.match(/([^?#]*)(#.*)?$/);
        if (!match) {
            return {};
        } else {
            return match[0];
        }
    }

    //After the page has loaded, run initWidget
    TSW.Util.DOM.addEventHandler(window, "load", initWidget);
}

