if( typeof Ad == "undefined" )
{
	/**
	 *	@class			Ad
	 *	@description	Writes a Chum ad to the page (client side)
	 *
	 *	@author			Jason Nussbaum (jason.nussbaum@chumtv.com) / Neil Taylor (neil.taylor@chumtv.com)
	 *	@created		January 18, 2005 / September 15, 2008
	 */
	 
	/**
	 *	@ctor			Ad
	 *
	 *	@param			PageName:String - the page name to send to the ad server
	 *	@param			Keywords:String - any keywords to send to the ad server;
	 *						if keywords passed (and not an empty string), if AdKeywordOverride is set it will be used)
	 *	@param			Placement:String - the placement of the ad on the page
	 *	@param			doNotAllowPageOverride:Boolean - if true, will not use the AdPageNameOverride (default false)
	 *	@param			useUniqueOrdinal:Boolean - if true, generate a unique ordinal for this ad (default false)
	 */
	var Ad = function( pageName, keywords, placement, doNotAllowPageOverride, useUniqueOrdinal )
	{
		if( placement.toLowerCase() == "middle" )
		{
			//do nothing
			return;
		}
		
		this.PageName 	= pageName;
		this.Keywords 	= keywords;
		this.Height 	= 0;
		this.Width 		= 0;
		this.ZoneLayers = -1;
		
		Ad.__instance = this;
		
		if( ! Ad.__OrdinalString )
		{ 
			Ad.__OrdinalString = Ad.GetOrdinalString(); 
		}
		
		// allow the PageName to be overridden
		// by declaring/defining a variable outside of this script
		if( !doNotAllowPageOverride && typeof AdPageNameOverride != "undefined" && AdPageNameOverride != "" )
		{
			this.PageName = AdPageNameOverride;
		}
		
		var keywordOverrideExists = ( typeof AdKeywordOverride != "undefined" && AdKeywordOverride != "" );
		var hasExplicityKeywords = !( typeof keywords == "undefined" || keywords == "" );
		
		if( keywordOverrideExists && !hasExplicityKeywords  )
		{
			this.Keywords = AdKeywordOverride;
		}
		
		if( typeof AdZoneLevelOverride != "undefined" && ( ! isNaN( ("" +AdZoneLevelOverride) ) ) && parseInt( "" + AdZoneLevelOverride ) > -1 )
		{
			this.ZoneLayers = parseInt( "" + AdZoneLevelOverride );
		}
		
		this.ParsePageName();
		
		this.MapPlacementToSize( placement ); //NT 2008/09/02
		
		this.Display();
	}
	
	Ad.GetInstance = function()
	{
		if( ! Ad.__instance )
		{
			Ad.__instance = new Ad();
		}
		
		return Ad.__instance;
	}
	
	Ad.GetOrdinalStringInstance = function()
	{
		if( ! Ad.__OrdinalString )
		{ 
			Ad.__OrdinalString = Ad.GetOrdinalString(); 
		}
		
		return Ad.__OrdinalString;
	}
	
	/**
	 *	@property		Count
	 *	@description	Used in the call to the ad server. Lets the server know how many other ads are on a given page
	 *					Is Dynamic throughout the life of the page
	 */
	Ad.Count = 0;
	/**
	 *	@property		OrdinalString
	 *	@description	Used in the call to the ad server. Essentially for cache busting
	 *					Remains the same for the lifetime of the page. Should not be changed manually
	 */

	Ad.prototype.MapPlacementToSize = function( pPlacement )  //NT 2008/09/02
	{
		switch( pPlacement.toLowerCase() )
		{
			case "top":
				this.Width = 728;
				this.Height = 90;
				
				break;
				
			case "frame1":
				this.Width = 300;
				this.Height = 250;
				
				break;
				
			case "right":
				this.Width = this.PageDoesNotSupportWideSkyscraper()  ?  120 : [ 120, 160 ];
				this.Height= this.PageDoesNotSupportWideSkyscraper()  ?  600 : [ 600, 600 ];
				
				break;

			case "left":
				this.Width = 270;
				this.Height = 60;
				
				break;
				
			default :
				this.Width = -1;
				this.Height = -1;
				
				break;
		}
		
		if( this.Width == -1 && this.Height == -1 )
		{
     		var allPlacements = pPlacement.split( "," );
     		
     		for( i=0; i < allPlacements.length; i++ )
     		{
            var thisPlacement =  allPlacements[ i ].Trim();

            if( thisPlacement.match( /^([0-9]+)x([0-9]+)/i ) ) 
            {
              var sizeMapping = new RegExp( /^([0-9]+)x([0-9]+)/i ).exec( thisPlacement );
              if( sizeMapping.length == 3 )
              {
                  if( this.Width == -1 )
                  {
                    this.Width = new Array();
                    this.Height = new Array();
                  }
                  
                  this.Width.push( sizeMapping[ 1 ] );
                  this.Height.push( sizeMapping[ 2 ] );
              }
            }
     		}
		}	
	}
	
	Ad.prototype.PageDoesNotSupportWideSkyscraper = function()
	{
		return  	this.PageName.indexOf( "ctv.muchmusic.com" ) 	== 0 ||
					this.PageName.indexOf( "ctv.star-tv.com" ) 		== 0 ||
					this.PageName.indexOf( "ctv.bravo.ca" ) 		== 0 ;
	}
	
	Ad.GetOrdinalString = function()
	{
		return Math.ceil( Math.random() * 1000000000 ).toString();
	}
	

	
	/**
	 *	@property		Template
	 *	@description	The html template to write to the page.
	 *					The {0}, {1}, etc get replaced with the relevant tag
	 *						before this is output.
	 */
	
	Ad.prototype.Template = '<!-- begin ad tag --><scr' + 'ipt language="JavaScript" src="http://ad.doubleclick.net/adj/{0};{7}{6}tile={1};{2}sz={3}x{4};ord={5}?" type="tex' + 't/javascript"></sc' + 'ript><no' + 'script><a href="http://ad.doubleclick.net/jump/{0};{7}{6}tile={1};sz={3}x{4};ord={5}?" target="_blank"><img src="http://ad.doubleclick.net/ad/{0};{7}{6}tile={1};sz={3}x{4};ord={4}?" width="{3}" height="{4}" border="0" alt=""></a></no' + 'script><!-- End ad tag -->';

	
		
	/**
	 *	@method			Display
	 *	@description	Writes the ad tag to the page.
	 *					Called by the constructor, so there is no need to call it manually.
	 */
	Ad.prototype.Display = function()
	{
		var adCategoryOverrideExists = ( typeof AdCategoryExclusionOverride != "undefined" && AdCategoryExclusionOverride != "" && AdCategoryExclusionOverride.length > 0 );
		
		var AdCanBeMultipleSizes = ( typeof( this.Width ) == "object" &&  typeof( this.Height ) == "object" && this.Width.length == this.Height.length );
		if( AdCanBeMultipleSizes )
		{
			var firstWidthToUse = this.Width[ 0 ];
			var firstHeightToUse = this.Height[ 0 ];
			
			var additionalHeights = "";
			
			for( i=1; i < this.Width.length; i++ )
			{
				additionalHeights+= "," + this.Width[ i ] + "x" + this.Height[ i ];
			}
			
			this.Width 		= firstWidthToUse;
			this.Height 	= firstHeightToUse + additionalHeights; 
		}
		
		
		var adCode = this.Template.Format( this.PageName, ( ++Ad.Count ), ( Ad.Count == 1 ? "dcopt=ist;" : "" ), this.Width, this.Height, Ad.GetOrdinalStringInstance(), ( adCategoryOverrideExists ? ( "!c=" + AdCategoryExclusionOverride + ";" ) : "" ), ( this.Keywords == "" ? "" : "kw=" + this.Keywords + ";" )  );
		
		document.write( adCode );
	}
	
	/**
	 *	@method			ParsePageName
	 *	@description	Parses the PageName into the format needed by the ad server
	 */
	 
	Ad.prototype.ParsePageName = function()
	{
		if( this.PageName == "" )
			/*{ this.PageName = document.location.toString(); }*/
			{ this.PageName = Ad.GetPageFromUrl(); }
		
		if( this.PageName.indexOf( new RegExp( "(stage(van|tor))" ) ) != -1 )
		{
			this.PageName = this.PageName.replace( "stagevan", "vancouver" );
			this.PageName = this.PageName.replace( "stagetor", "toronto" );
		}
		
		var re = new RegExp( "(http://(stage|freshlive|fresh|test|origin1|origin|temp)\\.)", "i" );
		if( re.test( this.PageName ) )
			{ this.PageName = this.PageName.replace( re, "www." ); }
		
		if( this.PageName.indexOf( "http://" ) == 0 )
			{ this.PageName = this.PageName.replace( "http://", "" ); }	
		
		if( this.PageName.indexOf( "?" ) != -1 )
			{ this.PageName = this.PageName.substring( 0, this.PageName.indexOf( "?" ) ); }
		
		if( this.PageName.lastIndexOf( "." ) > this.PageName.lastIndexOf( "/" ) && this.PageName.lastIndexOf( "/" ) != -1 )
			{ this.PageName = this.PageName.substring( 0, this.PageName.lastIndexOf( "." ) ); }
		
		if( this.PageName.indexOf( "#" ) != -1 )
			{ this.PageName = this.PageName.substring( 0, this.PageName.indexOf( "#" ) ); }
		
		this.MapPageNameToDartSiteAndZone(); //NT 2008/09/02
		
		// Added Feb 16, 2006 by Jason Nussbaum
		// Appends "index" if the Url ends in a slash
		this.PageName = this.PageName.replace( /^(.*)\/$/i, "$1/index" );
	}
	
	Ad.GetPageFromUrl = function( pOnlyFromQueryString ) 
	{
		if( document.location.search.length > 1 )
		{
			var query = document.location.search.substring(1);
			var parms = query.split( "&" );
			for ( i = 0; i < parms.length; i++ ) 
			{
				var pos = parms[i].indexOf('=');
				if ( pos > 0 ) 
				{
					var key = parms[i].substring(0,pos);
					var val = ( "" + parms[i].substring(pos+1) );
					
					if( key.toLowerCase() == "adpagenameoverride" && val.length > 0 )
					{
						return val.toLowerCase();
					}
				}
			}
		}
		
		pOnlyFromQueryString = pOnlyFromQueryString == true;
		
		if(! pOnlyFromQueryString )
		{
			return document.location.toString().toLowerCase();
		}
		else
		{
			return "";
		}
	}
	
	Ad.prototype.MapPageNameToDartSiteAndZone = function() //NT 2008/09/02
	{
		var UrlParts		 	= this.PageName.split( "/" );
		var Domain				= UrlParts[ 0 ];
		var IsPredefinedUrl		= false;
		
		
		//first take care of all the known sites with non www subdomains
		switch( Domain.toLowerCase() )
		{
			case "watch.muchmusic.com":
				this.PageName = "ctv.muchmusicwatch.com";
				IsPredefinedUrl = true;
				break;
				
			case "blog.muchmusic.com":
				this.PageName = "ctv.muchmusicblog.com";
				IsPredefinedUrl = true;
				break;
				
			case "shopmobile.muchmusic.com":
				this.PageName = "ctv.muchmusicmobile.com";
				IsPredefinedUrl = true;
				break;
				
			case "shopmusic.muchmusic.com":
				this.PageName = "ctv.muchmusicmusic.com";
				IsPredefinedUrl = true;
				break;
			
				
		}

		if( IsPredefinedUrl )
		{

			for( i=1; i < UrlParts.length; i++ )
			{
				if( this.ZoneLayers > -1 )
				{
					if( i > this.ZoneLayers )
					{
						break;
					}
				}
				
				this.PageName+= "/" + UrlParts[ i ];
			}
	
			return;
		}
		//else 
			//look at any other url
		

		//first, treat the www subdomain as if there is no subdomain at all...
		if( this.PageName.indexOf( "www." ) == 0 )
		{
			this.PageName 	= this.PageName.replace( "www.", "" );
			
			UrlParts		= this.PageName.split( "/" );
			Domain			= UrlParts[ 0 ];
		}
		
		var DomainParts			= Domain.split( "." );
		var NumberOfSubDomains 	= DomainParts.length - 2;
		
		if( NumberOfSubDomains > -1 )
		{
			this.PageName = "ctv." + DomainParts[ DomainParts.length - 2 ] + "." + DomainParts[ DomainParts.length - 1 ];
			
			for( i=0; i< NumberOfSubDomains; i++ )
			{
				var shouldAddSubDomainToZone = ( i > 0 ) || ( DomainParts[ i ].toLowerCase() != "ctv" );
				
				if( shouldAddSubDomainToZone )
				{
					this.PageName += "/" + DomainParts[ i ];
				}
			}
			
			for( i=1; i < UrlParts.length; i++ )
			{
				if( this.ZoneLayers > -1 )
				{
					if( ( i + NumberOfSubDomains ) > this.ZoneLayers )
					{
						break;
					}
				}
				
				this.PageName += "/" + UrlParts[ i ];
			}

		}
		else //this is a non-standard page name, let's try this :D
		{
			this.PageName = "ctv." + this.PageName;
		}
		
		this.PageName = this.PageName.toLowerCase();
	}
	
	
	if( "".trim == null ) //NT: check if string.Trim already exists
	{
      String.prototype.Trim = function() { return this.replace(/^\s+|\s+$/g, ''); };
	}
	
	if( "".Format == null ) //NT: check if string.Format already exists
	{
		String.prototype.Format = function()
		{
			var str = this;
			
			for(i = 0; i < arguments.length; i++)
			{
				str = str.replace(new RegExp('\\{' + (i) + '\\}','gm'), arguments[i]);
			}
			
			return str;
		}
	}
}

