






		

		
	




/*******************************
TEMPLATE LISL MAP dynamic
********************************/

var PIVOT_NAMES = {
	account: {
	_status: {
_: {
	template: "DownloadStatus",title: "status"}}, _my_account: {
_: {
	template: "OnlineStore",title: "Summary"}}
	}, home: {
	_main: {
_: {
	template: "RootLocation",title: "Home"}, _unknown_location: {
	template: "UnknownLocation",title: "Home"}}, _deep_link: {
_deep_link: {
	template: "RootLocation",isOnlineList: false,listTypeId: 107,title: "Deep Link"}}
	}, albums: {
	_all: {
_: {
	template: "AllCPAlbumIDs",title: "All Albums"}}, _album: {
_: {
	template: "CPAlbumID",title: "Album"}, _nonpurchasable: {
	template: "OnlineStore",title: "Album Non-Purchasable"}}
	}, artists: {
	_all: {
_: {
	template: "AllCPArtistIDs",title: "All Artists"}}, _bio: {
_bio: {
	template: "CPArtistID",title: "Artist Bio"}}, _related: {
_related: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPArtistID",
viewType: "ViewModeTile",title: "Artist Related Artists"}}, _videos: {
_: {
	template: "OnlineStore",title: "Artist Videos"}}, _feed: {
_feed: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Artist Feed"}}, _recs: {
_recs: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Artist Feed"}}, _artist: {
_: {
	template: "CPArtistID",title: "Artist"}, _nonpurchasable: {
	template: "OnlineStore",title: "Artist Non-Purchasable"}, _albums: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPAlbumID",
viewType: "ViewModeTile",title: "Artist Albums"}, _tracks: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Artist Tracks"}}, _title: {
_playlists: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPListID",
viewType: "ViewModeIcon",title: "Artist Playlists"}, _stations: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "Artist Stations"}, _exclusives: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Artist Exclusives"}, _top_songs: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Artist Top Songs"}, _featured_in: {
	template: "CPArtistID",isOnlineList: true,itemType: "CPListID",
viewType: "ViewModeIcon",title: "Artists Featured In Content"}}
	}, charts: {
	_all: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 4,title: "Charts All"}}, _title: {
_all: {
	template: "CPListID",isOnlineList: false,listTypeId: 1003,title: "All Charts"}, _billboard: {
	template: "CPListID",isOnlineList: false,listTypeId: 1004,title: "Billboard Charts"}, _urge: {
	template: "CPListID",isOnlineList: false,listTypeId: 1005,title: "URGE Charts"}}, _billboard: {
_yearly_album: {
	template: "CPListID",isOnlineList: false,listTypeId: 5,title: "Billboard Yearly Album"}, _yearly_track: {
	template: "CPListID",isOnlineList: false,listTypeId: 6,title: "Billboard Yearly Track"}, _weekly_album: {
	template: "CPListID",isOnlineList: false,listTypeId: 7,title: "Billboard Weekly Album"}, _weekly_track: {
	template: "CPListID",isOnlineList: false,listTypeId: 8,title: "Billboard Weekly Track"}}, _urge: {
_urge_artist: {
	template: "CPListID",isOnlineList: false,listTypeId: 19,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "URGE Artist Chart"}, _urge_album: {
	template: "CPListID",isOnlineList: false,listTypeId: 20,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "URGE Album Chart"}, _urge_track: {
	template: "CPListID",isOnlineList: false,listTypeId: 21,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "URGE Track Chart"}, _genre_artist: {
	template: "CPListID",isOnlineList: false,listTypeId: 16,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Genre Artist Chart"}, _genre_album: {
	template: "CPListID",isOnlineList: false,listTypeId: 17,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Genre Album Chart"}, _genre_track: {
	template: "CPListID",isOnlineList: false,listTypeId: 18,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Genre Track Chart"}}
	}, newreleases: {
	_newreleases: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 105,title: "All New Releases"}}, _title: {
_sitewide_new_releases: {
	template: "CPListID",isOnlineList: false,listTypeId: 28,itemType: "CPAlbumID",
viewType: "ViewModeTile",title: "New Releases"}, _sitewide_newly_added: {
	template: "CPListID",isOnlineList: false,listTypeId: 29,itemType: "CPAlbumID",
viewType: "ViewModeTile",title: "Newly Added"}}
	}, franchises: {
	_redirect: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 106,title: "Franchise Redirect"}}, _franchise: {
_artist_template: {
	template: "CPListID",isOnlineList: false,listTypeId: 9,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Artist Franchise"}, _album_template: {
	template: "CPListID",isOnlineList: false,listTypeId: 10,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Album Franchise"}, _50_50_song_template: {
	template: "CPListID",isOnlineList: false,listTypeId: 11,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "50 50 Song Franchise"}, _90_10_song_template: {
	template: "CPListID",isOnlineList: false,listTypeId: 24,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "90 10 Song Franchise"}, _full_html_template: {
	template: "OnlineStore",title: "Full HTML Franchise"}}, _archive: {
_artist_template_scheduled_content: {
	template: "CPListID",isOnlineList: false,listTypeId: 12,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Archived Artist Franchise"}, _album_template_scheduled_content: {
	template: "CPListID",isOnlineList: false,listTypeId: 13,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Archived Album Franchise"}, _50_50_song_template_scheduled_content: {
	template: "CPListID",isOnlineList: false,listTypeId: 14,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Archived 50 50 Song Franchise"}, _90_10_song_template_scheduled_content: {
	template: "CPListID",isOnlineList: false,listTypeId: 25,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Archived 90 10 Song Franchise"}, _full_html_template_scheduled_content: {
	template: "OnlineStore",title: "Archived Full HTML Franchise"}}, _title: {
_artist: {
	template: "CPListID",isOnlineList: false,listTypeId: 33,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Franchise Featured Artists"}, _album: {
	template: "CPListID",isOnlineList: false,listTypeId: 34,itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Franchise Featured Albums"}, _track: {
	template: "CPListID",isOnlineList: false,listTypeId: 35,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Franchise Featured Tracks"}}
	}, featuredstories: {
	_all: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 16435,title: "Feature Stories"}}
	}, networkplaylists: {
	_networkplaylist: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 2,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Network Playlist"}}, _archive: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 15,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Archived Network Playlist"}}
	}, blogs: {
	_all: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 11359,title: "All Blogs"}}, _blog: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 23,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Blog"}, _archive: {
	template: "CPListID",isOnlineList: false,listTypeId: 22,itemType: "CPTrackID",
viewType: "ViewModeDetails",title: "Blog Archive"}}
	}, genres: {
	_all: {
_: {
	template: "AllCPGenreIDs",title: "All Genres"}}, _genre: {
_: {
	template: "CPGenreID",title: "Genre"}}, _title: {
_artists: {
	template: "CPGenreID",itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Top Artists"}, _albums_top: {
	template: "CPGenreID",itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Top Albums"}, _songs_top: {
	template: "CPGenreID",itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Top Songs"}, _styles: {
	template: "CPGenreID",itemType: "CPAlbumSubGenreID",
viewType: "ViewModeIcon",title: "Genre Styles"}, _years: {
	template: "CPGenreID",title: "Years"}, _ratings_top: {
	template: "CPGenreID",title: "Top Rated"}, _new_releases: {
	template: "CPListID",isOnlineList: false,listTypeId: 37,itemType: "CPAlbumID",
viewType: "ViewModeTile",title: "New Releases"}, _stations: {
	template: "CPGenreID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "All Access Radio"}, _playlists: {
	template: "CPListID",isOnlineList: false,listTypeId: 36,itemType: "CPListID",
viewType: "ViewModeIcon",title: "Playlists"}}
	}, styles: {
	_all: {
_: {
	template: "AllCPAlbumSubGenreIDs",title: "All Styles"}}, _style: {
_: {
	template: "CPAlbumSubGenreID",title: "Style"}}, _feed: {
_feed: {
	template: "CPAlbumSubGenreID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Style Feed"}}, _recs: {
_recs: {
	template: "CPAlbumSubGenreID",isOnlineList: true,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Style Feed"}}, _title: {
_artists: {
	template: "CPAlbumSubGenreID",itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Top Artists"}, _albums_top: {
	template: "CPAlbumSubGenreID",itemType: "CPAlbumID",
viewType: "ViewModeIcon",title: "Top Albums"}, _songs_top: {
	template: "CPAlbumSubGenreID",itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "Top Songs"}, _years: {
	template: "CPAlbumSubGenreID",title: "Years"}, _ratings_top: {
	template: "CPAlbumSubGenreID",title: "Top Rated"}, _essential_albums: {
	template: "CPListID",isOnlineList: false,listTypeId: 30,itemType: "CPAlbumID",
viewType: "ViewModeTile",title: "Essential Albums"}, _stations: {
	template: "CPAlbumSubGenreID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "All Access Radio"}, _playlists: {
	template: "CPListID",isOnlineList: false,listTypeId: 36,itemType: "CPListID",
viewType: "ViewModeIcon",title: "Playlists"}}
	}, channels: {
	_channel: {
_mtv: {
	template: "RootLocation",isOnlineList: false,listTypeId: 100,title: "MTV"}, _vh1: {
	template: "RootLocation",isOnlineList: false,listTypeId: 101,title: "VH1"}, _cmt: {
	template: "RootLocation",isOnlineList: false,listTypeId: 102,title: "CMT"}}, _dev: {
_dev: {
	template: "RootLocation",isOnlineList: false,listTypeId: 108,title: "DEV"}}
	}, pages: {
	_feeds: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 200,title: "What is Feeds"}}, _gear: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 109,title: "Gear"}}, _splash: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 110,title: "Welcome to URGE"}}
	}, stories: {
	_all: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 2,title: "Stories All"}}, _title: {
_all_stories: {
	template: "CPListID",isOnlineList: false,listTypeId: 1007,title: "All Stories"}, _features: {
	template: "CPListID",isOnlineList: false,listTypeId: 16471,title: "Features"}, _informers: {
	template: "CPListID",isOnlineList: false,listTypeId: 16472,title: "Informers"}, _artist_profiles: {
	template: "CPListID",isOnlineList: false,listTypeId: 16466,title: "Artist Profiles"}, _interviews: {
	template: "CPListID",isOnlineList: false,listTypeId: 16467,title: "Interviews"}, _series: {
	template: "CPListID",isOnlineList: false,listTypeId: 16468,title: "Series"}}, _story: {
_story_50_50: {
	template: "CPListID",isOnlineList: false,listTypeId: 43,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Story 50 50"}, _story_90_10: {
	template: "CPListID",isOnlineList: false,listTypeId: 44,itemType: "CPArtistID",
viewType: "ViewModeIcon",title: "Story 90 10"}}, _story_title: {
_artist: {
	template: "CPListID",isOnlineList: false,listTypeId: 46,title: "Story Featured Artists"}, _album: {
	template: "CPListID",isOnlineList: false,listTypeId: 47,title: "Story Featured Albums"}, _playlist: {
	template: "CPListID",isOnlineList: false,listTypeId: 48,title: "Story Featured Playlists"}}
	}, playlists: {
	_all: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 3,title: "Playlists All"}}, _playlist: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 1,title: "Playlist"}}, _dueling: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 42,title: "Dueling Playlist"}}, _title: {
_all_playlists: {
	template: "CPListID",isOnlineList: false,listTypeId: 1001,title: "All Playlists"}, _all_feeds: {
	template: "CPListID",isOnlineList: false,listTypeId: 1006,title: "All Feeds"}, _celebrity_playlists: {
	template: "CPListID",isOnlineList: false,listTypeId: 11322,title: "Celebrity Playlists"}, _must_haves: {
	template: "CPListID",isOnlineList: false,listTypeId: 11323,title: "Must Haves"}, _on_tv: {
	template: "CPListID",isOnlineList: false,listTypeId: 11324,title: "On TV"}, _super_playlists: {
	template: "CPListID",isOnlineList: false,listTypeId: 11325,title: "Super Playlists"}, _moods: {
	template: "CPListID",isOnlineList: false,listTypeId: 11326,title: "Moods"}, _top_playlists: {
	template: "CPListID",isOnlineList: true,itemType: "CPListID",
viewType: "ViewModeIcon",title: "Top Playlists"}}
	}, radio: {
	_station: {
_: {
	template: "CPRadioID",title: "Station"}}, _all: {
_: {
	template: "AllCPRadioIDs",itemType: "CPRadioID",
viewType: "ViewModeIcon",title: "All Radio"}, _genre: {
	template: "CPRadioID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "Genre Stations"}}, _title: {
_top_stations: {
	template: "CPRadioID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "Top Stations"}, _free_radio: {
	template: "CPRadioID",isOnlineList: true,itemType: "CPRadioID",
viewType: "ViewModeTile",title: "Free Radio"}}
	}, tracks: {
	_all: {
_: {
	template: "AllCPTrackIDs",title: "All Tracks"}}
	}, years: {
	_all: {
_: {
	template: "AllReleaseDateYears",title: "All Years"}}, _year: {
_: {
	template: "ReleaseDateYear",title: "Year"}}
	}, search: {
	_advanced: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 103,title: "Advanced Search"}}
	}, downloads: {
	_manager: {
_downloads: {
	template: "CPListID",isOnlineList: false,listTypeId: 5,title: "Download Manager"}}
	}, ratings: {
	_all: {
_: {
	template: "AllUserEffectiveRatingStarss",title: "All Ratings"}}, _rating: {
_: {
	template: "UserEffectiveRatingStars",title: "Rating"}}
	}, recs: {
	_feed: {
_: {
	template: "CPListID",isOnlineList: false,listTypeId: 1,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "My Auto-Mix"}, _feed: {
	template: "CPListID",isOnlineList: true,listTypeId: 1,itemType: "CPTrackID",
viewType: "ViewModeOrderedList",title: "My Auto-Mix"}}
	}, bios: {
	_bio: {
_: {
	template: "RootLocation",isOnlineList: false,listTypeId: 2,title: "Bio"}}, _title: {
_features: {
	template: "CPListID",isOnlineList: false,listTypeId: 45,itemType: "CPListID",
viewType: "",title: "Features"}, _stations: {
	template: "CPListID",isOnlineList: false,listTypeId: 50,itemType: "CPRadioID",
viewType: "",title: "Stations"}}
	}
};


/************************************************************************
*

CookieManager
	- Singleton object used to get, delete, and create proper Cookie objects.

CookieManager.create (name)
	- Create an empty cookie with the given name. Name should be alpha-numeric
	- Returns the created cookie

CookieManager.remove (name)
	- Deletes cookie by given name, returning nothing
	
CookieManager.isRemoved (name)
	- Checks to see whether expiration date of named cookie has been set to the key value
	  used to show removal (01-Jan-1970)

CookieManager.exists (name)
	- Returns boolean indicating existance of cookie by given name

CookieManager.get (name, createIfNull)
	- Returns Cookie object with given name
	- If optional 'createIfNull' argument is true, non-existent cookie will be created
	- Returns null if cookie doesn't exist (and createIfNull is false/ not supplied)

CookieManager.getRawCookie (name)
	- Returns raw string value of cookie with given name, or "" if none exists

CookieManager.setCookie (name, value, expiration)
	- Sets raw cookie value
	- Param 'expiration' is optional, and can be either an integer representing seconds until the cookie expires, or the string "session" to make it a session cookie.

_________________________________________________________
Cookie
	- Instance of a single cookie, which comprises a name and a set of subvalues

Cookie.exists (key)
	- Returns boolean indicating existance of named key

Cookie.get (key)
	- Returns value for named key, or null if undefined

Cookie.set (key, value, expiration)
	- Sets value for named key. Key should be alpha-numeric.
	- Param 'expiration' is optional, and can be either an integer representing seconds until the cookie expires, or the string "session" to make it a session cookie.
	- Returns Cookie reference


________
EXAMPLES

// Get & set Paul's favorite color:

if(CookieManager.exists("Pauls_Cookie")) {
	var cookie = CookieManager.get("Pauls_Cookie");

	if(cookie.exists("favorite_color")) {
		alert("Paul's favorite color is "+cookie.get("favorite_color"));

	} else {
		alert("Paul's favorte color can't be determined.");

		cookie.set("favorite_color", "plaid");
	}
}


*************************************************************************/

var COOKIE_STAGE = window.location.host.substring(window.location.host.indexOf("urge")-1);

var _BROWSER_IS_IE =
    (document.all
     && window.ActiveXObject
     && navigator.userAgent.toLowerCase().indexOf("msie") > -1
     && navigator.userAgent.toLowerCase().indexOf("opera") == -1);

var _BROWSER_IS_OPERA =
    (navigator.userAgent.toLowerCase().indexOf("opera") != -1);

var CookieManager = {
	COOKIE_SHELF_LIFE:	365,
	DELIM1:				"|",
	DELIM2:				":",
	domain:				COOKIE_STAGE,

	create:		function(name, _expirationSeconds) {
		this.setCookie(name, "", _expirationSeconds);
		
		return this.get(name);
	},

    exists: function(name) {
        var found = false;

		for (var i = 0; i < document.cookie.split('; ').length; i++) {
			var rawCookie = document.cookie.split('; ')[i];
			var splitValues = rawCookie.split('=');
			if (splitValues[0] == name)
				found = true;
		}

		return found;
	},

    get: function(name, _createIfNull) {
    	var createIfNull = (typeof _createIfNull == "undefined")?false:_createIfNull;
		

    	if(!this.exists(name)) {
    		if(createIfNull) {
    			
    			this.create(name);

    		} else return null;
    	}

		var startValue = this.getRawCookie(name);
		if(startValue==null) startValue = "";
        return new Cookie(name, startValue);
    },

    getRawCookie: function(name) {
        var result = null;

		var potentialVals = new Array();
        
		for (var i = 0; i < document.cookie.split('; ').length; i++) {
			var rawCookie = document.cookie.split('; ')[i];
			var splitValues = rawCookie.split('=');
			if (splitValues[0] == name && splitValues[1] != null)
			{
				result = rawCookie.substring(rawCookie.indexOf("=")+1);
				
				if(name=="DMS_Conversion") {
					potentialVals.push(result);					
				}
			}
		}
		
		// Temporary sanity check on DMS_Conversion cookie to ensure the value used is the one set by the plugin and not the temporary session referrer
		if(name=="DMS_Conversion") {
			var sessionReferrer = this.getRawCookie("Temp_Session_Conversion");
			
			if(potentialVals.length>1 && sessionReferrer!=null) {
				if(potentialVals[0]==sessionReferrer) {
					try { dbg("Returning second value because first value is session referrer"); } catch (e) {}
					result = potentialVals[1];
				} else {
					try { dbg("returning first value because '"+potentialVals[0]+"'!='"+sessionReferrer+"'"); } catch (e) {}
					result = potentialVals[0];
				}
			}
		}

        if (_BROWSER_IS_OPERA && result != null) {
            result = result.replace(/%22/g, '"');
        }
        return result;
    },

    remove: function(aCookieName) {
		document.cookie =
			aCookieName + '=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';
		document.cookie =
			aCookieName + '=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/; domain=' + this.domain;
    },
	
	isRemoved: function(aCookieName) {
		return (this.getRawCookie(aCookieName).indexOf("01-Jan-1970") >= 0);
	},

    setCookie: function(name, aCookieValue, _expirationSeconds) {
    
    	var expirationSeconds = (typeof _expirationSeconds=="undefined")?
    		(this.COOKIE_SHELF_LIFE * 24*60*60):
    		((_expirationSeconds=="session")?false:_expirationSeconds);
  		    
		if (_BROWSER_IS_OPERA) {
			aCookieValue = aCookieValue.replace(/"/g, "%22");		// "
		}
		
		var expires = "";
		if(expirationSeconds) {
			try { dbg("cookie expirationSeconds is "+expirationSeconds); } catch (e) {}
			var date = new Date();
			date.setTime(date.getTime() + (expirationSeconds*1000));
			expires = '; expires=' + date.toGMTString();
		}
			
		try { dbg("setting cookie: "+name + '=' + aCookieValue + expires + '; path=/; domain=' + this.domain); } catch (e) {}
		document.cookie = name + '=' + aCookieValue + expires + '; path=/; domain=' + this.domain;
  		
    }
}

function Cookie(name, rawValue) {
	this.name = name;
	this.rawValue = rawValue;
}

Cookie.prototype.get = function(key) {
	var map = getNameValueMap(this.rawValue, CookieManager.DELIM1, CookieManager.DELIM2);
	if(typeof map[key] == "undefined") return null;
	return unescape(map[key]);
}

Cookie.prototype.exists = function (key) {
	var map = getNameValueMap(this.rawValue, CookieManager.DELIM1, CookieManager.DELIM2);
	return (typeof map[key] != "undefined");
}

Cookie.prototype.set = function(key, val, _expirationSeconds) {
	
	var map = getNameValueMap(this.rawValue, CookieManager.DELIM1, CookieManager.DELIM2);
	map[key] = val;

	var mapPairs = new Array();
	for(var k in map) {
		if(k)
			mapPairs.push(k + CookieManager.DELIM2 + map[k]);
	}

	this.rawValue = mapPairs.join(CookieManager.DELIM1);
	CookieManager.setCookie(this.name, this.rawValue, _expirationSeconds);

	
	return this;
}


/* this base64 script courtesy of Tyler Akins, http://rumkin.com/tools/compression/base64.php */

var keyStr = "ABCDEFGHIJKLMNOP" +
             "QRSTUVWXYZabcdef" +
             "ghijklmnopqrstuv" +
             "wxyz0123456789+/" +
             "=";

String.prototype.encode64 = function () {
	var output = "";
	var chr1, chr2, chr3 = "";
	var enc1, enc2, enc3, enc4 = "";
	var i = 0;

	do {
		chr1 = this.charCodeAt(i++);
		chr2 = this.charCodeAt(i++);
		chr3 = this.charCodeAt(i++);
		
		enc1 = chr1 >> 2;
		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		enc4 = chr3 & 63;
		
		if (isNaN(chr2)) {
			enc3 = enc4 = 64;
		} else if (isNaN(chr3)) {
			enc4 = 64;
		}
		
		output = output + 
		keyStr.charAt(enc1) + 
		keyStr.charAt(enc2) + 
		keyStr.charAt(enc3) + 
		keyStr.charAt(enc4);
		chr1 = chr2 = chr3 = "";
		enc1 = enc2 = enc3 = enc4 = "";
	} while (i < this.length);
	return output;
};

String.prototype.decode64 = function () {
	var output = "";
	var chr1, chr2, chr3 = "";
	var enc1, enc2, enc3, enc4 = "";
	var i = 0;
	var input = this.replace(/[^A-Za-z0-9\+\/\=]/g, "");
	
	do {
		enc1 = keyStr.indexOf(input.charAt(i++));
		enc2 = keyStr.indexOf(input.charAt(i++));
		enc3 = keyStr.indexOf(input.charAt(i++));
		enc4 = keyStr.indexOf(input.charAt(i++));
		
		chr1 = (enc1 << 2) | (enc2 >> 4);
		chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
		chr3 = ((enc3 & 3) << 6) | enc4;
		output = output + String.fromCharCode(chr1);

	if (enc3 != 64) {
		output = output + String.fromCharCode(chr2);
	}
	if (enc4 != 64) {
		output = output + String.fromCharCode(chr3);
	}
	
	chr1 = chr2 = chr3 = "";
	enc1 = enc2 = enc3 = enc4 = "";
	
	} while (i < input.length);
	
	return output;
	
}
var xml_gen_base = "/content/components/xml_gen/switch.jhtml?";

var XML_BODY_REQUEST_TIMEOUT = 60000;

/****************************************
	MAIN INTERFACE
*****************************************/

var xml_requests = {
	requests:	{},
	add:		function(request_type, xml_url, xsl_url, custom_transform_handler) {
		
		// If we're redefining the body, throw up the loading message and clear the current body
		if(request_type=="BODY" && this.get("BODY")) {
			if (loading_div = document.getElementById("loadingDiv")) loading_div.style.display = "block";
			PAGE_BODY.write("");
		}

		var request = new XMLRequestWrapper(xml_url, xsl_url);
		request.request_type = request_type;
		
		// If a custom transform handler was provided, register it.
		if(typeof custom_transform_handler != "undefined") {
			dbg("applying custom handler: "+custom_transform_handler);
			request.setCustomHandler(custom_transform_handler);
		}

		this.requests[request_type] = request;
			
		if(window.loaded) {
			request.instantiate();
		}
		return request;
	},
	get:		function(request_type) {
		return this.requests[request_type];
	},
	sendAll:	function() {
		for(var request_type in this.requests) {
			var request = this.requests[request_type];
			
			if (request) {
				request.instantiate();
				
			} else {
				dbg("WARNING: request type '" + request_type + "' is unrecognized.");
			}
		}		
	}
};


/****************************************
	OBJECTS
*****************************************/

// a wrapper for each request the page has to make to build itself
function XMLRequestWrapper(xml_url, xsl_url) {
	
	this.xml_url = (xml_url.charAt(0) == "/") ? xml_url : xml_gen_base + xml_url;
	this.xsl_url = xsl_url;
	this.xsl_parameters = { };
	this.custom_response_handler = false;
	this.loaded = false;

	this.request_type = "";

	// create a js closure to preserve the scope.
	// i think this is because the ActiveXObject isn't a true DOM object, and it can't accept properties and methods via JS
	// ... although, we can attach the onreadystatechange() method to it... weird
	var preserved_scope = this;

	this.xml_request = createActiveXObject("XMLHTTP");
	
	this.xml_request.onreadystatechange = function() {
		//dbg("request for "+this.xml_url+" returned with readyState "+preserved_scope.xml_request.readyState);
		switch(preserved_scope.xml_request.readyState) {
			case 0:
			case 1:
			case 2:
			case 3:
				//dbg("readyState: "+preserved_scope.xml_request.readyState);
				break;

			case 4:
				if (preserved_scope.xml_request.responseXML.parseError.errorCode == 0) {
					
					preserved_scope.loaded = true;
					 
					preserved_scope.xml_request.responseXML.setProperty("SelectionLanguage", "XPath");
					preserved_scope.transform();

				} else {
					if (preserved_scope.xml_request.responseXML.xml.length == 0) {
						dbg("ERROR: length of the XML is zero for the  " + preserved_scope.request_type + "\n" + "XML request HTTP status code = " + preserved_scope.xml_request.status + "\n" + preserved_scope.xml_url);
					} else {
						dbg("ERROR: an unknown parsing error for " + preserved_scope.request_type + " has occurred" + "\n" + preserved_scope.xml_url);
					}
					preserved_scope.xml_request.abort();
				}
				break;

			default:
				debug("WARNING: readyState undefined for " + preserved_scope.request_type + " request type.");
				break;							
		};
	}; // END: onreadystatechange

} // END: XMLRequestWrapper constructor


XMLRequestWrapper.prototype.instantiate = function() {
	//dbg("XMLRequestWrapper.instantiate");
	// what type of request are we handling?
	
	switch(this.request_type) {
		case "TAXONOMY": this.transform = setTaxonomyAndTitle; break;
		case "BODY": this.transform = transformBodyXML; break;
		case "APPEARS_ON": this.transform = constructCompleteDiscographyXML; break;
		case "DISCOGRAPHY": this.transform = transformDiscographyXML; this.xml_is_combined = false; break;
		case "FAQ": this.transform = transformFAQXML; break;
		case "CUSTOM": this.transform = function() {this.custom_response_handler(this.xml_request.responseXML);}; break;
		default: this.transform = function() { return; }; break;
	};
	
	if(this.request_type=="BODY") {
		var _this = this;
		setTimeout(function() {
			if(!_this.loaded) {
				location.replace("/sitewide/errors/error.jhtml?error=xmlRequestTimeout");
			}
		}, XML_BODY_REQUEST_TIMEOUT);
	}

	debugXmlUrl(this.xml_url);
	this.xml_request.open("GET", this.xml_url, true);
	
	this.xml_request.send();
	//dbg("xml request sent for "+this.xml_url);

/*
	if (this.xml_request.responseXML.parseError.errorCode != 0) {
		this.error_message = 'there was an error with the ' + this.request_type + ' request:' + '\n&nbsp;&nbsp;- ' + this.xml_request.responseXML.parseError.reason;
		return false;

	} else {
		return true;
	}
*/
}; // END: instantiate

XMLRequestWrapper.prototype.setXSLParameter = function(key, val) {
	this.xsl_parameters[key] = val;
};


XMLRequestWrapper.prototype.setCustomHandler = function(func) {
	this.custom_response_handler = func;
};


// Browser Detection
var browser_version = 0;
if (navigator.appVersion.indexOf("MSIE")!=-1){
	temp=navigator.appVersion.split("MSIE");
	browser_version=parseFloat(temp[1]);
}

// Declare which MSXML versions to attempt
var msxml_versions = new Array();
if (browser_version==7){
	msxml_versions = Array(6,4,3);
} else if (browser_version==6) {
	msxml_versions = Array(6,5,4,3);
} else {
	msxml_versions = Array(4,3);
}

function createActiveXObject(name) {
	var obj;
	// for(var version=6;version>0;version--) {
	for(var i=0;i<msxml_versions.length;i++) {
		var version = msxml_versions[i];
		var objectString = "Msxml2." + name + "." + version + ".0";

		try {
			obj = new ActiveXObject(objectString);
		} catch(e) {}	
			
		if(typeof obj != "undefined") {
			dbg("created "+name+" version "+version);
			return obj;
		}
	}
	dbg("created "+name+" - generic version");
	return new ActiveXObject("Msxml2." + name);
}

function XSLTWrapper(xsl_url, xml_data) {
	this.xsl_url = site_base_url + xsl_url;
	this.xml_data = xml_data;
	this.params = { };
	var dom_doc = createActiveXObject("FreeThreadedDOMDocument");
	try { dom_doc.resolveExternals = true; } catch(e) { }
	try { dom_doc.setProperty("AllowDocumentFunction", true) } catch(e) { }
	//dom_doc.setProperty("AllowXsltScript", true);
	var xslt_template = createActiveXObject("XSLTemplate");
	
	dom_doc.async = false;
	dom_doc.load(this.xsl_url);
	xslt_template.stylesheet = dom_doc;
	this.xsl_processor = xslt_template.createProcessor();

	try {
		this.xsl_processor.input = this.xml_data;
	} catch (e) {
		alert(e.name + "\n" + e.message);
		return;
	}

	this.remove_xml_prolog = false;
	this.target_dom_node_id = null;
	this.target_dom_node = null;

	this.render_to_page_body = false;

	// a wrapper around the transform method so we can add parameters that are assigned at the page level
	this.transform = function() {

		for (var param in this.params) {
			this.xsl_processor.addParameter(param, this.params[param]);
		}

		// this.target_dom_node = ( (typeof(this.target_dom_node_id) == "string") && (this.target_dom_node_id != "") ) ? document.getElementById(this.target_dom_node_id) : null;
		this.xsl_processor.transform();
		
		// remove the xml prolog from the transformed XML if we're outputting HTML
		this.xslt_output = (this.remove_xml_prolog) ? this.xsl_processor.output.replace(/<\?xml.+\?>/, '') : this.xsl_processor.output;

		if (this.render_to_page_body)
			PAGE_BODY.write("<div id='pattern'></div>\n"+this.xslt_output);
		
		/*
		// Paul's proposed change to support multiple target types (iframe vs. div)
		if (this.target_dom_node && this.target_dom_node.tagName == "IFRAME") {
			writeBodyFrame(this.xsl_processor.output);

		} else if (this.target_dom_node != null) {
			this.target_dom_node.innerHTML = this.xsl_processor.output;
			this.target_dom_node.style.display = "inline"; // suspicious... probably to work around some CSS bugs in IE
		}
		*/
		
		return;
	};
} // END: XSLTWrapper


function XMLWrapper(xml_as_string) {
	this.xml_obj = createActiveXObject("DOMDocument");
	
	/*
	try {
		this.xml_obj = new ActiveXObject("Msxml2.DOMDocument.3.0");
	} catch(e) {
		this.xml_obj = new ActiveXObject("Msxml2.DOMDocument");
	}
	*/
	this.xml_obj.async = false;
	this.xml_obj.loadXML(xml_as_string);

	if (this.xml_obj.parseError.errorCode != 0) {
		dbg("ERROR parsing XML; " + this.xml_obj.parseError);
		return false;
	}

	this.xml_obj.setProperty("SelectionLanguage", "XPath");
} // END: XMLWrapper




/****************************************
	COMMON RESPONSE HANDLERS
*****************************************/

// this function will be eventually assigned to a XMLRequestWrapper object as a method
function setTaxonomyAndTitle() {
	try {

		// set page title if it wasn't set by page itself
	// 	if (!page_title) {
			if(node = this.xml_request.responseXML.selectSingleNode("//pageName"))
				page_title = node.text;
	// 	}

		// Set page title (flash)
		// If page content is in an IFRAME, set up a listener for when that frame loads

		PAGE_BODY.setFlashVariable("titleboxFlash", "page_title", page_title);
		
		if (page_title_obj != null) {
			if (page_title) {
				page_title_obj.setParam("page_title", page_title);
			}

			var splashPageTitle;
			if (splashPageTitle = document.getElementById("splashPageTitle")) {
				splashPageTitle.innerHTML = page_title_obj.getEmbedString();
			}
		}
		
		
		// Set splash pagetitle
		if(page_title && (splashPageTitle = document.getElementById("splashPageTitle"))) {
			var section_title = (page_type.indexOf("playlist")>=0)?"PLAYLISTS":"";
			splashPageTitle.innerHTML = getPageTitleEmbedString(page_type, page_title, section_title);
		}
		

		// set taxonomy
		if(navNode = this.xml_request.responseXML.selectSingleNode("//nav")) {
			setFlashVariable("taxnav", "navdata", cleanDataForFlash(navNode.xml));
		}

		// kick off reporting call
		if (REPORTING_ENABLED) {
	//		makeReportingCall(this.xml_request.responseXML);
			REPORTING.makeCall(page_type);
		}

	} catch(e) {
		dbg("Taxonomy XML is invalid ("+e+"): "+this.xml_request.responseXML.xml);
	}
}; // END: setTaxonomyAndTitle


// this function will be eventually assigned to a XMLRequestWrapper object as a method
function transformBodyXML() {
	
	this.body_xslt = new XSLTWrapper(this.xsl_url, this.xml_request.responseXML);
	
	//dbg("transforming body request");
	for (var i in this.xsl_parameters) {
		this.body_xslt.params[i] = this.xsl_parameters[i];
	}

	// get Sub Rights
	var accountRights = getAccountRightsValue();
	
	// Apply rights 
	for (var key in RIGHTS_PARAMETERS) {
		var bit = RIGHTS_PARAMETERS[key];
		this.body_xslt.params[key] = ((accountRights & bit) == bit);
		// dbg("setting rights["+key+"] to "+((accountRights & bit) == bit));
	}
	this.body_xslt.params["_section"] = query.hasValue("section")?query.get("section"):"";
	this.body_xslt.params["_page"] = query.hasValue("page")?query.get("page"):"";
	this.body_xslt.params["_id"] = query.hasValue("id")?query.get("id"):"";
	this.body_xslt.params["_mode"] = query.hasValue("mode")?query.get("mode"):"";
	this.body_xslt.params["_version"] = query.hasValue("version")?query.get("version"):"";
	
	
	// Apply Flash Detection
	this.body_xslt.params["got_flash"] = flashDetect.flashSupported();
	
	// Apply templateBeingDisplayedInLocalLibrary
	try {
		this.body_xslt.params["in_library"] = (window.external.templateBeingDisplayedInLocalLibrary)?"true":"false";	
		dbg("checked templateBeingDisplayedInLocalLibrary successfully, evaluating to "+window.external.templateBeingDisplayedInLocalLibrary);
		
	} catch(e) {
		this.body_xslt.params["in_library"] = "false";	
		dbg("checking templateBeingDisplayedInLocalLibrary threw an error: "+e+": "+e.message)
	}
	
	this.body_xslt.render_to_page_body = true;
	// this.body_xslt.target_dom_node_id = ( (typeof(this.target_dom_node_id) == "string") && (this.target_dom_node_id.length > 0) ) ? this.target_dom_node_id : "main";
	// this.body_xslt.target_dom_node_type = ( (typeof(this.target_dom_node_type) == "string") && (this.target_dom_node_type.length > 0) ) ? this.target_dom_node_type : "div";
	this.body_xslt.remove_xml_prolog = true;
	this.body_xslt.transform();
	
	dbg("firing finishBodyRendering()");
	finishBodyRendering();
}; // END: transformXML

// this function will be eventually assigned to a XMLRequestWrapper object as a method
function transformFAQXML() {

	// alert("transforming faq xml 2");
	
	this.body_xslt = new XSLTWrapper(this.xsl_url, this.xml_request.responseXML);

	this.body_xslt.xsl_processor.transform();

	var output = this.body_xslt.xsl_processor.output.replace(/<\?xml.+\?>/, '');
	var categoryName = this.xml_request.responseXML.selectSingleNode("//categoryName").firstChild.text;

	// Output is coming back as escaped HTML (to protect the XML from breaking if the copy contains invalid XHTML)
	// .. so unescape the obvious:
	output = output.replace(/&lt;/g, "<");
	output = output.replace(/&gt;/g, ">");
	output = output.replace(/&amp;/g, "&");
		
	document.getElementById("header").innerHTML = categoryName.toUpperCase();
	document.getElementById("contentHolder").innerHTML = output;
}; // END: transformXML


// PageBody object
var PAGE_BODY = {
	node_id: 	"main",
	load_complete: 	false,
	type:		"DIV",	// (IFRAME|DIV|STATIC)
	load_listeners:	new Array()
	
} // END: PageBody

/****************************************
	MAIN INTERFACE
*****************************************/

PAGE_BODY.addLoadListener = function(func) {
	if (this.type == "IFRAME") {
		
		// If loaded, fire immediately
		if(this.isLoaded()) {
			if(typeof(this.load_listeners[i])=="function") {
				func();
			} else {
				window.main.eval(func);
			}	
			
		// Otherwise queue for load
		} else {
		
			this.load_listeners[this.load_listeners.length] = func;
		}
	} else {
	
		addToAfterBodyRenderingComplete(func);
	}
}; // END: addLoadListener

PAGE_BODY.setFlashVariable = function(movie_name, var_name, var_value) {	
	if (this.type == "IFRAME") {
		this.addLoadListener("setFlashVariable('"+movie_name+"','"+var_name+"','"+(var_value+"").replace(/'/g,"\\'")+"')");
	} else {	
		setFlashVariable(movie_name, var_name, var_value);
	}
}; // END: setFlashVariable

PAGE_BODY.setTitle = function(titleStr) {
	this.setFlashVariable("titleboxFlash", "titletext", titleStr);
}; // END: setTitle

PAGE_BODY.getWindowRef = function() {
	return (this.type == "IFRAME") ? window.main : window;
}; // END: setTitle

// Write content string to the page body.
PAGE_BODY.write = function(content) {

	if(this.type=="IFRAME") {
		writeBodyFrame(content);
	} else {	
		document.getElementById(this.node_id).innerHTML = content;
	}
	this.load_complete = true;

}; // END: write

PAGE_BODY.isLoaded = function() {
	return (window.main);
	//return (window.main && window.main.addButtonHighlights);
}
/****************************************
	PRIVATE UTILS
*****************************************/

PAGE_BODY._instantiate = function() {
	if(target_node = document.getElementById("main")) {
		this.type = target_node.tagName;
	} else {
		setTimeout(PAGE_BODY._instantiate, 100);
	}
}; // END: _instantiate

// Go ahead and instantiate now.
PAGE_BODY._instantiate();

PAGE_BODY._fireLoadListeners = function() {
	var listener = "none";
	// var str = "";
	try {
		for(var i=0;i<this.load_listeners.length;i++) {
			listener = this.load_listeners[i];
			// str += "firing: "+listener;
			if(typeof(this.load_listeners[i])=="function") {
				this.load_listeners[i]();
			} else {
				window.main.eval(this.load_listeners[i]);
			}
		}

		// alert("notifyFrmaeLoaded output: "+str);
	} catch(e) {
		// alert("ERROR: "+e+": "+(typeof listener)+": "+listener);
	}	
}; // END: _fireLoadListeners


// Called by IFRAME source onload to indicate IFRAME has loaded
// - Note that additional checks are made to ensure scripts have loaded
function notifyFrameLoaded() {
	if(!window.main.addButtonHighlights || !window.main.setFlashVariable) {
		setTimeout(notifyFrameLoaded, 100);
	} else {
		PAGE_BODY._fireLoadListeners();
	}
}; // END: notifyFrameLoaded


function getPageTitleEmbedString(page_type, page_title, section_title) {
	var titleSwf = "/sitewide/flash/titleText.swf";
	switch(page_type) {
		case "playlist_genres":
		case "playlist_artists":
		case "urgeList":
			titleSwf = "/sitewide/flash/titleText_twoPart.swf";break;
		}
	
	return "<object type=\"application/x-shockwave-flash\" data=\""+titleSwf+"\" width=\"503\" height=\"40\">"
			+ "<param name=\"movie\" value=\""+titleSwf+"\" />"
			+ "<param name=\"wmode\" value=\"transparent\" />"
			+ "<param name=\"PLAY\" value=\"true\" />"
			+ "<param name=\"LOOP\" value=\"true\" />"
			+ "<param name=\"flashVars\" value=\"page_title="+encodeURIComponent(page_title)+"&section_title="+encodeURIComponent(section_title)+"\"/>"
			+ "<param name=\"allowScriptAccess\" value=\"sameDomain\"/>"
			+ "</object>";
}

// extension of an idea from simon willison
// http://simon.incutio.com/archive/2004/05/26/addLoadEvent
function addToAfterBodyRenderingComplete(function_to_add) {
	var original_function = finishBodyRendering;

	if (typeof(original_function) != 'function') {
		finishBodyRendering = function_to_add;

	} else {
		finishBodyRendering = function() {
			function_to_add();
			original_function();
		};
	}
} // END: addToAfterBodyRenderingComplete()

/****************************************
	IFRAME CONTENT WRITING UTILS
*****************************************/

function writeBodyFrame(contentString) {
	var iframe_string = "";	

	// Head
	iframe_string += "<?xml version='1.1' encoding='windows-1252'?>" + "\n";
	iframe_string += "<!DOCTYPE HTML PUBLIC '-//W3C//DTD XHTML 1.1 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml11-transitional.dtd'>" + "\n";
	iframe_string += '<html xmlns="http://www.w3.org/TR/REC-html40" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">' + "\n";
	iframe_string += "<head>" + "\n";

	// Scripts
	iframe_string += "<scr" + "ipt type='text/javascript' src='/sitewide/js/main.js'></scr" + "ipt>" + "\n";
	iframe_string += "<scr" + "ipt type='text/javascript' src='/sitewide/js/utils.js'></scr" + "ipt>" + "\n";
	// iframe_string += "<scr" + "ipt type='text/javascript'>var page_title = '"+page_title.replace(/\'/g, "\\\'")+"';</scr" + "ipt>" + "\n";
	iframe_string += "<scr" + "ipt type='text/javascript'>function hoverContainerTitle(){}function unhoverContainerTitle(){}function mousedownContainerTitle(){}</scr" + "ipt>" + "\n";

	// Stylesheets
	// iframe_string += "<link rel='stylesheet' href='/sitewide/styles/common.css' type='text/css'>" + "\n";
	iframe_string += "<link rel='stylesheet' href='/content/components/styles/common.css' type='text/css'>" + "\n";
	if (page_specific_css)
	//	iframe_string += "<link rel='stylesheet' href='" + page_specific_css + "' type='text/css'/>" + "\n";
	iframe_string += "<style type='text/css'>BODY{overflow:auto;border:0;}</style>" + "\n";

	// Body Onload
	iframe_string += "</head><body onload='parent.notifyFrameLoaded();'>" + "\n";
	
	// Page Title
	
	iframe_string += "<div id='pageTitle'>" + "\n";

	if(page_title_obj) {
		if(page_title) {
			page_title_obj.setParam("page_title",page_title);
		}
		// iframe_string += page_title_obj.getEmbedString();
		var section_title = (page_type.indexOf("playlist")>=0)?"PLAYLISTS":"";
		iframe_string += getPageTitleEmbedString(page_type, page_title, section_title);
	}
	iframe_string += "</div>" + "\n";

	if (show_purchasable_toggle) {
		iframe_string += "<div id='displayToggle'>Show Purchasable Only<br />" + "\n";
		iframe_string += "<input type='radio' name='show_purchasable' id='show_purchasable_true' value='true' onclick='parent.toggleShowPurchasableItems(\"true\");' /><label for='show_purchasable_true'>Yes</label> &nbsp; " + "\n";
		iframe_string += "<input type='radio' name='show_purchasable' id='show_purchasable_false' value='false' onclick='parent.toggleShowPurchasableItems(\"false\");' /><label for='show_purchasable_false'>No</label></div>" + "\n";
	}	

	iframe_string += "<div id='main'>" + "\n";
	iframe_string += contentString + "\n";
	iframe_string += "</div>" + "\n";

	iframe_string += "</body></html>" + "\n";

	window.main.document.write(iframe_string);

	window.main.document.close();
}; // END: writeBodyFrame

/****************************************
	DEBUGGING
*****************************************/



var DEBUG = false;
var DEBUG_MESSAGE_TYPES = ["ERROR","WARNING"];
var debug_win;

// i'd love to use something like java.lang.System.out.println(message), but IE doesn't have LiveConnect like mozilla does
function debug(message_type, message) {
	if (DEBUG && (DEBUG_MESSAGE_TYPES.join(" ").indexOf(message_type) > -1 )) {
		if (!debug_win) {
			debug_win = window.open("","","");
			debug_win.document.open();
		}
		message = message.replace(/</g, "&lt;");
		message = message.replace(/>/g, "&gt;");
		
		try {
			debug_win.document.write("");
		} catch(e) {
			debug_win = window.open("","","");
			debug_win.document.open();			
		}

		debug_win.document.write("<h1 style='font-size: 80%; margin:0;border-bottom: 1px solid #CCC;'>" + message_type  + "</h1>");
		debug_win.document.write("<pre style='font-size: 75%; margin: .5em 0 2em 0;'>" + message + "</pre>");
	}
} // END: debug()

function escapeHTML(str) {
	return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

// this is just temp to be able to capture the url in a new window -- LM
function debugXmlUrl(xml_url) {
	if (!DEBUG_XML) return;
	window.open(xml_url, "");
} // END: debugXmlUrl()




/**************************
*	Debug div
***************************/

var dbgDiv;
var queuedDbg = new Array();
// DBG:

function toggleDbgDiv() {
	if(document.getElementById("dbgDiv").style.display == "block") {
		document.getElementById("dbgDiv").style.display = "none";
	} else {
		document.getElementById("dbgDiv").style.display = "block";
	}
}

function dbg(str, allowHTML) {
	try {
		if(window.loaded) {
			if(typeof dbgDiv=="undefined") return;

			if(!dbgDiv) {
				dbgDiv = document.getElementById("dbgDiv");
			}
			if(!dbgDiv) return;
			if(!allowHTML)
				str = escapeHTML(str);
			dbgDiv.innerHTML += ((new Date()).getTime()/1000 + "").substring(7)+": "+str+"<br/>";

		} else {
			queuedDbg.push(str);
			addWindowEvent("onload", flushQueuedDbg);
		}
	} catch(e) {
		
	}
}

var instrumentationDbg = new Array();
function dbgInstrumentation(str) {
	instrumentationDbg.push(str);
}

function flushQueuedDbg() {
	if(!dbgDiv) {
		dbgDiv = document.getElementById("dbgDiv");
	}
	if(!dbgDiv) return;
	if(queuedDbg) {
		for(var i=0;i<queuedDbg.length;i++) {
			dbgDiv.innerHTML += "- "+queuedDbg[i]+"<br/>";
		}
		queuedDbg = false;
	}			
}

function _writeDbgDiv() {
	if(window.loaded) {

		dbgDiv = document.createElement("DIV");
		dbgDiv.id = "dbgDiv";
		dbgDiv.style.width = "640px";
		dbgDiv.style.height = "200px";
		dbgDiv.style.backgroundColor = "white";
		dbgDiv.style.color = "gray";
		dbgDiv.style.padding = "1px";
		dbgDiv.innerHTML = "";

		document.getElementById("dbgControls").appendChild(dbgDiv);

		if(queuedDbg) {
			for(var i=0;i<queuedDbg.length;i++) {
				dbgDiv.innerHTML += "- "+queuedDbg[i]+"<br/>";
			}
			queuedDbg = false;
		}
	} else {
		setTimeout(_writeDbgDiv, 100);
	}
}

function toggleDBGControls() {
	with (document.getElementById("dbgControls").style) {
		display = (display=="block")?"none":"block";
	}
}

var dbg_window;

function launchDBGWindow() {
	var div;
	var str = "";
	if(div = document.getElementById("dbgDiv")) {
		str = div.innerHTML;
	}

	var dbg_window_avail = dbg_window?true:false;
	try {
		dbg_window.document.write("");
	} catch(e) {
		dbg_window_avail = false;
	}

	if(!dbg_window_avail) {
		dbg_window = window.open("", "dbg_window", "width=600,height=400,status=no,toolbar=no,scrollbars=yes,resizable=yes");
		dbg_window.document.open();
		dbg_window.document.write("<style type='text/css'>BODY{font-family:Arial;font-size:11px;}BODY *{font-family:Arial;font-size:11px;}</style>");
	}

	try {
		dbg_window.document.write(str);
	} catch(e) {}
}

function getCurrentContentDirectory() {
	var url = "";
	if(query.get("section")) {
		url = "/content/"+query.get("section")+"/"+query.get("page");
	} else {
		//url = window.location.href.substring(0, window.location.substring(;
	}
	return url;
}

function getCurrentXML() {
	var idParamName = (query.get("page")=="style")?"genreId":
		((query.get("page")=="title")?query.get("section").substring(0, query.get("section").length-1)+"Id":
		(query.get("page")+"Id"));
	var url = getCurrentContentDirectory() + "/xmlgen.jhtml?"+idParamName+"="+query.get("id");
	return url;
}

function getAlbumtextURL() {
	var url = "/inhouse/test/music/album.jhtml?albumId="+query.get("id");
	return url;
}

function getCurrentXSL() {
	return getCurrentContentDirectory() + "/wmp.xsl";
}

function popServerSideTransform() {
	var xml = getCurrentXML();
	var xsl = getCurrentXSL();

	xml = xml.replace(/\?/g, "__QUESTION_MARK__");
	xml = xml.replace(/&/g, "__AMPERSAND__");
	xml = xml.replace(/=/g, "__EQUALS__");

	xsl = xsl.replace(/\?/g, "__QUESTION_MARK__");
	xsl = xsl.replace(/&/g, "__AMPERSAND__");
	xsl = xsl.replace(/=/g, "__EQUALS__");

	var url = "/inhouse/pbeaudoin/xslTransform.jhtml?XML="+xml+"&XSL="+xsl;

	window.open(url, "", "");
}

function popCookie() {

	var str = ""+document.cookie;
	str = str.replace(/; /g, "<br/>");


	var dbg_window = window.open("", "dbg_window", "width=1000,height=400,status=no,toolbar=no,resize=yes");
	dbg_window.document.open();
	dbg_window.document.write("<style type='text/css'>BODY{font-family:Arial;font-size:11px;}BODY *{font-family:Arial;font-size:11px;}</style>");

	try {
		dbg_window.document.write(str);
	} catch(e) {}
}

function popInstrumentation() {

	var str = "";

	for(var i=0;i<instrumentationDbg.length;i++) {
		str += "<br/>"+instrumentationDbg[i];
	}

	var dbg_window = window.open("", "dbg_window", "width=1000,height=400,status=no,toolbar=no,resize=yes");
	dbg_window.document.open();
	dbg_window.document.write("<style type='text/css'>BODY{font-family:Arial;font-size:11px;}BODY *{font-family:Arial;font-size:11px;}</style>");
	dbg_window.focus();

	try {
		dbg_window.document.write(str);
	} catch(e) {}
}

function clearLog() {
	document.getElementById("dbgDiv").innerHTML = "";
}



/****************************************
	STRING UTILS
*****************************************/

// concatURL:
// Intelligently joins a base URL to a string of 'extra params'. 
// Steps carefullly around existing '?'s and '&'s to make sure the URL isn't broken, e.g.:
// 		concatURL("http://google.com/", "foo=bar") yields 'http://google.com/?foo=bar'
// 		concatURL("http://google.com/?foo=bar", "pop=fizz") yields 'http://google.com/?foo=bar&pop=fizz'

function concatURL(base, extraParams) {
	if(base.indexOf("?")>=0) {
		base_query = base.substring(base.indexOf("?")+1);
		base = base.substring(0, base.indexOf("?"));
	} else
		base_query = "";
	extraParams = (extraParams.charAt(0)=="?")?extraParams.substring(1):extraParams;
	extraParams = base_query + "&" + extraParams;
	pairs = extraParams.split("&");
	var params = new Array();
	for(var i=0;i<pairs.length;i++) {
		vals = pairs[i].split("=");
		if(vals.length>0 && vals[0].length>0) {
			var k = vals[0];
			var v = vals[1]?vals[1]:"";
			params.push(k+"="+v);
		}
	}
	return base + "?" + params.join("&");
}

function escapeBadChars(bad_string) {
	var bad_chars = "* @ - _ + . / \\".split(" ");

	for (var i=0; i < bad_chars.length; i++) {
		bad_string = eval("bad_string.replace(/[" + ((bad_chars[i].charAt(0) == "\\") ? "\\\\" : bad_chars[i].charAt(0)) + "]/g, \"%" + (bad_chars[i].charCodeAt(0).toString(16)) + "\")");
	}

	return bad_string;
} // END: escapeBadChars()

function QueryParser(url) {
	this.queryString = (url.indexOf("?")>=0)?url.substring(url.indexOf("?")+1):"";
	if(this.queryString.indexOf("#") >=0) {
		this.queryString = this.queryString.substring(0, this.queryString.indexOf("#"));
	}
	this.params = {};
	this.init();
}
QueryParser.prototype.get = function(val) {
	return this.params[val];
}
QueryParser.prototype.hasValue = function(val) {
	return this.get(val)?true:false;
}
QueryParser.prototype.init = function() {
	var params = this.queryString.split("&");
	for(var i in params) {
		var param = params[i].split("=");
		var key = param[0];
		var val = param[1];
		this.params[key] = val;
	}
}

// Limit character count on a given textarea or text input
// Usage: <textarea onkeypress="constrainCharCount(this, 99);"></textarea>
function constrainCharCount(field, maxlimit) {
	if(field.value.length >= maxlimit ) {
		field.value = field.value.substring( 0, maxlimit);
		return false;
	}
}

function formatIntegerWithCommas(unformatted_integer) {
	var integer_as_string = parseInt(unformatted_integer) + "";

	// catch bad parameters and return them untouched
	if ( integer_as_string != (unformatted_integer + "") ) return unformatted_integer;

	var i = integer_as_string.length - 1;
	var digit_count = 0;
	var digits_array = [];

	while (i >= 0) {
		if ( ((digit_count % 3) == 0) && (digit_count != 0)) digits_array.push(",");
		digits_array.push(integer_as_string.charAt(i));
		digit_count++;
		i--;
	}

	digits_array.reverse();
	return digits_array.join("");
} // END: formatIntegerWithCommas()


// getNameValueMap
// Takes a raw query-style string, returns a map with the name-value pairs represented in that string.
// Optionally override default delimiters ("&" and "=" respect.)
// 'strict' mode (default false) dissallows delim2 to appear in values, as in:
// 		var map = getNameValueMap("foo=bar&test=value=with=delim2s")
// 		'map' is now {foo: 'bar', test: 'value'}
// With strict==false, the values of 'map' above would be:
// 		{foo: 'bar', test: 'value=with=delim2s'}

function getNameValueMap(str, delim1, delim2, strict) {
	delim1 = (typeof delim1 == "undefined")?"&":delim1;
	delim2 = (typeof delim2 == "undefined")?"=":delim2;
	strict = (typeof strict == "undefined")?false:strict;
	var m = {};
	var pairs = str.split(delim1);
	for(var i=0;i<pairs.length;i++) {
		var vals = pairs[i].split(delim2);
		var key = vals[0];
		var val = strict?vals[1]:pairs[i].substring(key.length+delim2.length);
		m[key] = val;
	}
	return m;
}

// from http://www.codetoad.com/javascript/isnumeric.asp
function isNumeric(sText) {
	if(typeof sText == "undefined") return false;

	var ValidChars = "0123456789.";
	var IsNumber=true;
	var Char;

	for (i = 0; i < sText.length && IsNumber == true; i++) { 
		Char = sText.charAt(i); 
		if (ValidChars.indexOf(Char) == -1) {
			IsNumber = false;
		}
	}
	return IsNumber;
}

function formatCurrency(num, _omitDollarSign) {
	var omitDollarSign = _omitDollarSign?_omitDollarSign:false;

	num = parseFloat(num);

	// Lop off needless precision
	num = Math.ceil(num*100) / 100;

	// Format with trailing zeros
	var ret = "" +num;
	ret += (ret.indexOf(".")<0)?".":"";
	for(var i=1;i<3;i++)
		ret += (ret.indexOf(".")==ret.length-i)?"0":"";

	// prefix with dollar sign
	ret = (omitDollarSign?"":"$") + ret;

	return ret;
}


/****************************************
	FLASH UTILS
*****************************************/

function setFlashVariable(flash_movie_name, variable, value) {
	var doc_ref = window.document;
	if (!doc_ref[flash_movie_name]) {
		doc_ref = window.main ? window.main.document : doc_ref;
	}
	if (!doc_ref[flash_movie_name]) {
		doc_ref = (window.taxonomyFrame ? window.taxonomyFrame.document : doc_ref);
	}

	if (!doc_ref || !doc_ref[flash_movie_name]) return false;

	doc_ref[flash_movie_name].SetVariable(variable, value);
	return true;
} // END: setFlashVariable

function cleanDataForFlash(data_to_clean) {
	var cleaned_data = data_to_clean;
	cleaned_data = cleaned_data.replace(/[\t\r\n]/g, "");
	cleaned_data = cleaned_data.replace(/\&amp;/g, "&");
	return cleaned_data;
} // END: cleanDataForFlash()

function getVariable(varName, movieName) {
	var value = eval(varName);
	setFlashVariable(movieName, varName, value);
}


/****************************************
	DOM UTILS
*****************************************/

document.getElementsByClass = function (needle) {
	function _GetElementsByClass(outArray, seed, needle) {
		while (seed) {
			if (seed.nodeType == 1) {
				if (seed.className) {
					var c = " " + seed.className + " ";
					if (c.indexOf(" " + needle + " ") != -1)
					outArray.push(seed);
				}
				_GetElementsByClass(outArray, seed.firstChild, needle)
			}
			seed = seed.nextSibling;
		}
	}
	var outArray = new Array();
	_GetElementsByClass(outArray, document.documentElement, needle);
	return outArray;
} // END getElementsByClass()


// extension of an idea from simon willison
// http://simon.incutio.com/archive/2004/05/26/addLoadEvent
// adds function call to an arbitrary window event trigger
function addWindowEvent(event_name, function_to_add) {
  var current_event = window[event_name];
  
  //dbg("adding "+function_to_add+" to "+current_event);

  if (typeof(current_event) != 'function') {
    window[event_name] = function_to_add;

  } else {
    window[event_name] = function() {
      current_event();
      function_to_add();
    };
  }
} // END: addWindowEvent()

function getWindowWidth() {
	var val = 0;
	try {
		val = document.body.offsetWidth-20;
	} catch(e) {}
	return val;
}

function getElementByClassName(elem, className) {
	for(var i=0;i<elem.childNodes.length;i++) {
		if(elem.childNodes[i].className && elem.childNodes[i].className==className)
			return elem.childNodes[i];
		else {
			var e = getElementByClassName(elem.childNodes[i], className);
			if(e) return e;
			else continue;
		}
	}
	return false;
}




function array_search(a, val) {
	for(var i in a) {
		if(a[i] && a[i]==val)
			return i;
	}
	return -1;
}


function xml_getAttribute(nodeObj, att) {
	if(typeof nodeObj == "undefined") return;
	if(typeof nodeObj.attributes == "undefined") return;
	for(var j=0;j<nodeObj.attributes.length;j++) {
		if(nodeObj.attributes[j].nodeName==att)
			return nodeObj.attributes[j].value;
	}
}

//urlencode all characters including single quotes, double quotes, plus sign and slash
function URLencode(sStr) {
	return escape(sStr).
	replace(/\+/g, '%2B').
	replace(/\"/g,'%22').
	replace(/\'/g, '%27').
	replace(/\@/g, '%40').
	replace(/\//g,'%2F');
}


// same as function below but not implemented
function disableBtn(myDocument,id) {
	var elem = myDocument.getElementById(id);
	var str = elem.href + " ";
	str = unescape(str.replace("javascript:", ""));
	var func = str;
	
	if (elem.href != 'javascript:void(0)'){	
	
		//save playlist
		eval(func);
		
		//trap href before disable
		var hrefHold = elem.href;
		
		//disable button
		elem.href='javascript:void(0)';	
		
		//change button style
		var textSpan, iconImg, elemAnchor = elem;
		
		// trap all link info before disable
		var cursorHold = elem.style.cursor;
		var mouseOutHold = elem.onmouseout;
		var mouseOverHold = elem.onmouseover;
		
		elem.style.cursor = "default";
		elem.onmouseout = null;
		elem.onmouseover = null;
		
		for(var i=0;i<elem.childNodes.length;i++) {
			if(elem.childNodes[i].tagName=="SPAN") {
				textSpan = elem.childNodes[i];				
			}else if(elem.childNodes[i].tagName=="IMG") {
				iconImg = elem.childNodes[i];
			}
		}
		
		// trap data before overwriting
		var textHold = textSpan.innerHTML;
		
		switch(iconImg.className){
			case "save_feed":
				textSpan.innerHTML = "Saved";
				break;
			case "save_station":
				textSpan.innerHTML = "Saved";
				break;	
			case "download":
				textSpan.innerHTML = "Downloading";
				break;
			case "play":
				textSpan.innerHTML = "Playing";
				break;
		}	
		
		setTimeout(function() {
			textSpan.style.color = "#999";
			setNewStyleFilter(iconImg);
			elemAnchor.className = "button-disabled";}, 0)
		
		// restore button after 5 seconds except for save feed and save station
		switch(iconImg.className){
			case "save_feed":
				break;
			case "save_station":
				break;	
			default:
				setTimeout(function() {
					textSpan.style.color = "#fff";
					resetNewStyleFilter(iconImg);
					elemAnchor.className = "button";
					textSpan.innerHTML = textHold;
					elem.onmouseout = mouseOutHold;
					elem.onmouseover = mouseOverHold;
					elem.style.cursor = cursorHold;
					elem.href = hrefHold;	
				}, 5000);
				break;
		}	
		
	}
	
}

function disableButton(myDocument,altBtn) {
	var elem = myDocument.getElementById("saveList");
	if (altBtn) elem = myDocument.getElementById(altBtn);
	alert(elem.id)
	var str = elem.href + " ";
	str = unescape(str.replace("javascript:", ""));
	var func = str;
	
	if (elem.href != 'javascript:void(0)'){	
	
		//save playlist
		eval(func);  
		
		//disable button
		elem.href='javascript:void(0)';	
		
		//change button style
		var textSpan, iconImg, elemAnchor = elem;
		elem.style.cursor = "default";
		elem.onmouseout = null;
		elem.onmouseover = null;
		
		for(var i=0;i<elem.childNodes.length;i++) {
			if(elem.childNodes[i].tagName=="SPAN") {
				textSpan = elem.childNodes[i];
				
			}else if(elem.childNodes[i].tagName=="IMG") {
				iconImg = elem.childNodes[i];
			}
		}
		if (iconImg.className != "download"){
		textSpan.innerHTML = "Saved";
		}
		setTimeout(function() {
			textSpan.style.color = "#999";
			setNewStyleFilter(iconImg);
		elemAnchor.className = "button-disabled";}, 0)
		
		if (elem.className = "button-disabled"){
			elem.onmouseout = null;
			elem.onmouseover = null;
		}
	}
	
}


function saveFeedButton(myDocument, enableButton) {
	var elem = myDocument.getElementById("saveList");
	var str = elem.href + " ";
	str = unescape(str.replace("javascript:", ""));
	var func = str;
	
	if (elem.href != 'javascript:void(0)'){	
	
		//save playlist
		eval(func);  
		
		//disable button
		elem.href='javascript:void(0)';	
		
		//change button style
		var textSpan, iconImg, elemAnchor = elem;
		elem.style.cursor = "default";
		elem.onmouseout = null;
		elem.onmouseover = null;
		
		for(var i=0;i<elem.childNodes.length;i++) {
			if(elem.childNodes[i].tagName=="SPAN") {
				textSpan = elem.childNodes[i];
				
			}else if(elem.childNodes[i].tagName=="IMG") {
				iconImg = elem.childNodes[i];
			}
		}
		if (iconImg.className != "download"){
		textSpan.innerHTML = "Saved";
		}
		setTimeout(function() {
			textSpan.style.color = "#999";
			setNewStyleFilter(iconImg);
		elemAnchor.className = "button-disabled";}, 0)
		
		if (elem.className = "button-disabled"){
			elem.onmouseout = null;
			elem.onmouseover = null;
		}
	}		
}





function setNewStyleFilter(imgElem){
	switch(imgElem.className){
		case "save_feed":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveFeed_disabled.png',sizingMethod='scale')";
			break;
		case "save_station":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveStation_disabled.png',sizingMethod='scale')";
			break;
		case "download":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download_disabled.png',sizingMethod='scale')";
			break;	
		case "play":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/play_disabled.png',sizingMethod='scale')";
			break;
	}
}

function resetNewStyleFilter(imgElem){
	switch(imgElem.className){
		case "save_feed":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveFeed.png',sizingMethod='scale')";
			break;
		case "save_station":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveStation.png',sizingMethod='scale')";
			break;
		case "download":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download.png',sizingMethod='scale')";
			break;	
		case "play":
			return imgElem.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/play.png',sizingMethod='scale')";
			break;
	}
}

function escapeQuote(string){
	return string.replace(/\'/g, "\\'");
}

function displayFin(myDocument, str){
	myDocument.getElementById("fin").innerHTML = str;
}

function capitalizeMe(str) {
    val = str;
    newVal = '';
    val = val.split(' ');
    for(var c=0; c < val.length; c++) {
            newVal += val[c].substring(0,1).toUpperCase() + val[c].substring(1,val[c].length) + ' ';
    }
       return newVal;
	   
}


var _preloadedImages = new Array();
function preloadImage(src) {
	var img = new Image();
	img.src = src;
	
	_preloadedImages[_preloadedImages.length] = img;
}

function roll(img,from,to) {
	img.src = img.src.replace(from,to);
}

/****************************************
	MAIN INTERFACE
*****************************************/

// called when the page is finished loading
function removeLoadingMessage() {
	if (loading_div = document.getElementById("loadingDiv")) loading_div.style.display = "none";
} // END: removeLoadingMessage()

// remove scrollbars on navigation
function removeScrollbars() {
	if (main_div = document.getElementById('main')) main_div.style.overflow = 'hidden';
} // END: removeScrollbars()

// remove scrollbars on navigation
function showScrollbars() {
	if (main_div = document.getElementById('main')) main_div.style.overflow = 'auto';
} // END: removeScrollbars()

// does show/hide used in genre descriptions
function dmsSwap(sDiv,hDiv) {
	document.getElementById(hDiv).className = "dms-hidden";
	document.getElementById(sDiv).className = "dms-visible";
} // END: dmsSwap()

// does show/hide by class
function dmsSwapByClass(showClassName,hideClassName) {
	var hides = PAGE_BODY.getWindowRef().document.getElementsByClass(hideClassName);
	var shows = PAGE_BODY.getWindowRef().document.getElementsByClass(showClassName);
	for(var i=0;i<hides.length;i++) { hides[i].style.display = "none"; }
	for(var i=0;i<shows.length;i++) { shows[i].style.display = "block"; }
} // END: dmsSwapByClass()

function sendToAFriend( _section, _page, _mode, _id, _viewParams ) {
	// _section  _page _mode _id reserved for future use
	
	// Get 'type' and 'id' values to pass to send-to-friend page
	var page = "", id = "", launch_id = "", mode="";
	
	var launchURL;
	
	launchURL = "launch_section="+query.get("section")+
				"&launch_page="+query.get("page")+
				"&launch_id="+query.get("id");
	
	if (typeof query.get("mode") != 'undefined') {
		launchURL += "&launch_mode="+query.get("mode");
	}		
	
	if (typeof _viewParams != 'undefined') {
		launchURL += "&viewParams="+_viewParams;
	}
	
	showPopup ("sendToAFriend", launchURL);
}

function sendToAFriendNowPlaying(section, id) {

	// Get 'type' and 'id' values to pass to send-to-friend page
	var page = "";
	switch(section) {
		case "radio":	page = "station";
						break;
	}
	showPopup ("sendToAFriend", "launch_section="+query.get("section")+"&launch_page="+page+"&id="+id);
}
// BEGIN: utility functions ====================================================
// these functions are called from other functions
// you may add new functions here

// this gets called after the body has been inserted into the page
function finishBodyRendering() {
	removeLoadingMessage();
	applyButtonHovers();

} // END: finishBodyRendering()

// END: utility functions ------------------------------------------------------



/****************************************
	UI UTILS
*****************************************/

function applyImageHovers() {
	var imgDivs = PAGE_BODY.getWindowRef().document.getElementsByClass("linked-image-border");
	for(var i=0;i<imgDivs.length;i++) {
		imgDivs[i].onmouseover = function() {
			this.style.borderColor = "#FF0B01";
		}
		imgDivs[i].onmouseout = function() {
			this.style.borderColor = "#000000";
		}
	}
}

function handleFlashMouseOut(flashId) {
	var flashMovie;
	if(flashMovie = document.getElementById(flashId)) {
		flashMovie.SetVariable('__external_mouse_data', 'onMouseOut');
	}
}

function applyButtonHovers() {
	var buttonLinks = document.getElementsByClass("button");
	for(var i=0;i<buttonLinks.length;i++) {

		buttonLinks[i].onmouseover = function() {
			switch(this.childNodes[0].className) {
				case "buy":			this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/buy_on.png',sizingMethod='image');";break;
				case "play":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/play_on.png',sizingMethod='image');";break;
				case "save":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download_on.png',sizingMethod='image');";break;
				case "watch":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/watch_on.png',sizingMethod='image');";break;
				case "download":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download_on.png',sizingMethod='image');";break;
				case "save_feed":	overlib('<strong>What are Feeds?</strong><br>Feeds are automatically updating playlists that keep you and your portable music player stocked with fresh music. <a href="javascript:nav(\'pages\',\'feeds\',\'\',\'200\');">Learn more</a>.','','','', STICKY, NOCLOSE, OFFSETY, 0, OFFSETX, 0, RIGHT, BELOW, DELAY, 1000); this.style.cursor = 'help';this.style.textDecoration = 'none';this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.urge.com/sitewide/img/content/button/saveFeed_on.png',sizingMethod='image');";document.getElementsByTagName('html')[0].style.overflow = 'hidden';break;
				case "refresh":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/refresh_on.png',sizingMethod='image');";break;
				case "save_station":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveStation_on.png',sizingMethod='image');";break;
				case "searchBtn":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/confirm_on.png',sizingMethod='image');";break;
				case "share":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/share_on.png',sizingMethod='image');";break;
				default:		break;
			}
		}
		buttonLinks[i].onmouseout = function() {
			switch(this.childNodes[0].className) {
				case "buy":			this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/buy.png',sizingMethod='image');";break;
				case "play":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/play.png',sizingMethod='image');";break;
				case "save":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download.png',sizingMethod='image');";break;
				case "watch":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/watch.png',sizingMethod='image');";break;
				case "download":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/download.png',sizingMethod='image');";break;
				case "save_feed":	nd(); this.style.cursor = 'default';this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.urge.com/sitewide/img/content/button/saveFeed.png',sizingMethod='image');";document.getElementsByTagName('html')[0].style.overflow = 'hidden';break;
				case "refresh":		this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/refresh.png',sizingMethod='image');";break;
				case "save_station":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/saveStation.png',sizingMethod='image');";break;
				case "searchBtn":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/confirm.png',sizingMethod='image');";break;
				case "share":	this.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/share.png',sizingMethod='image');";break;
				default:		break;
			}
		}
		buttonLinks[i].onfocus = function() {
			this.blur();
		}

		preloadImage("/sitewide/img/content/button/buy_on.png");
		preloadImage("/sitewide/img/content/button/saveFeed_on.png");
		preloadImage("/sitewide/img/content/button/download_on.png");
	}
}


function setAnchorTargetsVox() {
	var anchors = window.main.document.getElementsByTagName("A");
	for(var i=0;i<anchors.length;i++) {
		if(!anchors[i].target && anchors[i].href.substring(0,"javascript".length)!="javascript")
			anchors[i].target = "VOX";
	}
}

function hoverHubTitle(div, hubname) {
	try {
		var seeAllTD = div.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[1];
		seeAllTD.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/channels/"+hubname+"/see_all_1.png',sizingMethod='scale')";
	} catch(e) {}
}

function unhoverHubTitle(div, hubname) {
	try {
		var seeAllTD = div.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[1];
		seeAllTD.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/channels/"+hubname+"/see_all_0.png',sizingMethod='scale')";
	} catch(e) {}
}

function hoverContainerTitle(div) {
	try {
		var seeAllTD = div.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[1];
		seeAllTD.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/arrow.1.png',sizingMethod='scale')";
	} catch(e) {}
}

function unhoverContainerTitle(div) {
	try {
		var seeAllTD = div.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[1];
		seeAllTD.childNodes[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/arrow.0.png',sizingMethod='scale')";
	} catch(e) {}
}

function hoverSoloSeeAllLink(a) {
	try {
		for(var i=0;i<a.childNodes.length;i++) {
			if((a.childNodes[i].tagName=="IMG") && (a.childNodes[i].id!="ArrowLink")) {
				a.childNodes[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/arrow.1.png',sizingMethod='scale')";
			}
		}
	} catch(e) {}
}

function unhoverSoloSeeAllLink(a) {
	try {
		for(var i=0;i<a.childNodes.length;i++) {
			if((a.childNodes[i].tagName=="IMG") && (a.childNodes[i].id!="ArrowLink")) {
				a.childNodes[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/arrow.0.png',sizingMethod='scale')";
			}
		}
	} catch(e) {}
}


var clickedButton = false;

function applySupportButtonHovers() {

	var hoverButtons = document.getElementsByClass("hoverButton");
	for(var i=0;i<hoverButtons.length;i++) {
		var btn = hoverButtons[i];

		preloadImage(btn.src.substring(0, btn.src.length-4) + "_on.gif");

		btn.onmouseover = function() {
			if(this.src.indexOf("_on")<0/* && !clickedButton*/)
				this.src = this.src.substring(0, this.src.length-4) + "_on.gif";
		}
		btn.onmouseout = function() {
			// if(clickedButton) return;

			if(this.src.indexOf("_on")>=0)
				this.src =  this.src.substring(0, this.src.length-7) + ".gif";
		}
		if(!btn.onclick) {
			btn.onclick = function() {
				clickedButton = this;
			}
		}
	}
}


// Set flash title values
function setPageTitle(str1, str2) {
	var new_page_title = false;
	var new_section_title = false;
	if(typeof str2 == "undefined") {
		new_page_title = str1;
	} else {
		new_page_title = str2;
		new_section_title = str1;
	}

	var swf;
	if(swf = document["pageTitleFlash"]) {
		if(new_section_title) {
			swf.SetVariable("dyn_section_title", new_section_title);
			dbg("set section_title to "+new_section_title);
		}
		swf.SetVariable("dyn_page_title", new_page_title);
		dbg("set page_title to "+new_page_title);
	} else
		dbg("couldn't find pageTitle");
}

function setSectionTitleLink(str) {
	var swf;
	if(swf = document["pageTitleFlash"]) {
		swf.SetVariable("dyn_section_link", str);
	}
}

var tabbedContainer_current = "genre";
function tabbedContainerSwitch(name) {
	if(name==tabbedContainer_current) return;

	// Show new tab
	var anchor = document.getElementById("tabbedContainer_"+name+"Tab");
	for(var i=0;i<anchor.childNodes.length;i++) {
		if(anchor.childNodes[i].tagName=="IMG") {
			anchor.childNodes[i].src = "/sitewide/img/content/home/tabbedContainer/"+name+"_on.jpg";
		}
	}
	anchor.style.cursor = "default";
	var contentDiv = document.getElementById("tabbedContainer_"+name+"Content");
	contentDiv.style.display = "block";

	// Hide current tab:
	var anchor = document.getElementById("tabbedContainer_"+tabbedContainer_current+"Tab");
	for(var i=0;i<anchor.childNodes.length;i++) {
		if(anchor.childNodes[i].tagName=="IMG") {
			anchor.childNodes[i].src = "/sitewide/img/content/home/tabbedContainer/"+tabbedContainer_current+".jpg";
		}
	}
	var contentDiv = document.getElementById("tabbedContainer_"+tabbedContainer_current+"Content");
	contentDiv.style.display = "none";
	anchor.style.cursor = "hand";

	tabbedContainer_current = name;
}

function exposeCharts() {
	document.getElementsByClass('shown')[0].className = 'hidden';
	document.getElementById(document.getElementById('year').value).className = 'shown';
}
function gotoChart() {
	var currentChartSelect = document.getElementsByClass('shown')[0];
	if (currentChartSelect.value) {
		nav('charts','billboard',currentChartSelect.options[currentChartSelect.selectedIndex].name,currentChartSelect.value);
	}
}

function launchVideo(trackIdString, audvidIdString, channelHubReferrer) {
	
	var DMSCookie = null;
	var referringBrand = channelHubReferrer;
	if ((referringBrand == "") &&
		(CookieManager.getRawCookie('DMS_Conversion') != null)) {
		referringBrand = CookieManager.getRawCookie('DMS_Conversion');
		referringBrand = referringBrand.split("|")[0];
	}
	var videoURL = "http://" + document.location.hostname + 
					"/sitewide/dataservices/video/index.asx?" +
					"trackId=" + trackIdString;
	if (referringBrand != "") {
		videoURL = videoURL + "&siteOrigin=" + referringBrand;
	}
	if ((trackIdString == '0') && (audvidIdString != "")) {
		videoURL = videoURL + "&audvidId=" + audvidIdString;
	}
	// Create a hidden player object in the page in order to access the Player methods.
	try {
		var objectString = '<OBJECT ID="TempPlayer" ';
		objectString += 'CLASSID = "CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" width="0" height="0"> ';
		objectString += '<PARAM NAME="autoStart" VALUE="true"> ';
		objectString += '</OBJECT>';
		document.getElementById("videoObject").innerHTML = objectString;
	} catch(e) {
		dbg("Video launch failed!");
	}
	//Use the player object to switch view to Now Playing and play video.
	TempPlayer.openPlayer(videoURL);
}

// Global nav script
function toggleGNControls() {
	if (document.getElementById("topPageFormItem")) {
		with (document.getElementById("topPageFormItem").style) {
			visibility = (visibility=="hidden")?"visible":"hidden";
		}
	}
	with (document.getElementById("gnDiv").style) {
		display = (display=="block")?"none":"block";
	}
}
/*******************************
TEMPLATE LISL MAP
********************************/

var SERVICE_URLS = {
3001:	"/switch/?section=artists&page=videos",
3002:	"/switch/?section=franchises&page=franchise",
3003:	"/switch/?section=franchises&page=archive",
3004:	"/switch/?section=channels&page=dev",
3005:	"/switch/?section=channels&page=mtv",
3006:	"/switch/?section=channels&page=vh1",
3007:	"/switch/?section=channels&page=cmt"
};


var POPUP_CONFIG = {
	why:					1001,
	sendToAFriend:			1002,
	termsOfUse:				1003,
	sendFeedback:			1005,
	stationFeedback:		1006,
	faq:					1004,
	securitycode:			1007,
	syncRestore:			1008,
	incompletePurchases:	1011,
	autopilot:				1009,
	privacyStatement: 		1010,
	printTermsAndPolicy:	1012,
	janusDevices: 			1013,
	advancedSearch:			1016,
	reportBug:				1014,
	interstice:				1017,
	contactUs:				1018,
	freeTrialTerms:			1019,
	registeredUserAgreement:1020,
	eula:					1021,
	combinedTermsSubscriptionAgreement:	1022,
	musicnetConnectionTimeout:	1023,
	recsError:				1024,
	sample_popup:			1026,
	recsFeedback: 			1027
};


var SiteConfig = {

	getPageConfig:	function(section, page, mode) {
		try {
			// All page names have been prepended with a '_' to prevent conflicts with JS reserved names
			var linkInfo = PIVOT_NAMES[section];
			if(linkInfo["_"+page]) {
				linkInfo = linkInfo["_"+page]["_"+mode];
			} else {
				linkInfo = linkInfo["_title"]["_"+mode];
			}

			return linkInfo;

		} catch(e) {
			dbg("Error getting link info for PIVOT_NAMES["+section+"][_"+page+"][_"+mode+"]: "+e.message);
		}
		return false;

	},
	getPopupIndex:	function(name) {
		var index = POPUP_CONFIG[name];
		if(typeof index != "undefined") {
			return index;
		} else
			return false;
	},
	getAccountPageConfig:	function(section, page) {

	}
};


/*******************************************************************************
*
*	WINDOW.EXTERNAL INTERFACE:
*
*	See http://www.dms-p.mtvi.com/inhouse/pbeaudoin/docs/wmp11_external.xml.jhtml for extensive documentation
*
*******************************************************/

var externalErrorDiv;

var WMP_DETECTED = (typeof window.external.changeView != "undefined");

// COOKIES["wmp_testing"] = new CookieManager("DMS_wmp_testing");

var query = new QueryParser(document.location.href);

var navCalledByDeepLink = false;

/******************************************************************************
	NAVIGATION
*******************************************************************************/

/*******************************************
	PUBLIC FUNCTIONS
********************************************/

/***********************
*
*	nav (section, page, mode, id, additionalParams, wordwheel)
*	OR
*	nav (cmsLinkString)
*
*	Navigates to page given by params
*	e.g. <a href="javascript:nav('artist','main','bio','972');">Bjork</a>
*	OR
*	<a href="javascript:nav('/artist/972');">Bjork</a>
**/
function nav () {

//appendDCookie("WMP Nav");


	// Single argument indicates link is coming from CMS or Biography data
	// Parse the argument as a query of the form '/{itemType}/{itemId}'
	if(arguments.length==1 && arguments[0].toLowerCase()=="return") {
		try {
			window.external.changeView("Return", "", "", "");
		} catch(e) {
			dbg("Error doing a changeView('Return', ...)");
		}
	} else if(arguments.length==1) {

		var str = arguments[0];
		if(str.charAt(0) == "/")
			str = str.substring(1);
		var vals = str.split("/");
		switch(vals[0]) {
			// nonpurchasable needs to be notcatalog
			case "artist":		nav ("artists", "artist", '', vals[1]);break;
			case "artist_np":		nav ("artists", "artist", "nonpurchasable", vals[1]);break;
			case "album":		nav ("albums", "album", '', vals[1]);break;
			case "album_np":		nav ("albums", "album", "nonpurchasable", vals[1]);break;
			case "genre":		nav ("genres", "genre", '', vals[1]);break;
			case "playlist":	nav ("playlists", "playlist", '', vals[1]);break;
			case "station":		nav ("radio", "station", '', vals[1]);break;

			case "artist_template":		nav ("franchises", "franchise", 'artist_template', vals[1]);break;
			case "album_template":		nav ("franchises", "franchise", 'album_template', vals[1]);break;
			case "50_50_song_template":		nav ("franchises", "franchise", '50_50_song_template', vals[1]);break;
			case "90_10_song_template":		nav ("franchises", "franchise", '90_10_song_template', vals[1]);break;
			case "full_html_template":		nav ("franchises", "franchise", 'full_html_template', vals[1]);break;

			case "artist_template_scheduled_content":		nav ("franchises", "archive", 'artist_template_scheduled_content', vals[1]);break;
			case "album_template_scheduled_content":		nav ("franchises", "archive", 'album_template_scheduled_content', vals[1]);break;
			case "50_50_song_template_scheduled_content":		nav ("franchises", "archive", '50_50_song_template_scheduled_content', vals[1]);break;
			case "90_10_song_template_scheduled_content":		nav ("franchises", "archive", '90_10_song_template_scheduled_content', vals[1]);break;
			case "full_html_template_scheduled_content":		nav ("franchises", "archive", 'full_html_template_scheduled_content', vals[1]);break;

			case "50_50_story": nav ("stories", "story", 'story_50_50', vals[1]);break;
			case "90_10_story": nav ("stories", "story", 'story_90_10', vals[1]);break;

			case "blog":		nav ("blogs", "blog", '', vals[1]);break;
			case "blog_entry":		nav ("blogs", "blog", "archive", vals[1]);break;

			case "network_playlist":		nav ("networkplaylists", "networkplaylist", '', vals[1]);break;

			// case "faq":		pop ("faq", vals[1]);break;

			case "channel_hub":		nav ("channels", "channel", 'idBased', vals[1]);break;

			default:			nav ("channels", "dev");
		}

	} else if(arguments.length>1) {

		// Account nav
		if(arguments[0]=="account") {

			accountNav(arguments[1], arguments[2]);

		// Store nav
		} else {
			_navigateLISL (arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
			EventLogger.log("nav", arguments[0], arguments[1], arguments[2], arguments[3], "" , "", "");
		}
	}

	if(!navCalledByDeepLink) {
		// Nav came from an actual user 'click', so get rid of any lingering startup params
		CookieManager.remove("DMS_startup");
		CookieManager.remove("DMS_startup_secure");
	}
}

function navURL(_url) {
	var url = (_url.indexOf("http")<0)?(site_domain+_url):_url;

	try {
		dbg('issuing: window.external.changeView("OnlineStore", "'+url+'");');
		window.external.changeView("OnlineStore", url, "", "");
	} catch(e) {
		dbg('error navigating: window.external.changeView("OnlineStore", "'+url+'");');
	}

}

/***********************
	updateLibraryLocationav (type, id)
	Updates the LISL grid to display the item given by type and id. Page does not refresh.
	Currently only 'playlist' and 'blog' are accepted for type
	e.g. <a href="javascript:updateLibraryLocation('playlist','123412341234');">Show next playlist</a>
**/
function updateLibraryLocation (type, id, mode) {

	mode = (typeof mode != "undefined")?mode:"";

	updateLibraryLocationEnabled = true;

	var templateId = _getCPName(type);

	switch(type) {
		case "playlist":	id = packPlaylistId(id, 1);break;
		case "blog":		id = packPlaylistId(id, 22);break;
	}

	try {
		dbg('issuing window.external.changeView(templateId, id, "", ' + mode?("mode:"+mode):"" + ');');
		window.external.changeView(templateId, id, "", "");

	} catch(e) {
		dbg("error doing window.external.changeView('"+templateId+"', '"+id+"', '', '');");
	}
}

// a wrapper for the nav funtion that disables template navigation
function navLibraryLocation (section, page, mode, id, additionalParams, wordwheel) {
	updateLibraryLocationEnabled = true;
	EventLogger.log("refresh", section, page, mode, id, "", "", additionalParams);
	nav(section, page, mode, id, additionalParams, wordwheel);
}


/*******************************************
	NAV EVENT HANDLING
********************************************/
 
var updateLibraryLocationEnabled = false;
var disableCancelNavigate = false;

/*******************************************
*	handleOnViewChange
* 	Fires when user navigates anywhere. Carries out cancel-navigate policy to prevent redundant page refreshes.
**/
function handleOnViewChange() {
	var localFlagLoadAutoMix = "";
	if(typeof(_flagLoadAutoMix)!="undefined" && _flagLoadAutoMix!=null && _flagLoadAutoMix!="")
	{
		eval("localFlagLoadAutoMix = _flagLoadAutoMix;");
	}


	try {

		// If navigation resulted from an updateLibraryLocation call, cancel-navigate
		if(updateLibraryLocationEnabled) {
			dbg("Cancelling nav because updateLibraryLocationEnabled");

			updateLibraryLocationEnabled = false;

			window.external.cancelNavigate();
			return;
		}
		//Cancelling nav for Recs
		else if(query.get("section") == "recs" || query.get("page") == "recs")
		{

			var cookie = CookieManager.get("saveOnLoad", true);

			//checking if it's trying to do a navAndSave
			if(cookie.get("save") != "please")
			{
				var prevVpObj = viewParamsParser(externalSnapshot.viewParams);
				var currVpObj = viewParamsParser(window.external.ViewParameters);

				if(
					//need extra check for coming in as saved and then click on my automix again
					(prevVpObj != null && currVpObj != null &&
					prevVpObj.customPlaylistId != null && currVpObj.customPlaylistId != null
					&& prevVpObj.customPlaylistId != currVpObj.customPlaylistId
					)||
					(prevVpObj != null && prevVpObj.customPlaylistId !=null &&
					 ((currVpObj != null && currVpObj.customPlaylistId == null) || (currVpObj == null))
					)||
					(currVpObj != null && currVpObj.customPlaylistId !=null &&
					((prevVpObj != null && prevVpObj.customPlaylistId == null) || (prevVpObj == null))
					)
				)
				{
					dbg("Recs Do not Cancel nav because navigating between two different ids");

				}else
				{

					if (
					externalSnapshot.type==window.external.libraryLocationType
					&& externalSnapshot.id==window.external.libraryLocationId
					&& (externalSnapshot.selectedType==window.external.selectedItemType)
					&&  (localFlagLoadAutoMix==null || localFlagLoadAutoMix == false || localFlagLoadAutoMix == "")
					&& (

					     (//only changing types in a rec, dont nav
						(prevVpObj == null || prevVpObj == "")
						&&(currVpObj.t != null && currVpObj.t != "")
					     )||
					     (//Havent chosen any type or anything at all
						(prevVpObj == null || prevVpObj == "")
						&&(currVpObj == null || currVpObj == "")
					     )||
					     (//same customPlaylistId, dont nav
						(prevVpObj.customPlaylistId != null && prevVpObj.customPlaylistId != "")
						&&(currVpObj.customPlaylistId == prevVpObj.customPlaylistId)
					     )||
					     (//same customPlaylistId just different slider params, dont nav
						(prevVpObj.customPlaylistId != null && prevVpObj.customPlaylistId != "")
						&&((currVpObj.customPlaylistId == "" || currVpObj.customPlaylistId == null) && (currVpObj.pop != null && currVpObj.pop != "") )
					     )||
					     (//no customPlaylistId, but is in mode:recs, ie for artist and style mix
						(prevVpObj != null && (prevVpObj.customPlaylistId == null || prevVpObj.customPlaylistId == "") && prevVpObj.mode=="recs")
						&&(currVpObj !=null && (currVpObj.customPlaylistId == null || currVpObj.customPlaylistId == "") && currVpObj.mode=="recs")
					     )||//loadAutoMix
					     (
						(prevVpObj.ids == currVpObj.ids)
					     )
					   )
					)
					{
						dbg("Cancelling Recs and Automixes trying to re-navigate");
						window.external.cancelNavigate();
					}
				}
			}
			return;

		// Otherwise, check new location values against 'current' location values to determine whether or not to cancel-navigate
		} else if (externalSnapshot.type==window.external.libraryLocationType
			&& externalSnapshot.id==window.external.libraryLocationId
			&& (externalSnapshot.selectedType==window.external.selectedItemType)
			&& externalSnapshot.viewParams==window.external.ViewParameters) {

			dbg("disableCancelNavigate is "+disableCancelNavigate);

			// Is this the deep link page? Don't cancelNavigate ever.
			if(window.external.libraryLocationType=="RootLocation" && window.external.libraryLocationId==107) {
				// do nothing

			} else if(!disableCancelNavigate && (!document.getElementById("debug_cancelNavigate") || !document.getElementById("debug_cancelNavigate").checked)) {
				dbg("Cancelling nav because type, id, typeId, and viewParams didn't change: "+externalSnapshot.type+", "+externalSnapshot.id+", "+externalSnapshot.selectedType+", '"+externalSnapshot.viewParams+"'");
				window.external.cancelNavigate();
				return;

			}
			else{
				dbg("Overriding cancelNavigate because you asked me to");

			}

		// Finally, make sure user hasn't explicitly checked 'disable navigation'
		} else if(!disableCancelNavigate && (document.getElementById("debug_nav") && document.getElementById("debug_nav").checked)) {
			dbg("Cancelling nav because 'disable navigation' is checked");
			window.external.cancelNavigate();
			return;
		}

	} catch(e) {
		dbg("ERROR: handleOnViewChange: "+e+": "+e.message);
	}

	// throw up loading div
	if (going_div = document.getElementById("goingDiv"))
		going_div.style.display = "block";
}

function handleOnChangeViewError(error, viewType, viewIds, filter, params) {
	dbg("handleOnChangeViewError('"+error+"', '"+viewType+"', '"+viewIds+"', '"+filter+"', '"+params+"');");

	var section = "",
		page = "",
		id = viewIds;

	switch(viewType) {
		case "CPArtistID":
			section = "artists";
			page = "artist";
			break;
		case "CPAlbumID":
			section = "albums";
			page = "album";
			break;
		case "CPAlbumSubGenreID":
			section = "styles";
			page = "style";
			break;
		case "CPGenreID":
			section = "genres";
			page = "genre";
			break;
		case "CPListID":
			readStartupParams();
			return;
		default: return;
	}

	// Delete startup params
	CookieManager.remove("DMS_startup");

	var url = site_domain+"/switch/?section="+section+"&page="+page+"&mode=notcatalog"+(id?("&id="+id):"");
	dbg("handleOnChangeViewError redirecting to URL: "+url);
	window.external.changeView("OnlineStore", url, "", "");
}

function handleOnChangeViewOnlineListError(error, libraryLocationType, libraryLocationId, params, friendlyName, listType, viewMode) {
	dbg("handleOnChangeViewOnlineListError('"+error+"', '"+libraryLocationType+"', '"+libraryLocationId+"', '"+params+"', '"+friendlyName+"', '"+listType+"', '"+viewMode+"');");

}



/*******************************************
*	VIEW_PARAMETERS
* 	Interface on window.external.ViewParameters
**/
var VIEW_PARAMETERS = {

	_BIG_DELIM:	";",
	_SMALL_DELIM:	":",

	/*******************************************
	*	VIEW_PARAMETERS.get (key)
	*	Parses window.external.ViewParameters for key value, returning null if not found
	**/
	get:	function(k) {
		var hash = this._getHash();
		return hash[k];
	},

	_getHash:	function() {
		try {
			var s = window.external.ViewParameters;
		} catch (e) {
			handleExternalError("VIEW_PARAMETERS", e.message);
		}
		if(!s) {
			return {};
		}
		var a = s.split(this._BIG_DELIM);
		var h = {};
		for(var i=0;i<a.length;i++) {
			var vals = a[i].split(this._SMALL_DELIM);
			h[vals[0]] = vals[1];
		}
		return h;
	}
};


/*
Nav Upsell - Handles clicks from promo modules upselling Free Trial or Subscription. From Adam:
	"
	1) If the user is signed in (as a basic member) then the user would land on the Edit Subscription page with the appropriate messaging.
	2) If the user is not signed in then the user is taken to the create account screen
		a) If user signs in from the create account page and was a subscriber then the user is re-directed back to the page they arrived at
		b) If user signs in from the create account page and was a basic member then the user is directed to the edit subscription page with appropriate messaging
	"

	Solo arg 'product' is one of 'free trial' or 'subscription'
*/

function navUpsell(product,pageName) {

	var referrerString = (CookieManager.exists("DMS_Conversion")?("referrer="+CookieManager.getRawCookie("DMS_Conversion")):"");

	// Logged In?
	if(USER.isLoggedIn()) {

		// Is Basic Member?
		if(!USER.isSubscriber()) {

			//accountNav("promo", "page=trial_logged_in&intcmp="+ pageName +(referrerString?("&"+referrerString):""));
			
			accountNav("edit_subscription", "intcmp="+ pageName +(referrerString?("&"+referrerString):""));


		} else {
			dbg("Error: navUpsell called for logged-in user that's already a subscriber");
			nav("home", "main");
		}

	// Not logged in - Show subscribe promo
	} else {
		//accountNav("promo", "intcmp="+ pageName +"&page=trial"+(referrerString?("&"+referrerString):""));
		
		accountNav("create_account", "intcmp="+ pageName +(referrerString?("&"+referrerString):""));

	}

	if(!navCalledByDeepLink) {
		// Nav came from an actual user 'click', so get rid of any lingering startup params
		CookieManager.remove("DMS_startup");
		CookieManager.remove("DMS_startup_secure");
	}
}

/*
Nav Upsell Third Party - Handles clicks from promo modules upselling third-party promos.

Note that this function calls accountNav with a paramater "promo", which does not correspond
to a page in the authIndex. This is intentional to do a sort of end-run around the auth process
- especially important for deep-linking to a promo.

Argument promoName defines which set of rules to apply
*/

function navUpsellThirdParty(promoName) {

	var pageName = "";

	//need this for any create-account flow
	var tmpReferrerId = CookieManager.getRawCookie("DMS_Conversion");
	var referrerString = "referrer="+tmpReferrerId;

	/**************************** CIRCUIT CITY ********************************/
	if (promoName == "promo_cc") {
		pageName = "promo_cc";
		accountNav("promo", "page="+pageName+(referrerString?("&"+referrerString):""), true, true);
	}


	/**************************** IRIVER ********************************/
	if (promoName == "iriver") {
		// Logged In?
		if(USER.isLoggedIn()) {
			pageName = "promo_iriver_logged_in";
			accountNav("promo", "page="+ pageName +(referrerString?("&"+referrerString):""));

		// Not logged in - Show upsell promo as if new user
		} else {
			pageName = "promo_iriver";
			accountNav("promo", "page="+ pageName +(referrerString?("&"+referrerString):""));
		}

	}

}


	//nav('charts', 'urge', 'genre_track', '1572');

function _navigateLISL (section, _page, _mode, _id, _additionalParams, _wordwheel) {
	dbg("_navigateLISL: section="+section+", _page="+_page+", _mode="+_mode+", _id="+_id);
	var inNowPlayingPane = false;
	if (window.location.href.indexOf('infocenter') != -1) { inNowPlayingPane = true; }

	var page = _page?_page:"main";
	var mode = _mode?_mode:"";
	var id = _id?_id:"";
	var additionalParams = _additionalParams?_additionalParams:"";
	var wordwheel = _wordwheel?_wordwheel:"";

	var linkInfo = SiteConfig.getPageConfig(section, page, mode);

	// Force Hiding Scrollbars
	// removeScrollbars();

	try {
		var linkType = "standard";	// (standard|fullView|onlineList)
		var templateId = linkInfo["template"],
			itemId = id,
			listTypeId = linkInfo["listTypeId"],
			friendlyName = "", itemType = "", viewType = "";

		if(linkInfo["template"]=="OnlineStore") {

			// Do full view nav
			linkType = "fullView";
			itemId = site_domain+"/switch/?section="+section+"&page="+page+(mode?"&mode="+mode:"")+(id?("&id="+id):"");

		} else if(linkInfo["isOnlineList"]) {

			linkType = "onlineList";

			if(linkInfo["title"]) {
				friendlyName = linkInfo["title"];
			} else {
				// Get friendly name by searching for clicked link, and pulling the title att
				var hrefs = document.getElementsByTagName("A");
				for(var i=0;i<hrefs.length;i++) {
					var a = hrefs[i];
					if(a.href.indexOf("nav")>=0 && a.href.indexOf(section)>=0 && a.href.indexOf(page)>=0) {
						if(a.title) {
							friendlyName = a.title;
							break;
						}
					}
				}
			}

			itemType = linkInfo["itemType"];
			viewType = linkInfo["viewType"];

		} else
			linkType = "standard";


		dbg("linkType"+linkType+" templateId"+templateId);
		// If listTypeId set, do bitwise encoding
		if (listTypeId && itemId) {
			var tmp = itemId;
			// if position is not in the recs section, or on the recs pivot pack the id
			// 1 represents recs which is an online list anchored to a custom node
			if (section!='recs' && section!="pages") {
				itemId = packPlaylistId (itemId, listTypeId);
				dbg("packing listTypeId ("+listTypeId+") onto id ("+tmp+") for a combined id of "+itemId);
			}

		} else if(listTypeId) {
			itemId = listTypeId;
			dbg("Found listTypeId "+listTypeId+" and no item id, so using listtypeid as the item id.");

		// Charts & Playlists hack
		} else if(templateId=="CPListID") {
			itemId = (section=="charts")?1:0;
		}

		var viewParams = "";

		// insert references to the parameters of the page from which the navigation is occuring for use in backwards navigation
		//if ((section != "recs") && (_page != "recs")) {
		//	viewParams += "back:" + query.get("section") + ","  + query.get("page") + "," + query.get("mode") + "," + query.get("id") + ";";
		//}

		viewParams += (mode?("mode:"+mode):"") + ((mode && additionalParams)?";":"") + additionalParams;

		if(linkType=="onlineList") {
			dbg("changeViewOnlineList('"+templateId+"', '"+itemId+"', '"+viewParams+"', '"+friendlyName+"', '"+itemType+"', '"+viewType+"')");
			try {
				window.external.changeViewOnlineList (templateId, itemId, viewParams, friendlyName, itemType, viewType);
			} catch (e) {
				handleExternalError("changeViewOnlineList", e.message);
			}
		} else {

			// Attempt normal ChangeView call
			try {
				dbg("issuing: window.external.changeView('"+templateId+"', '"+itemId+"', '"+wordwheel+"', '"+viewParams+"');");
				window.external.changeView(templateId, itemId, wordwheel, viewParams);

			} catch(err) {

				// Failed because we're in Now Playing? (-2146827850 is code for "property not supported" - which should never change)
				if (inNowPlayingPane=true) {
					window.external.NavigateTaskPaneURL('MTVN','ServiceTask1','redirect=true&templateId='+templateId+'&itemId='+itemId+'&wordwheel='+wordwheel+'&viewParams='+viewParams);

				// Probably failed because item isn't in catalog
				} else {

					// Build switch URL to hit item page directly on URGE tab
					itemId = site_domain+"/switch/?section="+section+"&page="+page+"&mode=notcatalog"+(id?("&id="+id):"");

					try {
						dbg('issuing window.external.changeView("OnlineStore", "'+itemId+'", "", "");');
						window.external.changeView("OnlineStore", itemId, "", "");
					} catch(e) {
						dbg('Error issuing window.external.changeView("OnlineStore", "'+itemId+'", "", "");');
					}
				}
			}
		}

	} catch(e) {
		dbg("Error linking to PIVOT_NAMES["+section+"][_"+page+"][_"+mode+"]: "+e.message);
	}

	// Force Show Scrollbars
	// showScrollbars();
}

function getTemplateName(section, page, mode) {
	var templateName = "";
	try {
		var templateName = PIVOT_NAMES[section]["_"+page]["template"];
	} catch(e) {
		templateName = "unknown: "+section+", "+page;
	}
	return templateName;
}


/** puts the playlist type id in the highest order 8 bits of a playlistId
*/
function packPlaylistId(playlistId, typeId) {
	return (parseInt(typeId) << 24) | playlistId;
}

/** extracts the playlistId from an type-encoded id.
*/
function unpackPlaylistId(playlistId, typeId) {
	return (playlistId & ~(typeId << 24));
}

/** pulls the playlist type id from the highest order 8 bits of a
* type-encoded playlistId
*/
function getPlaylistType(playlistId) {
	return playlistId >> 24;
}


/*******************************************
*	externalSnapshot
* 	Used to store a snapshot of current location information, for comparison with new location information in onViewChange handler.
**/
var externalSnapshot = {
	type:			"",
	id:				-1,
	filter:			"",
	selectedType:	"",
	selectedId:		-1,
	mode:			""
};



/******************************************************************************
	DID YOU MEAN
*******************************************************************************/

function requestDidYouMean (searchStr) {
	var path = "/sitewide/xml/droplets/mtvi/didYouMeanDroplet.jhtml?";
	path += "wordwheel="+searchStr;
	var type = "all";
	try {
		switch (window.external.libraryLocationType) {
			case PIVOT_NAMES["albums"]["_all"]["template"]:			type = "album";break;
			case PIVOT_NAMES["albums"]["_album"]["template"]:
			case PIVOT_NAMES["artists"]["_artist"]["template"]:
			case PIVOT_NAMES["tracks"]["_all"]["template"]:			type = "track";break;
			case PIVOT_NAMES["artists"]["_all"]["template"]:		type = "artist";break;
			default:												type = "all";
		}
	} catch(e) {}
	path += "&searchType="+type;
	path += "&searchTermIsArray=false";
	path += "&showAvailOnly="+getShowPurchasableOnlyPref();
	path += "&numSuggestions=4";

	xml_requests.add ("CUSTOM", path).setCustomHandler (displayDidYouMeanData);
}

function displayDidYouMeanData (xml) {

	var didYouMeanHolder = document.getElementById("didYouMeanHolder");
	if(!didYouMeanHolder) {
		didYouMeanHolder = document.createElement("DIV");
		didYouMeanHolder.style.marginBottom = "20px";
		didYouMeanHolder.style.backgroundColor = "black";
		didYouMeanHolder.style.color = "white";
		didYouMeanHolder.id = "didYouMeanHolder";
		document.getElementById("main").insertBefore(didYouMeanHolder, document.getElementById("main").firstChild);
	} else
		didYouMeanHolder.innerHTML = "";

	var suggestions = xml.getElementsByTagName("suggestion");

	if(suggestions.length==0)
		return;

	didYouMeanHolder.innerHTML = "<h2>Did you mean:</h2>";
	var didjaLinks = {};
	for(var i=0;i<suggestions.length;i++) {
		var name = suggestions[i].text;
		var type = xml_getAttribute(suggestions[i], "type");

		if(!name) {
			continue;
		}

		// Construct changeView link
		// Should link to root section containing the item types to search for,
		// and set the filter to auto-drill on the search term
		var lislLink = "nav('"+type+"s', 'all', '', '"+name+"');";

		var link = '<a href="javascript:'+lislLink+'">'+name+"</a>";

		if(!didjaLinks[type]) {
			didjaLinks[type] = new Array();
		}
		didjaLinks[type].push(link);
	}
	var str = "";
	str += "<table><tr>";
	for(var type in didjaLinks) {
		var typedLinks = didjaLinks[type];
		if(typedLinks.length > 0) {
			str += 	"<td><div class='sectionContainer' style='width:200px;'>";
			str += 		"<div class='sectionHeader' style='width:200px;'>";
			str += 			"<div class='sectionTitle' style='cursor:default;'>"+ type.toUpperCase() + "S</div>";
			str += 			"<div class='line' style='width:200px;'><img src='/sitewide/img/spacer.gif' alt='' width='1' height='1' border='0'/></div>";
			str += 		"</div>";
			str += 		"<div class='sectionContent'>" + typedLinks.join(", &nbsp;") + "</div>";
			str += 	"</div></td>";
		}
	}
	str += "</tr></table>";

	didYouMeanHolder.innerHTML += str;
}


/******************************************************************************
	MUSIC MANAGEMENT
*******************************************************************************/

/*******************************************
*	play (type, ids): Play the item given by type and id(s)
**/
function play(type, ids) {
	dbg("jen play:" + type + " " + ids);
	if(typeof type == "undefined") {
		type = "CurrentView";
		ids = "";
		EventLogger.log("play", query.get("section"), query.get("page"), query.get("mode"), query.get("id"),  "CurrentView" , ids, "");

	} else {
		EventLogger.log("play", query.get("section"), query.get("page"), query.get("mode"), query.get("id"),  type , ids, "");
		var resolvedParams = resolveActionParams(type, ids);
		type = resolvedParams[0];
		ids = resolvedParams[1];
		//EventLogger.log("play", "", "", "", "",  type , ids, "");
	}

	dbg("Trying to play('"+type+"', '"+ids+"')");
	try {
		window.external.play(type, ids);
		dbg("no obvious error occurred trying to play('"+type+"', '"+ids+"')");
	} catch(e) {
		handleExternalError("play", "Couldn't play "+type+" "+ids+" ("+e.message+")");
	}
}

/*******************************************
*	refreshFeed (): Tells the plugin to dump the feed cache for the current id, and reloads the grid with new tracks.
**/
function refreshFeed() {

	var viewParams = "";
	var cacheId = "";
	try {
		viewParams = window.external.ViewParameters;
	} catch(e) {
		dbg("error getting viewParams");
	}

	// Cache id is a unique identifier held by Patrick consisting of '{viewParams}:{id}'
	if(viewParams == null || viewParams=="")
	{
		cacheId = "(null):" + query.get("id");
	}
	else
	{
		cacheId = viewParams + ":" + query.get("id");
	}

	dbg('issuing window.external.SendMessage("FlushFeedCache", "'+cacheId+'");');
	try {
		window.external.SendMessage("FlushFeedCache", cacheId);
		dbg('Finished issuing window.external.SendMessage("FlushFeedCache", "'+cacheId+'");');
	} catch(e) {
		dbg('ERROR issuing window.external.SendMessage("FlushFeedCache", "cacheId='+cacheId+');');
	}

	if(query.get("page") != "recs" && query.get("section") != "recs")
	{nav(query.get("section"), "feed", "feed", query.get("id"));}


}





/*******************************************
*	save (name, dynamic): Save the currently displayed list to library, giving it the given name
*	if dynamic is true, the list will update automatically when the feed is updated
* 	e.g. save('Bjork feed', true);
*	e.g. save('Snapshot of tracks in Bjork feed', false);
**/
function save(name, dynamic) {
	dynamic = (typeof dynamic=="undefined")?true:dynamic;
	dbg("Trying to saveCurrentViewToLibrary('"+name+"', '"+dynamic+"')");
	REPORTING.makeCall('save_list', { name : name});
	try {
		//always logging list for now
		if(query.get('section') == "recs")
		{
			document.getElementById('RecsFlash').onload = EventLogger.log("save", query.get('section'), "list", query.get('mode'), query.get('id'),  "" , "", "");
		}
		else
		{
			EventLogger.log("save", query.get('section'), "list", query.get('mode'), query.get('id'),  "" , "", "");
		}
		window.external.saveCurrentViewToLibrary (name, dynamic);
		
	} catch(e) {
		handleExternalError("saveCurrentViewToLibrary", "Couldn't save '"+name+"' with dynamic=="+dynamic);
	}
}

/*******************************************
*	buy (type, ids): Attempt to buy the item given by type and id(s)
**/
function buy(type, ids) {

	var resolvedParams = resolveActionParams(type, ids);
	type = resolvedParams[0];
	ids = resolvedParams[1];

	dbg("Trying to buy('"+type+"', '"+ids+"')");
	//REPORTING.makeCall('purchase_complete', { type : type, id : ids });
	try {
		EventLogger.log("buy", "", "", "", "",  type , ids, "");
		window.external.buy(type, ids);
		dbg("buy('"+type+"', '"+ids+"') seems to have succeeded...");
	} catch(e) {
		handleExternalError("buy", "Couldn't buy "+ids);
		REPORTING.makeCall('error_purchase_fail');
	}
}

/*******************************************
*	showBuyConform (type, ids, button): Begin the confirm-purchase sequence. Sets button
*	to 'confirm' state and activates 'buy' on next click
**/
function showBuyConfirm(type, ids, btn) {
	REPORTING.makeCall('buy_click', { type : type, id : ids });
	//REPORTING.makeCall('buy_click');
	if(!USER.isLoggedIn()) {
		login();
		return;
	}

	try {
		dbg('issuing window.external.SendMessage("EnableWebBuy", "");');
		window.external.SendMessage("EnableWebBuy", "");

	} catch (e) {
		dbg('error issuing window.external.SendMessage("EnableWebBuy", "");');
	}

	for(var i=0;i<btn.childNodes.length;i++) {
		if(btn.childNodes[i].tagName=="SPAN") {
			if(btn.childNodes[i].innerText.toLowerCase().indexOf("confirm") < 0) {
				btn.childNodes[i].innerText = "Confirm";

				btn.onclick = function() {
					REPORTING.makeCall('confirm_click', { type : type, id : ids });
					showBuyDownloading(this);
					buy(type, ids, this);
					this.onclick = null;
				};
			}
		}else if(btn.childNodes[i].tagName=="IMG") {
			iconImg = btn.childNodes[i];
			iconImg.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/confirm.png',sizingMethod='scale')";
		}
	}
	btn.onmouseover = null;
	btn.onmouseout = null;
}

/*******************************************
*	download (type, ids): Attempt to download the item given by type and id(s)
**/
function download(type, ids) {

	var resolvedParams = resolveActionParams(type, ids);
	type = resolvedParams[0];
	ids = resolvedParams[1];

	dbg("Trying to download('"+type+"', '"+ids+"')");
	REPORTING.makeCall('download_initiate', { type : type, id : ids });
	try {
		EventLogger.log("download", query.get('section'), query.get('page'), query.get('mode'), query.get('id'),  type , ids, "");
		window.external.download(type, ids);
		dbg("no obvious error occurred trying to download('"+type+"', '"+ids+"')");
	} catch(e) {
		handleExternalError("download", "Couldn't download "+type+" "+ids+" ("+e.message+")");
		REPORTING.makeCall('download_failure');
	}
}


/*******************************************
	MUSIC MANAGMENT UTILS
********************************************/

/*******************************************
*	showBuyDownloading (button): Sets button status to 'Purchasing...' after user has confirmed purchase
**/
function showBuyDownloading (btn) {

	var textSpan, iconImg, btnAnchor = btn;

	for(var i=0;i<btn.childNodes.length;i++) {

		// TODO: need to figure out a clean way to hide link hover state
		// btn.className = "buttonUnclickable";
		btn.style.cursor = "default";
		if(btn.childNodes[i].tagName=="SPAN") {
			btn.childNodes[i].innerText = "Purchasing...";
			btn.childNodes[i].style.textDecoration = "none";

			textSpan = btn.childNodes[i];

		// Set buy icon to ON state permanently
		} else if(btn.childNodes[i].tagName=="IMG") {

			iconImg = btn.childNodes[i];
		}
	}

	setTimeout(function() {
		textSpan.innerText = "Buy";
		textSpan.style.color = "#999";

		iconImg.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/sitewide/img/content/button/buy_disabled.png',sizingMethod='scale')";

		btnAnchor.className = "button-disabled";

	}, 3000);

	btn.onmouseover = null;
	btn.onmouseout = null;
}

/*******************************************
*	resolveActionParams (type, ids): Used internally by 'action button' handlers to translate item types and encode ids
*		- type is either a listTypeId (int) or a friendly name (see _getCPName)
*		- ids is a single int id or (TK) a ';' delimited list
**/
function resolveActionParams (type, ids) {

	ids = (typeof ids == "undefined")?"":ids;

	// listTypeId sent, must be some playlist variant
	if(isNumeric(type)) {
		tmp = ids;
		ids = packPlaylistId(ids, type);
		dbg("packing "+type+" onto "+tmp+" for a combined of "+ids);

		type = "CPListID";

	// friendly name sent
	} else {

		var packId = false;
		switch(type) {
			case "playlist": packId = 1;break;
			case "artist_template": packId = 9;break;
			case "album_template": packId = 10;break;
			case "50_50_song_template": packId = 11;break;
			case "90_10_song_template": packId = 24;break;
			case "artist_template_scheduled_content": packId = 12;break;
			case "album_template_scheduled_content ": packId = 13;break;
			case "50_50_song_template_scheduled_content": packId = 14;break;
			case "90_10_song_template_scheduled_content": packId = 25;break;
			case "networkplaylist": packId = 2;break;
			case "networkplaylist_archive": packId = 15;break;
			case "blog": packId = 23;break;
			case "blog_archive": packId = 22;break;
			case "franchise": packId = 24;break;
			case "story_50_50": packId = 43;break;
			case "story_90_10": packId = 44;break;
		}
		if(packId) {
			tmp = ids;
			ids = packPlaylistId(ids, packId);
			dbg("packing "+packId+" onto "+tmp+" for a combined of "+ids);
		}
		type = _getCPName(type);

		if(type=="CurrentView") ids = "";
	}

	return [type, ids];
}

/******************************************************************************
	POPUPS
*******************************************************************************/


var FAQ_CMS_IDS = {
	feeds:	10902
}

// TODO: extraParams currently required to be of form 'foo=bar&bar=val' (no leading '&') - should be more flexible.

function showPopup(name, _extraParams) {
	var extraParams = _extraParams?_extraParams:"";
	var index;
	if (index = SiteConfig.getPopupIndex(name)) {
		var params = "f=b&";

		if(name=="faq") {
			// Translate faq section name to cms id
			if(extraParams && FAQ_CMS_IDS[extraParams]) {
				params += "faqQuestionId="+FAQ_CMS_IDS[extraParams];
			} else {
				dbg('added');
				params += "faqQuestionId="+extraParams;
			}
		}

		else
			params += extraParams;


		try {
			dbg("issuing cmd: 'window.external.showPopup ('"+index+"', '"+params+"');");

			//LOG send to a friend
			if(name=="sendToAFriend"){
				EventLogger.log("share", query.get('section'), query.get('page'), query.get('mode'), query.get('id'), "" , "", "");
			}

			window.external.showPopup (index, params);
		} catch (e) {
			handleExternalError("showPopup","Error hitting showPopup with URL index "+index+" and params "+params);
		}

	} else
		dbg("no popup config found for name '"+name+"'");
}

/*******************************************
*	pop (width, height, resize, scrollbars, windowTitle, name, url)
*	Displays popup with passed attributes (All but width, height, and url are currently ignored)
**/
/** Candidate for deletion!!! This doesn't seem to work anymore. **/

function pop (width, height, resize, scrollbars, windowTitle, name, url) {
	var params = "_f=b&DlgX=" + width + "&DlgY=" + height;
	dbg("looking for urlIndex for "+url);
	for(var i in POPUP_URLS) {
		if(POPUP_URLS[i] == url) {
			var urlIndex = i;
			break;
		} else
			dbg("passing over urlIndex "+i+" because "+POPUP_URLS[i]);
	}

	if(WMP_DETECTED) {
		try {
			dbg("issuing cmd: 'window.external.showPopup ('"+urlIndex+"', '"+params+"');");
			window.external.showPopup (urlIndex, params);
		} catch (e) {
			handleExternalError("showPopup","Error hitting showPopup with URL index "+urlIndex+" and params "+params);
		}
	} else {
		var childWin = window.open(url, name, "width="+width+",height="+height+",scrollbars="+scrollbars+",resize="+resize+",windowTitle="+windowTitle);
		return (childWin);
	}
}

function setPopupReturn(val) {
	document.cookie = "popup_return="+encode(val);
}

function getPopupReturn() {
	var ret = "";
	if(document.cookie["popup_return"]) {
		ret = unencode(document.cookie["popup_return"]);
	}
	return ret;
}
/*
function clearPopupReturn() {
	if(document.cookie["popup_return"]) {
		document.cookie["popup_return"] = null;
	}
}*/

/******************************************************************************
	ACCOUNT
*******************************************************************************/



var ACCOUNT_PAGES = {
	create_account:	{
		allow_without_login:	true
	},
	my_account: {
		auth:	false,
		index:	2001
	},
	edit_subscription: {
		auth:	true,
		index:	2002
	},
	pc_management:	{
		auth:	true,
		index: 2003
	},
	sync_restore:	{
		auth:	true,
		index:	2004
	},
	gift_purchase:	{
		auth:	true,
		index:	2005
	},
	gift_redeem:	{
		auth:	true,
		index:	2006
	},
	edit_billing:	{
		auth:	true,
		index:	2007
	},
	forgot_password: {
		auth:	false
	},
	purchase_history: {
		auth:	false
	},
	purchase_history_drilldown: {
		auth:	false
	},
	edit_account: {
		auth:	true,
		index:	2008
	},
	edit_password: {
		auth:	true,
		index:	2009
	},
	trial_promo:	{
		index:	2010
	},
	promo_trial_logged_in:	{
		auth:	false,
		index:	2012
	},
	subscribe_promo:	{
		auth:	true,
		index:	2013
	},
	newsletter_subscribe:	{
		auth:	true,
		index:	2014
	},
	device_management:	{
		auth:	true,
		index: 2015
	},
	promo_iriver_logged_in:	{
		auth:	true,
		index:	2016
	},
	promo_iriver:	{
		auth:	false,
		index:	2017
	},
	promo_cc:	{
		index:	2018
	}
}

/*******************************************
*	accountNav(page)
* 	Main function for navigating around account section
**/
/*******************************************
*	accountNav(page)
* 	Main function for navigating around account section
**/
var _accountNavActivated = false;
function accountNav(page, params, _ignoreExpiredSession, _ignoreLoginState, _cancelNavAfterLogin) {
	
	dbg("accountNav called with user state of " + USER.hasAccountSession());
	var params = (typeof params == "undefined")?"":params;
	var ignoreExpiredSession = (typeof _ignoreExpiredSession == "undefined")?false:_ignoreExpiredSession;
	var ignoreLoginState = (typeof _ignoreLoginState == "undefined")?false:_ignoreLoginState;
	var cancelNavAfterLogin = (typeof _cancelNavAfterLogin == "undefined")?true:_cancelNavAfterLogin;

	dbg("accountNav('"+page+"', '"+params+"', "+ignoreExpiredSession+");");

	var isStorePage = query.get("page")?true:false;
	if (page == "gift_redeem") {
		CookieManager.setCookie("promoFlow", "gift_redeem", 300);
		CookieManager.setCookie("cancelLoginNav","false",5*60);
	}

	// If account nav in progress, return immediately - unless initiated from a store page
	if(false && _accountNavActivated  && !isStorePage)
		return;

	// If we can't determine whether auth is required, just bail
	if (!ACCOUNT_PAGES[page]) {
		dbg("can't find account page: "+page+"; Allowing");

		ignoreExpiredSession = true;
		ignoreLoginState = true;
	}

	// Not logged in?
	if(!ignoreLoginState && !USER.isLoggedIn()) {
		// Clear Redeem flow if page ref is not "gift_redeem"

		if ((CookieManager) && (CookieManager.getRawCookie("promoFlow")=="gift_redeem") && (page != "gift_redeem")) {
			CookieManager.remove("redeemFlow");
		}

		if(typeof ACCOUNT_PAGES[page].allow_without_login!="undefined" && ACCOUNT_PAGES[page].allow_without_login==true) {
			var secureURL = "/secure/account/?section=" + page;
			secureURL += params?("&"+params):"";

			dbg("changeView('OnlineStore', '"+secure_site_domain+secureURL+"', '', '')");

			_accountNavActivated = true;
			window.external.changeView("OnlineStore", secure_site_domain + secureURL, "", "");

		} else {

			// Set up login-change listener to perform nav when they log in.
			var _page = ""+page;
			var _params = ""+params;
			addLoginChangeHandler (function() {
				if(USER.isLoggedIn())
					accountNav(_page, _params);
			}, "accountNav"+_page, cancelNavAfterLogin);

			dbg("setting loginchangehandler from accountNav; cancelNavAfterLogin is "+cancelNavAfterLogin);

			// show login dialog
			login();
		}

	// Logged in but attempting to view a protected page with an expired acct session
	} else if(!ignoreExpiredSession && ACCOUNT_PAGES[page].auth && !USER.hasAccountSession()) {

		// Do window.external.authenticate
		var index = ACCOUNT_PAGES[page].index;
		dbg("window.external.authenticate("+index+")");
		window.external.authenticate(index);

	// otherwise, either page doesn't require auth, or user has active session - just navigate
	} else {
		//Margaret: I don't know why this was here, commenting out to fix deep-linking to acct pages
		/*if(CookieManager.exists("startupNav")) {
			window.location.replace("/switch/?section=home&page=main");
			//CookieManager.remove("startupNav");
		}*/

		var secureURL = "/secure/account/?section=" + page;
		dbg("changeView('OnlineStore', '"+secure_site_domain+secureURL+(params?"&"+params:"")+"', '', '')");

		secureURL += "&forceNav="+(new Date()).getTime();

		_accountNavActivated = true;
		window.external.changeView("OnlineStore", secure_site_domain + secureURL + (params?"&"+params:""), "", "");


	}
}


/*******************************************
*	USER
* 	Wrapper on user-oriented functions
**/
var USER = {

	/*******************************************
	*	USER.isLoggedIn(): Returns true if user is logged in, false otherwise
	**/
	isLoggedIn:		function () {
		try {
			return window.external.userLoggedIn;
		} catch(e) { dbg("USER obj error: userLoggedIn: "+e+": "+e.message); }
		return false;
	},

	/*******************************************
	*	USER.isSubscriber(): Returns true if user is a LOGGED-IN subscriber, false otherwise
	**/
	isSubscriber:	function() {
		try {
			var acctType = window.external.accountType;
			return (acctType > 0);
		} catch(e) { dbg("USER obj error: userLoggedIn: "+e+": "+e.message); }
		return false;
	},

	/*******************************************
	*	USER.hasAccountSession(): Returns true if user is logged in with a fresh 'account session'
	**/
	hasAccountSession:	function() {

		if(!this.isLoggedIn()) {
			return false;

		} else {
			return (typeof ACTIVE_ACCOUNT_SESSION != "undefined")?ACTIVE_ACCOUNT_SESSION:false;
		}
	},

	/*******************************************
	*	USER.hasCachedCreds(): Returns true if user is logged in or on cached creds
	**/
	hasCachedCreds:	function() {
		if(CookieManager.getRawCookie("DMS")) {
			var accountCookieStr = CookieManager.getRawCookie("DMS");
			var map = getNameValueMap(accountCookieStr, "&", "=");

			//dbg (typeof map["RememberMeFlag"] != "undefined") && (map["RememberMeFlag"] == "true");
			// Return !unset
			return (typeof map["RememberMeFlag"] != "undefined") && (map["RememberMeFlag"] == "true");
		}

		return false;
	}
}

/****************************************
	SUBSCRIPTION RIGHTS HANDLING
*****************************************/

// These parameters will be applied to the body XML transform as boolean values determined by
// the result of bitmasking the values given below over the DMS_account 'rights' cookie value.

// For example for DMS_account.rights equal to 10, the parameters available during the body
// XML transform will be:
// 	is_free_trial = false
// 	show_radio = true
//	show_subscription = false
// 	show_janus = true

var RIGHTS_PARAMETERS = {
	is_free_trial:		1,
	show_radio:		2,
	show_subscription:	4,
	show_janus:		8
}


addLoginChangeHandler.anonId = 1;
this._loginChangeHandlers = {};

/*	addLoginChangeHandler
	Manages a hash table of event handlers for the loginChange event.

	Arguments are:
	handler:	the function to queue up for the loginChange event
	name:		a name for the function, so we can find it again if necessary
	navFlag:	flag indicating a user-initiated navigation; sometimes we will want
				to pull these out as a class
*/

function addLoginChangeHandler (handler, name, navFlag) {
	try {
		// backwards compatibility
		if (arguments.length==1) {
			name = "anon" + addLoginChangeHandler.anonId++;;
		}
		if (!name) name = "anon" + addLoginChangeHandler.anonId++;
		if (!navFlag) navFlag = false;
		if (navFlag == "true") { navFlag = true; }
		if (navFlag == "false") { navFlag = false; }
		// assign each handler function a name so we can find it again
		if (!handler.$$name) handler.$$name = name;
		if (!handler.$$navFlag) handler.$$navFlag = navFlag;

		// create a hash table of handlers
		var handlers = this._loginChangeHandlers;
		if (!handlers) {
			handlers = {};
		}
		// store the new handler in the hash table
		handlers[handler.$$name] = handler;
		this._loginChangeHandlers = handlers;
		// assign a global event handler to do all the work
		window.onLoginChange = handleOnLoginChange;
	} catch (e) {
		dbg("error adding loginChangeHandler: " + e.message);
	}
}

function removeLoginChangeHandler (name) {
	// delete the event handler from the hash table
	try {
		if (this._loginChangeHandlers) {
			delete this._loginChangeHandlers[name];
			dbg("removing loginChangeHandler: "+name);
		}
	} catch (e) {
		dbg("error removing loginChangeHandler: "+e.message);
	}
}

function clearLoginNav () {
	try {
		dbg("clearing nav loginChangeHandlers");
		var handlers = this._loginChangeHandlers;
		for(var i in handlers) {
			if(handlers[i]) {
				var _func = handlers[i];
				if (_func.$$navFlag) {
					removeLoginChangeHandler(i);
				}
			}
		}
	} catch (e) {
		dbg("error clearing nav loginChangeHandlers: " + e.message);
	}
}


function handleOnLoginChange () {
	try {
		var cancelCookie = CookieManager.getRawCookie("cancelLoginNav");
		var shouldClearLoginNav = (	(cancelCookie != "") &&
									(cancelCookie != "false")  );
		if(shouldClearLoginNav) {
			clearLoginNav();
			CookieManager.setCookie("cancelLoginNav","false",5*60);
		}

		var handlers = this._loginChangeHandlers;
		for(var i in handl