/* global functions */
/**
 * Global function for actionscript to call.
 * @private
 * @param {Object} theObj The component to search.
 * @param {Object} thePath The component's depth within the CVP.
 * @returns A recursive call.
 * @type Function
 */
function cvpSearchTheClient(theObj, thePath)
{
	if(theObj===null)
		theObj = window;

	var index = thePath.indexOf(".");
	if(index == -1)
	{
		if (thePath.indexOf("(") != -1 && thePath.indexOf(")") != -1)
			return eval("theObj." + thePath);
		else
			return theObj[thePath];
	}

	var objName = thePath.substring(0, index);
	var subPath = thePath.substring(index+1);

	var childObj = theObj[objName];
	if (objName.indexOf("(") != -1 && objName.indexOf(")") != -1)
		childObj = eval("theObj." + objName);

	if(childObj === null)
		return "";

	if(subPath.length < 1)
		return childObj.toString();

	return cvpSearchTheClient(childObj, subPath);
}

(function() {

var debug = true;
if (typeof window.CVP != 'undefined') {
	return;
}

var Class = (function(){var initializing=false, fnTest=/xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; var Class = function(){}; Class.extend = function(prop) { var _super = this.prototype; initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } function Class() { if ( !initializing && this.init ) this.init.apply(this, arguments); } Class.prototype = prototype; Class.constructor = Class; Class.extend = arguments.callee; return Class;}; return Class;})();


var VERSION = "1.6";;
var FLASH_VERSION = "9.0.115.0";

var HTML5 = "html5";
var FLASH = "flash";

var HTML5_JS = "http://i.cdn.turner.com/xslo/cvp/js/cvp.html5/cvp.html5_0.8.min.js";

/* The CVP constructor */
/**
 * Constructs a new CVP object.
 * @class This is the main CVP class.
 * Here is where the player is created and its behaviors are defined.
 * @constructor
 * @param {Object} options The various parameters that define a player.
 * @return The CVP instance.
 * @type CVP
 */
var CVP = window.CVP = function(options) {

	this.options = extend({
		id : 'cvp_player',
		width : '320',
		height : '240',
		flashVars : { },
		playerType : FLASH,
		initialize : function() {}
	}, options || {});

	this.options.embed = extend({
		containerSwf : '',
		expressInstallSwf : 'http://i.cdn.turner.com/v5cache/turnerplayer/flash/expressInstall.swf',
		flashVersion : FLASH_VERSION
	}, this.options.embed || {});

	this.options.embed.options = extend({
		quality : 'high',
        bgcolor : '#000000',
        allowFullScreen : 'true',
        allowScriptAccess : 'always'
	}, this.options.embed.options || {});

	if (!this.options.embed.containerSwf || this.options.embed.containerSwf == '') {
		log('Invalid containerSwf...exiting');
		throw "Invalid containerSwf";
		return;
	}

	this.options.initialize();

	var id = this.options.id,
		width = this.options.width,
		height = this.options.height,
		flashVars = this.options.flashVars,
		embed= this.options.embed
	;

	this._playerType = this.options.playerType;

	var player = null;
	if (this._playerType == HTML5 || (CVP.Browser.apple_mobile))
	{
		if (this.options.flashVars.containerUrl
			&& this.options.flashVars.configUrl)
		{
			log("instantiating the HTML5 player");
			this._playerType = HTML5;
			player = new CVP.Players.HTML5(this.options);
		}
		else
		{
			log("invalid HTML5 params...instantiating null player");
			this._playerType = false;
			player = new CVP.Players.NullPlayer(this.options);
		}

		this.getPlayer = function()
		{
			return player;
		};
	}
	else
	{
		log("instantiating the Flash player");
		this._playerType = FLASH;
	}

	if (CVP.findInstance(id) || byId(id)) {
		log(id + ' is already in use...exiting');
		throw id + ' is already in use';
		return;
	}

	this.getId = function() { return id };
	this.getWidth = function() { return width };
	this.getHeight = function() { return height };
	this.getFlashVars = function() { return flashVars };
	this.getEmbed = function() { return embed };
	this.getPlayerType = function() { return this._playerType };

	if (!createCallbackHandler(id)) {
		log('callback handler for id "' + id + '" could not be created...exiting');
		throw 'callback handler for id "' + id + '" could not be created...exiting';
		return;
	}

	this.callbacks = {};
	delete this.options.initialize;
	for (var p in this.options) {
		if (isFunc(this.options[p])) {
			this.callbacks[p] = this.options[p];
		}
	}

	this.handleCallBack = function() {
		var ret = null;

		if (arguments.length) {
			var funcName = arguments[0];
			var args = Array.prototype.slice.call(arguments, 1);
			log("handleCallback - " + funcName + " args(" + args.length + ")");

			if (typeof this[funcName] != 'undefined'
				&& isFunc(this[funcName])) {
				try {
					log("Found internal CB");
					ret = this[funcName].apply(this, args);
				} catch(e) {
					log("Warning - exception on internal CB " + funcName);
					log("Exception - " + e.message);
				}
			}

			if (this.callbacks[funcName]
				&& isFunc(this.callbacks[funcName])) {
				try {
					log("Found user CB");
					ret = this.callbacks[funcName].apply(this, args);
				} catch(e) {
					log("Warning - exception on user CB " + funcName);
					log("Exception - " + e.message);
				}
			}
		}

		return ret;
	};

	var contentId = this.options.flashVars.contentId || '',
		contentUrl = this.options.flashVars.contentUrl || '',
		playlistId = this.options.flashVars.playlistId || '',
		context = this.options.flashVars.context || '',
		playerInstance = context,

		contentType = '',
		contentWidth = 0,
		contentHeight = 0,

		duration = 0,
		playhead = 0,

		buffering = false,
		bufferProgress = 0,

		paused = false
	;

	this.getPlayerInstance = function() { return playerInstance };
	this.getContentId = function() { return contentId };
	this.getContentUrl = function() { return contentUrl };
	this.playlistId = function() { return playlistId };
	this.getContext = function() { return context };

	this.getContentWidth = function() { return contentWidth };
	this.getContentHeight = function() { return contentHeight };

	this.getDuration = function() { return duration };
	this.getPlayhead = function() { return playhead };

	this.isBuffering = function() { return buffering };
	this.getBufferProgress = function() { return bufferProgress };

	this.isPaused = function() { return paused };


	this.onContentMetadata = function(pContentId, pDuration, pWidth, pHeight) {
		contentId = pContentId;
		playhead = 0;
		duration = pDuration;
		contentWidth = pWidth;
		contentHeight = pHeight;

	};

	this.onContentBegin = function(pContentId) {
		contentId = pContentId;
	};

	this.onContentBuffering = function(pBuffering, pBufferProgress) {
		buffering = pBuffering;
		bufferProgress = pBufferProgress;
	};

	this.onContentPlayHead = function(pContentId, pPlayhead, pTotalDuration) {
		playhead = pPlayhead;
	};

	this.onContentPause = function(pContentId, pPaused) {
		paused = pPaused;
	};

	this.onPlayerReady = function() {
		playerInstance = this.getPlayer().getPlayerInstance();
	};

	CVP.registerInstance(id, this);
	return this;
};

/* The CVP function prototype, representing the API */
CVP.prototype = {

	/**
	 * References the player based on the browser.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	getPlayer : function() {
		if (navigator.appName.indexOf("Microsoft") != -1) {
			return window[this.getId()];
		} else {
			return document[this.getId()];
		}
	},

	/**
	 * Deprecated.  Use embed() instead.
	 */
	embedSWF : function(containerElementId) {
		return this.embed.apply(this, arguments);
	},

	/**
	 * Global function for actionscript to call.
	 * @param {String} containerElementId The container element's id.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	embed : function(containerElementId) {
		if (this._playerType == FLASH)
		{
			var flashvars = this.getFlashVars();
			flashvars.domId = this.getId();
			flashvars.w = this.getWidth();
			flashvars.h = this.getHeight();

			var embed = this.getEmbed();
			var container = embed.containerSwf;
			var params = embed.options;
			var express = embed.expressInstallSwf;
			var version = validateFlashVersion(embed.flashVersion);

			var attributes = {
				id : this.getId(),
				name : this.getId()
			};

			if(!CVP.swfobject.hasFlashPlayerVersion("1.0.0"))
				this.handleCallBack("onNoFlashDetected");

			CVP.swfobject.embedSWF(container, containerElementId, this.getWidth(), this.getHeight(), version, express, flashvars, params, attributes);
		}
		else
		{
			if (this.getPlayer().embed)
			{
				this.getPlayer().embed(containerElementId);
			}
		}
		return this;
	},

	/**
	 * Removes the swfobject from CVP.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	removeSWF : function() {
		if (CVP.swfobject
			&& CVP.swfobject.removeSWF) {
			CVP.swfobject.removeSWF(this.getId());
		}
		return this;
	},

	/**
	 * Play a specified video by id. This call will reset the video queue and will
	 * start playing the video immediately.
	 * @param {String} id The id of the content being played.
	 * @param {Object} options More parameters for playing content.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	play:function(id, options) {
		this.getPlayer().playContent(id, options || {});
		return this;
	},

	/**
	 * Plays a video from the specified URL. The queue will be cleared and the video
	 * will play immediately. The videoId will be set to the passed URL.
	 * @param {String} url The complete URL to the video.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	playVideoFromUrl:function(url) {
		this.getPlayer().playVideoFromUrl(url);
		return this;
	},

	/**
	 * Paused the video playback. Asynchronous call, a callback will be made when
	 * the call completes. See callbacks.
	 * @param {String} id The id of the content being played.
	 * @param {Object} options More parameters for playing content.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	pause:function() {
		this.getPlayer().pause();
		return this;
	},

	/**
	 * Resumes video playback. Asynchronous call, a callback will be made when the
	 * call completes. See callbacks.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	resume:function() {
		this.getPlayer().resume();
		return this;
	},

	/**
	 * Add a video to the play queue. The video will be added to the end of the queue.
	 * If the queue is empty and there is no video playing the video will start playing
	 * immediately.
	 * @param {String} id The id of the content.
	 * @param {Object} options More parameters for playing content.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	queue:function(id, options) {
		this.getPlayer().queue(id, options || {});
		return this;
	},

	/**
	 * Empties the content queue.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	emptyQueue:function() {
		this.getPlayer().emptyQueue();
		return this;
	},

	/**
	 * Seeks the video playhead to a specified position. Asynchronous call, a callback
	 * will be made when the call completes. See callbacks.
	 * @param {float} time The seconds from the beginning of the video to seek to.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	seek:function(time) {
		this.getPlayer().seek(time);
		return this;
	},

	/**
	 * Mutes the video player. The current volume setting will be preserved and restored
	 * when unmute is called. Calling setVolume() will unmute the player. Asynchronous
	 * call, a callback will be made when the call completes. See callbacks.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	mute:function() {
		this.getPlayer().mute();
		return this;
	},

	/**
	 * Un-mutes the video player. The previous volume setting will be restored. Asynchronous
	 * call, a callback will be made when the call completes. See callbacks.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	unmute:function() {
		this.getPlayer().unmute();
		return this;
	},

	/**
	 * Returns the current audio volume from the player.
	 * @param {float} volume A volume setting between 0 and 1.
	 */
	setVolume:function(volume) {
		this.getPlayer().setVolume(volume);
		return this;
	},

	/**
	 * Returns the current audio volume from the player.
	 * @returns The volume is a value between 0 and 1.
	 * @type float
	 */
	getVolume:function() {
		return this.getPlayer().getVolume();
	},

	/**
	 * Checks if the player is muted.
	 * @returns A boolen (true or false) depending if the player is muted or not.
	 * @type Boolean
	 */
	isMuted:function() {
		return this.getPlayer().isMuted();
	},

	/**
	 * Shows the menu.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	showMenu:function() {
		this.getPlayer().showMenu();
		return this;
	},

	/**
	 * Hides the menu.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	hideMenu:function() {
		this.getPlayer().hideMenu();
		return this;
	},

	/**
	 * Squeezes the content area.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	squeeze:function(secs) {
		this.getPlayer().squeeze(secs);
		return this;
	},

	/**
	 * Un-squeezes the content area.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	unsqueeze:function(secs) {
		this.getPlayer().unsqueeze(secs);
		return this;
	},

	/**
	 * This call will return the JSON for a content entry.
	 * @param {String} id The id of the content.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	getContentEntry:function(id) {
		return this.getPlayer().getContentEntry(id);
	},

	/**
	 * Launch the player into full screen mode. The current video will
	 * continue to play from where it was without interruptions. Asynchronous
	 * call, a callback will be made when the call completes. See callbacks.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	goFullScreen:function() {
		this.getPlayer().goFullScreen();
		return this;
	},

	/**
	 * Returns a URL for a companion Ad.
	 * @param {String} size The dimensions of the companion Ad requested. Example: �728x90�.
	 * @returns The url for a companion ad.
	 * @type String
	 */
	getCompanionAd:function(size) {
		return this.getPlayer().getCompanionAd(size);
	},

	/**
	 * Returns the dynamically generated tile id used in calls to DE.
	 * @returns The tile id as a String
	 * @type String
	 */
	getAdId:function() {
		return this.getPlayer().getAdId();
	},

	/**
	 * Returns the ad id associated to the given ad.  The id returned is the one received
	 * from the ad server response.  Please note that currently only DFP sets the ad id.
	 * @returns The ad id as a String
	 * @type String
	 */
	getTileId:function() {
		return this.getPlayer().getTileId();
	},

	/**
	 * Sets the video quality of the player. The quality change will not be applied
	 * to the current video; quality becomes effective from the next video.
	 * @param {String} String with �high� or �low�, representing the video quality
	 * @returns The CVP instance.
	 * @type CVP
	 */
	setContentQuality:function(quality) {
		this.getPlayer().setContentQuality(quality);
		return this;
	},

	/**
	 * Returns the current video quality of the video control.
	 * @returns Returns a string with �high� or �low�, representing the video quality.
	 * @type String
	 */
	getContentQuality:function() {
		return this.getPlayer().getContentQuality();
	},

	/**
	 * Disables the overlaid toolbar from appearing. Useful when the video control is
	 * being driven from Javascript
	 * @returns The CVP instance.
	 * @type CVP
	 */
	disableToolBar:function() {
		this.getPlayer().disableToolBar();
		return this;
	},

	/**
	 * Enable the overlaid toolbar. Please note the toolbar is enabled by default.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	enableToolBar:function() {
		this.getPlayer().enableToolBar();
		return this;
	},

	/**
	 * Enable the next up slate.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	enableNextUpSlate:function() {
		this.getPlayer().enableNextUpSlate();
		return this;
	},

	/**
	 * Disable the next up slate.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	disableNextUpSlate:function() {
		this.getPlayer().disableNextUpSlate();
		return this;
	},

	/**
	 * Writes the logged comments to he debugger.
	 * @param {Object} filters Parameters for filtering the logs.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	startLogging:function(filters) {
		this.getPlayer().startLogging(filters);
		return this;
	},

	/**
	 * Resets the number of videos to play before an ad is shown.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	resetAdFrequency:function() {
		this.getPlayer().resetAdFrequency();
		return this;
	},

	/**
	 * Sets the current ad context's name.
	 * @param {String} strContextName The name of the context to set.
	 * @param {int} bGlobalFrequency The global ad frequency.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	setAdCurrentContext:function(strContextName, bGlobalFrequency) {
		this.getPlayer().setAdCurrentContext(strContextName, bGlobalFrequency);
		return this;
	},

	/**
	 * Sets the current ad section.
	 * @param {String} section The name of the section to set.
	 * @param {int} bGlobalFrequency The global ad frequency.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	setAdSection:function(section) {
		this.getPlayer().setAdSection(section);
		return this;
	},

	/**
	 * Sets the current ad's value based on a passed key.
	 * @param {String} key The key to set.
	 * @param {String} value The value to set the key to.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	setAdKeyValue:function(key, value) {
		this.getPlayer().setAdKeyValue(key, value);
		return this;
	},

	/**
	 * Returns the currently available bitrates.
	 * @param {Object} filter Parameters to further organize the results.
	 * @returns A list of bitrates.
	 * @type Object
	 */
	getAvailableBitrates:function(filter)
	{
		return this.getPlayer().getAvailableBitrates(filter);
	},

	/**
	 * Returns the passed bitrate's label.
	 * @param {String} bitrate Parameters to further organize the results.
	 * @returns The bitrate's string label.
	 * @type String
	 */
	getBitrateLabel:function(bitrateId)
	{
		return this.getPlayer().getBitrateLabel(bitrateId);
	},

	/**
	 * Returns the bitrate's id.
	 * @returns The bitrate's id.
	 * @type String
	 */
	getBitrateId:function()
	{
		return this.getPlayer().getBitrateId();
	},

	/**
	 * Sets the current bitrate's id.
	 * @param {String} bitrateId The id to set.
	 * @returns The bitrate's string label.
	 * @type String
	 */
	setBitrateId:function(bitrateId)
	{
		return this.getPlayer().setBitrateId(bitrateId);
	},

	/**
	 * Sets the current tracking context.
	 * @param {String} value The context value to set.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	setTrackingContext:function( value) {
		this.getPlayer().setTrackingContext(value);
		return this;
	},

	/**
	 * Returns the current context.
	 * @returns The context string.
	 * @type String
	 */
	getTrackingContext:function() {
		return this.getPlayer().getTrackingContext();
	},

	/**
	 * Pause when the next content is available.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	pauseNextUpCountdown:function() {
		this.getPlayer().pauseNextUpCountdown(true);
		return this;
	},

	/**
	 * Resume when the next content is available.
	 * @returns The CVP instance.
	 * @type CVP
	 */
	resumeNextUpCountdown:function() {
		this.getPlayer().pauseNextUpCountdown(false);
		return this;
	},

	/**
	 * Resizes video player area
	 * @returns The CVP instance.
	 * @newWidth The new width of the CVP player
	 * @newHeight The new height of the CVP player
	 */
   resizeVideo: function(newWidth, newHeight) {
      this.getPlayer().resizeVideo(newWidth,newHeight);
      return this;
    }


};

/* Stubs */
CVP.Utils = {};
CVP.Events = {};
CVP.Browser = {};
CVP.Players = {};

/* 'Static' members on the CVP object */
/**
 * The current CVP version.
 * @private
 */
CVP.version = VERSION;

/**
 * An error constant for video not found.
 * @private
 */
CVP.VIDEO_NOT_FOUND_ERROR = "video not found";
/**
 * An error constant for xml not found.
 * @private
 */
CVP.VIDEO_XML_NOT_FOUND_ERROR = "cms error";

/**
 * Instances object.
 * @private
 */
CVP.instances = {};

/**
 * Sets the specified CVP instance.
 * @private
 * @param {String} id The instance's id.
 * @param {Object} instance The instance object to set.
 */
CVP.registerInstance = function(id, instance) {
	CVP.instances[id] = instance;
};

/**
 * Un-registers the specified CVP instance.
 * @private
 * @param {String} id The instance's id.
 */
CVP.unregisterInstance = function(id) {
	CVP.instances[id] = null;
};

/**
 * Returns the specified CVP instance.
 * @private
 * @param {String} id The instance's id.
 * @returns The CVP instance object.
 * @type Object
 */
CVP.findInstance = function(id) {
	return CVP.instances[id];
};

/**
 * Handles a registered callback.
 * @private
 * @param {String} id The callback's id.
 * @param {Object} args Optional parameters for the callback function.
 */
CVP.onCallback = function(id, args) {
	var instance = CVP.findInstance(id);
	if (instance) {
		return instance.handleCallBack.apply(instance, args);
	} else {
		log("Error - onCallback - unable to find instance " + id);
	}
};

/**
 * Sets all instances of CVP to null.
 * @private
 */
CVP.cleanup = function() {
	for (var inst in CVP.instances) {
		window[inst + '_callback_handler'] = null;
		CVP.instances[inst] = null;
	}
};

/* Event impl
 * Note - this should come before utils, as some utilities make use of the custom event class
 */

/**
 * Event handling functionality
 */

/**
 * Will fire an event as soon as the DOM is ready
 * @param handler The function to execute on ready.
 */
CVP.Events.onReady = function(handler)
{
	if (!CVP.swfobject)
		throw new Error("swfobject is required for onReady functionality");
	CVP.swfobject.addDomLoadEvent(handler);
};

CVP.Events.addListener = function(element, type, handler)
{
	if (element.addEventListener)
		element.addEventListener(type, handler, false);
	else
	{
		if (!handler.$$guid) handler.$$guid = CVP.Events.addListener.guid++;
		if (!element.events) element.events = {};
		var handlers = element.events[type];
		if (!handlers)
		{
			handlers = element.events[type] = {};
			if (element['on' + type]) handlers[0] = element['on' + type];
			element['on' + type] = CVP.Events._handleEvent;
		}

		handlers[handler.$$guid] = handler;
	}
};
CVP.Events.addListener.guid = 1;

CVP.Events.removeListener = function(element, type, handler)
{
	if (element.removeEventListener)
		element.removeEventListener(type, handler, false);
	else if (element.events && element.events[type] && handler.$$guid)
		delete element.events[type][handler.$$guid];
};

CVP.Events._handleEvent = function(event)
{
	event = event || CVP.Events._fixEvent(window.event);
	var returnValue = true;
	var handlers = this.events[event.type];

	for (var i in handlers)
	{
		if (!Object.prototype[i])
		{
			this.$$handler = handlers[i];
			if (this.$$handler(event) === false) returnValue = false;
		}
	}

	if (this.$$handler) this.$$handler = null;

	return returnValue;
};

CVP.Events._fixEvent = function(event)
{
	event.preventDefault = CVP.Events._fixEvent._preventDefault;
	event.stopPropagation = CVP.Events._fixEvent._stopPropagation;
	return event;
};
CVP.Events._fixEvent._preventDefault = function()
{
	this.returnValue = false;
};
CVP.Events._fixEvent._stopPropagation = function()
{
	this.cancelBubble = true;
};



CVP.Events.CustomEvent = Class.extend({
	init : function(type) {
		this._type = type;
		this._listeners = [];
	},

	addListener : function(fn, scope) {
		this._listeners.push({ fn : fn, scope : scope });
	},

	removeListener : function(fn, scope) {
		var len = this._listeners.length;
		for (var i = 0; i < len; i++)
		{
			var o = this._listeners[i];
			if (o.fn == fn && o.scope == scope)
			{
				this._listeners.splice(i, 1);
				break;
			}
		}
	},

	dispatch : function() {
		var len = this._listeners.length;
		for (var i = 0; i < len; i++)
		{
			var o = this._listeners[i];
			o.fn.apply(o.scope, arguments);
		}
	}
});

/* CVP Utilities */
/**
 * An object containing miscellaneous utility functions and
 * variables.
 * @class An object containing miscellaneous utility functions
 * and variables.
 */
CVP.Utils = {

	/**
	 * Tests to determine if the obj parameter is undefined.
	 * @param {Object} obj The object to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	undef : function(obj) {
		return obj == undefined;
	},

	/**
	 * Tests to determine if the obj parameter is null.
	 * @param {Object} obj The object to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	isNull : function(obj) {
		return CVP.Utils.undef(obj) || obj == null;
	},

	/**
	 * Tests to determine if the str param is an empty String.
	 * @param {String} str The String to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	empty : function(str) {
		return CVP.Utils.undef(str) || CVP.Utils.isNull(str) || str == "";
	},

	/**
	 * Tests to determine if the parameter's type is 'function'.
	 * @param {Object} f The object to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	isFunc : function(f) {
		return typeof f == 'function';
	},

	/**
	 * Tests to determine if the parameter's type is 'object'.
	 * @param {Object} obj The object to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	isObject : function(obj) {
		return typeof obj == "object";
	},

	/**
	 * Tests to determine if the parameter's type is 'string'.
	 * @param {Object} obj The object to test.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	isString : function(obj) {
		return typeof obj == "string";
	},

	/**
	 * Tests to determine if the passed string flag is active.
	 * The possible true values are "yes", "true", and "on".
	 * All other values will return false.
	 * @param {String} str The string flag.
	 * @param {String} bDefault The flag's default value.
	 * @returns The Boolean result: true or false.
	 * @type Boolean.
	 */
	isFlagActive : function(str, bDefault)
	{
		if (CVP.Utils.empty(str))
			return bDefault;

		switch (str.toLowerCase())
		{
			case "yes":
			case "true":
			case "on":
				return true;
			break;
			default:
				return false;
		}
	},

	/**
	 * extend.
	 * @param {Object} target
	 * @param {Object} source
	 * @returns The target object.
	 * @type Object
	 */
	extend : function (target, source) {
		if (!target) target = {};
		for (var p in source) {
			target[p] = source[p];
		}
		return target;
	},

	/**
	 * Retrieves the object on the page by the passed in id.
	 * @param {String} id The element's unique identifier.
	 * @returns The desired element.
	 * @type Object
	 */
	byId : function(id) {
		return document.getElementById(id);
	},

	/**
	 * Query the page for a specific element.
	 * @param {String} str The query string.
	 * @returns The desired element.
	 * @type Object
	 */
	query : function(str)
	{
		return CVP.Utils.isString(id) ? document.querySelector(str) : str;
	},

	/**
	 * Performs the slice function on the passed array.
	 * @param {Array} arr The array to slice.
	 * @param {int} index The array's index to start the slice.
	 * @returns The resulting array after it has been sliced at the
	 * passed index.
	 * @type Array
	 */
	slice : function(arr, index) {
		return Array.prototype.slice.call(arr, index || 0);
	},

	/**
	 * bind.
	 * @param {Function} func
	 * @param {String} scope
	 * @param {Object} args
	 * @returns
	 * @type
	 */
	bind : function(func, scope, args) {
		var args = CVP.Utils.slice(arguments, 2);
		return function() {
			var a = args.concat(CVP.Utils.slice(arguments));
  			return func.apply(scope, a);
		}
	},

	/**
	 * template.
	 * @param {Object} template
	 * @param {Object} values
	 * @returns The template.
	 * @type Object
	 */
	template : function(template, values)
	{
		var matches = template.match(/{.*?}/g);
		if (matches && matches.length)
		{
			var args = CVP.Utils.slice(arguments, 1),
				isObj = CVP.Utils.isObject(values),
				key,
				len = matches.length;

			for (var i = 0; i < len; i++)
			{
				if (isObj)
				{
					key = matches[i].split("{")[1].split("}")[0];
					if (!values[key])
						continue;
					template = template.replace(matches[i], values[key]);
				}
				else if (i < args.length)
				{
					value = args[i];
					template = template.replace(matches[i], args[i]);
				}
			}
		}
		return template;
	},

	/**
	 * Epoch to Date instance
	 * @param {Number} epoch
	 * @return The Date instance
	 * @type Date
	 */
	epochToDate : function(epoch) {
		if(epoch < 10000000000)
			epoch *= 1000;
		var d = new Date();
		d.setTime(epoch);
		return d;
	},

	/**
	 * Parses the passed object into a string inserting the passed
	 * delimiter string.
	 * @param {Object} obj The object to parse into a string.
	 * @param {String} delimiter The string added to the result as a delimiter.
	 * @returns A string of the parsed object.
	 * @type String
	 */
	joinKeys : function(obj, delimiter)
	{
		if (nil(delimiter)) delimiter = ",";

		var str = "";
		for (var p in obj)
		{
			str += p + "=" + obj[p] + delimiter;
		}
		str = str.substr(0, str.length - 1);
		return str;
	},

	/**
	 * Returns a map of query params
	 *
	 * @param {Str} str The optional query string source
	 * @returns A map of query params
	 * @type Object
	 */
	getQueryParams : function(str) {
	  	var params = {},
	  		source = str || document.location.href;

		try{
			var data = (source.split("?", 2)[1] || "").split("#")[0].split("&") || [];
			for(var i = 0; i< data.length; i++){
				var pair = data[i].split("=");
				if(pair[0])
					params[pair[0]] = unescape(pair[1]);
			}
		}
		catch(e){
			CVP.Utils.log("unable to get url params");
		}

		Utils.getQueryParams = function() { return params; };
		return Utils.getQueryParams();
	},

	/**
	 * Returns a query param value
	 *
	 * @param {Str} key The query param to retrieve
	 * @returns The query param value
	 * @type String
	 */
	getQueryParam : function(key) {
	  	return CVP.Utils.getQueryParams()[key];
	},

	/**
	 * Prints the logs.
	 */
	log : function() {
		if (window.console
			&& window.console.log
			&& debug) {
			window.console.log(CVP.Utils.slice(arguments).join(" | "));
		}
	},

	/**
	 * Prints the logs.
	 * @param {Object} obj
	 * @param {String} str
	 */
	print : function(obj, str) {
		if (str != "nested")
			CVP.Utils.log("\nPrint all values for ", str);

		for (var o in obj)
		{
			if (!o) continue;

			if (CVP.Utils.isObject(obj[o]))
			{
				CVP.Utils.log("Printing nested object value", o);
				CVP.Utils.print(obj[o], "nested");
			}
			else
			{
				CVP.Utils.log(str=="nested" ? "\t" : "", "key:", o, "value:", obj[o]);
			}
		}

		if (str != "nested")
		{
			CVP.Utils.log("End Print all values for ", str);
			CVP.Utils.log("\n");
		}
	}
};

/**
 * The queue containing the CVP's requested commands.
 * @class The queue containing the CVP's requested commands.
 * @extends CVP.Utils
 */
CVP.Utils.CommandQueue = Class.extend({

	/**
	 * Initializes the queue.
	 * @function
	 * @memberOf CVP.Utils.CommandQueue
	 */
	init : function()
	{
		this._queue = [];
	},

	/**
	 * Pushes a new command into the queue.
	 * @param {Function} fn The function to put in the queue.
	 * @param {String} scope The scope of the command.
	 * @param {Object} args Additional, optional parameters.
	 * @memberOf CVP.Utils.CommandQueue
	 */
	push : function(fn, scope, args)
	{
		this._queue.push({ fn : fn, scope : scope, args : args });
	},

	/**
	 * Removes a command from the queue according to the given index.
	 * @param {int} index The index of the queue to remove the command.
	 * @memberOf CVP.Utils.CommandQueue
	 */
	remove : function(index)
	{
		index = CVP.Utils.isNull(index) ? this._queue.length - 1 : index;
		this._queue.splice(index, 1);
	},

	/**
	 * Run the commands in the queue.
	 * @memberOf CVP.Utils.CommandQueue
	 */
	execute : function()
	{
		var len = this._queue.length;
		if (len)
		{
			for (var i = 0; i < len; i++)
			{
				var cmd = this._queue[i];
				cmd.fn.apply(cmd.scope, cmd.args);
			}
		}
	}
});

/**
 * A base class representing an asset to be used by CVP.
 * @class Contains variables and methods pertaining to the management of an
 * assets.
 * @extends CVP.Utils
 */
CVP.Utils.Asset = Class.extend({

	/**
	 * Initializes the asset.
	 * @param {String} url The asset's source url.
	 * @param {String} type The asset's type.
	 * @memberOf CVP.Utils.Asset
	 */
	init : function(url, type)
	{
        this._firedSuccess = false;
		this._url = url;
		this._type = CVP.Utils.isNull(type) ? this._determineType() : type;
		this.id = "cvp_asset_" + Math.round(Math.random() * 1000);

		this.eSuccess = new CVP.Events.CustomEvent();
		this.eFailure = new CVP.Events.CustomEvent();
	},

	/**
	 * Determine's the type of the asset.
	 * @private
	 * @returns The extension of the file.
	 * @type String
	 * @memberOf CVP.Utils.Asset
	 */
	_determineType : function()
	{
		var ext = (CVP.Utils.empty(this._url)) ? "" : this._url.substring(this._url.lastIndexOf(".") + 1);
		return ext;
	},

	/**
	 * Loads the asset.
	 * @memberOf CVP.Utils.Asset
	 */
	load : function()
	{
		log("Asset", "loading type", this._type);
		switch(this._type)
		{
			case "js":
				this._loadJs();
				break;
			case "css":
				this._loadCss();
				break;
			default:
				this._failure();
		}
	},

	/**
	 * This function is called when the asset loads successfullly.
	 * @private
	 * @memberOf CVP.Utils.Asset
	 */
	_success : function()
	{
		log("Asset", "successfully loaded asset", this._url);
		this.eSuccess.dispatch();
        this._firedSuccess = true;
	},

	/**
	 * This function is called when the asset fails to load.
	 * @private
	 * @memberOf CVP.Utils.Asset
	 */
	_failure : function()
	{
		log("Asset", "failed to load asset", this._url);
		this.eFailure.dispatch();
	},

	/**
	 * Loads a javascript asset.
	 * @private
	 * @memberOf CVP.Utils.Asset
	 */
	_loadJs : function()
	{
		var head = document.getElementsByTagName("head")[0];
		if (!head)
		{
			this._failure();
			return;
		}

		var script = document.createElement('script');
		script.id = this.id;
		script.type = 'text/javascript';

        var success = CVP.Utils.bind(this._success, this);
        var successCB = function() {
        	if (this._firedSuccess)
            	return;

        	success();
        	script.onload = script.onreadystatechange = null;
        	head.removeChild(script);
        };

		script.onload = successCB;
		script.onerror = CVP.Utils.bind(this._failure, this);
        script.onreadystatechange= function ()
        {
            if (this.readyState == 'loaded' || this.readyState == 'complete')
            {
               successCB();
            }
        };

		script.src = this._url;
		head.appendChild(script);

	},

	/**
	 * Loads a css asset.
	 * @private
	 * @memberOf CVP.Utils.Asset
	 */
	_loadCss : function()
	{
		var head = document.getElementsByTagName("head")[0];
		if (!head)
		{
			this._failure();
			return;
		}

		var node = document.createElement('link');
		node.type = 'text/css';
		node.rel = 'stylesheet';
		node.href = src;
		node.media = 'screen';
		head.appendChild(node);

		this._success();
	}
});

/* 'Private' Utils */

/**
 * Add the global callback handler for a particular instance of CVP.
 * @private
 * @param {String} id The callback id.
 * @returns True or false based on the success of the creation of
 * the callback.
 * @type Boolean
 */
function createCallbackHandler(id) {
	var funcName = id + '_callback_handler';
	if (typeof window[funcName] != 'undefined')
		return false;

	window[funcName] = function() {
		var ret = CVP.onCallback(id, arguments);
		if (typeof ret != 'undefined') {
			return ret;
		}
	};
	return true;
}

/**
 * Validate the embed flash version against our min version.
 * @private
 * @param {String} embedVersion The embed version's name.
 * @returns The correct flash version name.
 * @type String
 */
function validateFlashVersion(embedVersion)
{
	if (embedVersion === FLASH_VERSION)
		return embedVersion;

	if (typeof embedVersion == "undefined" || embedVersion == null)
		return FLASH_VERSION;

	FLASH_VERSION += "";
	embedVersion += "";

	var f1 = FLASH_VERSION.split(".");
		f2 = embedVersion.split("."),
		f1V = 0,
		f2V = 0
	;

	for (var i = 0; i < f1.length; i++)
	{
		f1V = f1[i] * 1;
		f2V = f2[i] * 1;

		if (isNaN(f2V) ||
			f1V > f2V)
			return FLASH_VERSION;
		else if (f2V > f1V)
			return embedVersion;
	}
	return FLASH_VERSION;
}

/**
 * Adds before unloading.
 * @private
 * @param {Function} func The function to add.
 */
function addBeforeUnLoadEvent(func)
{
	var oldfunc = window.onbeforeunload;
	if (typeof window.onbeforeunload != 'function')
	{
		window.onbeforeunload = func;
	}
	else
	{
		window.onbeforeunload = function()
		{
			if (oldfunc)
			{
				oldfunc();
			}
			func();
		}
	}
}

/* Aliases */
var byId = CVP.Utils.byId;
var extend = CVP.Utils.extend;
var isFunc = CVP.Utils.isFunc;
var bind = CVP.Utils.bind;
var log = CVP.Utils.log;

/**
 * Class object.
 * @private
 */
CVP.Class = Class;

/* Converters */
CVP.Utils.JsonConverter = {
	escapeString : function(str)
	{
		var s = "";
		var ch;

		if (!CVP.Utils.empty(str))
		{
			var len = str.length;

			for (var i = 0; i < len; i++)
			{

				ch = str.charAt(i);

				switch (ch)
				{
					case '"': // quotation mark
						s += "\\\\\"";
						break;


					case '\\': // reverse solidus
						s += "\\\\";
						break;

					case '\b': // bell
						s += "\\b";
						break;

					case '\f': // form feed
						s += "\\f";
						break;

					case '\n': // newline
						s += "\\n";
						break;

					case '\r': // carriage return
						s += "\\r";
						break;

					case '\t': // horizontal tab
						s += "\\t";
						break;

					default: // everything else

						if (ch < ' ')
						{
							var hexCode = ch.charCodeAt(0).toString(16);

							var zeroPad = hexCode.length == 2 ? "00" : "000";

							s += "\\u" + zeroPad + hexCode;
						}
						else
						{

							s += ch;

						}
				} // end switch

			} // end for loop
		return "\"" + s + "\"";
		}
	},

	convertNode : function(xml, ident, nodeType)
	{
		if (CVP.Utils.empty(ident))
			ident = ""; //optional param

		if (CVP.Utils.empty(nodeType))
			nodeType = ""; //optional param

		var json = ident;

		if (nodeType == "")
			json += "{";

		ident = ident + "\t";
		var first = true;

		if (!CVP.Utils.undef(xml.attributes))
		{
			var removeAttributes = new Array();
			for (var i = 0; i < xml.attributes.length; i++)
			{
				var attribute = xml.attributes[i];

				if (!CVP.Utils.undef(attribute.nodeName) && !(attribute.nodeName.indexOf('xmlns') >= 0))
				{
					if (first)
					{
						first = false;
						json += "\n";
					}
					else
					{
						json += ",\n";
					}

					if (nodeType == 'array')
						json += ident + "{";
					else
						json += ident;

					json += "\"" + attribute.nodeName + "\":" + CVP.Utils.JsonConverter.escapeString(attribute.nodeValue);

					if (nodeType == 'array')
						json += "}";
				} else {
					if (!CVP.Utils.undef(attribute.nodeName)) {
						removeAttributes.push(attribute.nodeName);
					}
				}
			}
			if (removeAttributes.length > 0) {
				for (var i = 0; i < removeAttributes.length; i++) {
					xml.removeAttribute(removeAttributes[0]);
				}
			}
		}

		var type;
		var name;

		if (!CVP.Utils.undef(xml.childNodes))
		{
			for (var i = 0; i < xml.childNodes.length; i++)
			{
				var node = xml.childNodes[i];

				if (!CVP.Utils.undef(node.tagName))
				{
					if (first)
					{
						first = false;
						json += "\n";
					}
					else
					{
						json += ",\n";
					}

					name = node.tagName;

					if (CVP.Utils.empty(name))
					{
						name = "text";
					}

					/**
					 * If it's an array, the output should look like this :
					 * prop : [
					 * 		{ entry1 : { prop1 : hi } },
					 * 		{ entry2 : { prop2 : bye } },
					 * ]
					 */

					json += ident;

					if (nodeType == 'array')
						json += "{";

					if ((CVP.Utils.empty(node.childNodes) || node.childNodes.length <= 1) &&
						(CVP.Utils.empty(node.attributes) || node.attributes.length == 0) &&
						!(node.childNodes.length > 0 && node.childNodes[0].nodeType == 1))
					{
						if (node.childNodes.length > 0)
							json += "\"" + name + "\":" + CVP.Utils.JsonConverter.escapeString(node.childNodes[0].nodeValue);
						else
							json += "\"" + name + "\":" + "\"\"";
					}
					else
					{
						var isArray = CVP.Utils.JsonConverter.isNodeAnArray(node);
						json += "\"" + name + "\":" + (isArray ? "[" : "{") + "\n";

						if (node.childNodes.length > 0 && !CVP.Utils.empty(node.childNodes[0].nodeValue))
							json += "\"text\":" + CVP.Utils.JsonConverter.escapeString(node.childNodes[0].nodeValue) + ",";

						type = isArray ? 'array' : 'object';
						json += CVP.Utils.JsonConverter.convertNode(node, ident, type);
					}

					if (nodeType == 'array')
						json += "}";
				}
			}
		}
		ident = ident.substr(0, ident.length - 1);
		json += "\n" + ident;
		json += (nodeType == 'array') ? "]" : "}";
		return json;
	},

	/**
	 * Detect if an XML node should be converted to a
	 * JSON array
	 *
	 * This assumes that if two or more child nodes of the
	 * passed in node have the same name, than the node
	 * should be treated as an array
	 *
	 * Example : <files> would be treated as an array
	 *  <files>
	 *  <file bitrate="150" type="hp">...</file>
	 *  <file bitrate="300" type="standard">...</file>
	 *  <file bitrate="600" type="hd">...</file>
	 *  </files>
	 */
	isNodeAnArray : function(node)
	{
		var names = {};

		if (!CVP.Utils.undef(node.childNodes))
		{
			for (var i = 0; i < node.childNodes.length; i++)
			{
				var element = node.childNodes[i];

				if (!CVP.Utils.empty(element.tagName))
				{
					if (!CVP.Utils.empty(names[element.tagName]))
						return true;
					else
						names[element.tagName] = "exists";
				}
			}
		}
		return false;
	},

	encodeXmlObject : function(xmlObj)
	{
		var json = CVP.Utils.JsonConverter.convertNode(xmlObj);
		return json;
	}
};

/* Browser detection */
/**
 * A function to detect the current browser.
 * @private
 * @returns An object with browser info.
 * @type Object
 */
CVP.Browser = (function() {
	var ua = navigator.userAgent;

	var chrome = !!ua.match(/chrome/i);

	var iphone = !!ua.match(/iPhone/i);
	var ipod = !!ua.match(/iPod/i);
	var ipad = !!ua.match(/iPad/i);

	var apple_mobile = (iphone || ipod || ipad);

	return {
		chrome : chrome,

		iphone : iphone,
		ipod : ipod,
		ipad : ipad,
		apple_mobile : apple_mobile
	};
})();

/* Player impls */
/**
 * The HTML5 player.
 * @private
 */
CVP.HTML5 = HTML5;
/**
 * The Flash player.
 * @private
 */
CVP.FLASH = FLASH;

CVP.Players.NullPlayer = Class.extend({
	init : function()
	{
		var functions = "play,playVideoFromUrl,pause,resume,queue,emptyQueue,seek,mute,unmute,setVolume,getVolume,isMuted,showMenu,hideMenu,squeeze,unsqueeze,getContentEntry,goFullScreen,getCompanionAd,getAdId,getTileId,setContentQuality,getContentQuality,disableToolBar,enableToolBar,enableNextUpSlate,disableNextUpSlate,startLogging,resetAdFrequency,setAdCurrentContext,setAdSection,setAdKeyValue,getAvailableBitrates,getBitrateLabel,getBitrateId,setBitrateId,setTrackingContext,getTrackingContext,pauseNextUpCountdown";
		var functions = functions.split(",");
		for (var f in functions)
		{
			if (!this[functions[f]])
				this[functions[f]] = function() {};
		}
	}
});

/**
 * Wrapper around the HTML5 Player instance
 */
CVP.Players.HTML5 = CVP.Players.NullPlayer.extend({
	init : function(options)
	{
		this._super();

		this._options = options;
		this._instance = null;

		this._loadQ = new CVP.Utils.CommandQueue();
		this._loaded = false;


		this._load();
	},

	_load : function()
	{
		if (CVP.Players._HTML5Player)
		{
			this._onLoaded();
		}
		else
		{
			var asset = new CVP.Utils.Asset(this._options.flashVars.html5_js_url || HTML5_JS);
			asset.eSuccess.addListener(bind(this._onLoaded, this));
			asset.eFailure.addListener(bind(this._onLoadError, this));
			asset.load();
		}
	},

	_onLoaded : function()
	{
		if (CVP.Players._HTML5Player)
		{
			this._instance = new CVP.Players._HTML5Player(this._options);
			this._instance.ePlayerLoaded.addListener(this._onPlayerLoaded, this);
			this._instance.ePlayerReady.addListener(this._onPlayerReady, this);

			this._instance.ePlayerReady.addListener(bind(this._onCallBack, this, 'onPlayerReady'));
			this._instance.eContentBegin.addListener(bind(this._onCallBack, this, 'onContentBegin'));
			this._instance.eContentPlay.addListener(bind(this._onCallBack, this, 'onContentPlay'));
			this._instance.eContentCompleted.addListener(bind(this._onCallBack, this, 'onContentCompleted'));

			this._instance.eAdStarted.addListener(bind(this._onCallBack, this, 'onAdStarted'));
			this._instance.eAdFinished.addListener(bind(this._onCallBack, this, 'onAdFinished'));
			this._instance.eAdError.addListener(bind(this._onCallBack, this, 'onAdError'));
		}
		else
		{
			log("HTML5 player not found");
		}
	},

	_onLoadError : function()
	{
		log("HTML5 load error");
	},

	_onPlayerLoaded : function()
	{
		log("_onPlayerLoaded");
		this._loaded = true;
		this._loadQ.execute();
	},

	_onPlayerReady : function()
	{
		log("_onPlayerReady");
	},

	embed : function(containerElement)
	{
		if (!this._loaded)
		{
			log("queuing embed");
			this._loadQ.push(this.embed, this, arguments);
			return;
		}
		log("executing embed");
		this._instance.render(containerElement);
	},

	playContent : function(contentId, options)
	{
		this._instance.play(contentId, options);
	},

	queue : function(contentId, options)
	{
		this._instance.queue(contentId, options);
	},

	pause : function()
	{
		this._instance.pause();
	},

	resume : function()
	{
		this._instance.resume();
	},

	setVolume : function(v)
	{
		this._instance.setVolume(v);
	},

	getVolume : function()
	{
		return this._instance.getVolume();
	},

	mute : function()
	{
		this._instance.mute();
	},

	unmute : function()
	{
		this._instance.unmute();
	},

	getContentEntry:function(id)
	{
		return this._instance.getContentEntry(id);
	},

	_onCallBack : function()
	{
		log("HTML5", arguments[0]);
		CVP.onCallback(this._options.id, arguments);
	},

	instance : function()
	{
		return this._instance;
	},

	supported : function()
	{
		return !!document.createElement('video').canPlayType;
	}
});




/* Keep swfobject at the bottom */
/**
 * SWFObject v2.2 <http://code.google.com/p/swfobject/>
 * is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
 * @private
 */
CVP.swfobject = function() {

	var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",
		ON_READY_STATE_CHANGE = "onreadystatechange",

		win = window,
		doc = document,
		nav = navigator,

		plugin = false,
		domLoadFnArr = [main],
		regObjArr = [],
		objIdArr = [],
		listenersArr = [],
		storedAltContent,
		storedAltContentId,
		storedCallbackFn,
		storedCallbackObj,
		isDomLoaded = false,
		isExpressInstallActive = false,
		dynamicStylesheet,
		dynamicStylesheetMedia,
		autoHideShow = true,

	/* Centralized function for browser feature detection
		- User agent string detection is only used when no good alternative is possible
		- Is executed directly for optimal performance
	*/
	ua = function() {
		var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
			u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			windows = p ? /win/.test(p) : /win/.test(u),
			mac = p ? /mac/.test(p) : /mac/.test(u),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
			playerVersion = [0,0,0],
			d = null;
		if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
			d = nav.plugins[SHOCKWAVE_FLASH].description;
			if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
				plugin = true;
				ie = false; // cascaded feature detection for Internet Explorer
				d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
				playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
				playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
				playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			try {
				var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
				if (a) { // a will return null when ActiveX is disabled
					d = a.GetVariable("$version");
					if (d) {
						ie = true; // cascaded feature detection for Internet Explorer
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
			}
			catch(e) {}
		}
		return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
	}(),

	/* Cross-browser onDomLoad
		- Will fire an event as soon as the DOM of a web page is loaded
		- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
		- Regular onload serves as fallback
	*/
	onDomLoad = function() {
		if (!ua.w3) { return; }
		if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
			callDomLoadFunctions();
		}
		if (!isDomLoaded) {
			if (typeof doc.addEventListener != UNDEF) {
				doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
			}
			if (ua.ie && ua.win) {
				doc.attachEvent(ON_READY_STATE_CHANGE, function() {
					if (doc.readyState == "complete") {
						doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
						callDomLoadFunctions();
					}
				});
				if (win == top) { // if not inside an iframe
					(function(){
						if (isDomLoaded) { return; }
						try {
							doc.documentElement.doScroll("left");
						}
						catch(e) {
							setTimeout(arguments.callee, 0);
							return;
						}
						callDomLoadFunctions();
					})();
				}
			}
			if (ua.wk) {
				(function(){
					if (isDomLoaded) { return; }
					if (!/loaded|complete/.test(doc.readyState)) {
						setTimeout(arguments.callee, 0);
						return;
					}
					callDomLoadFunctions();
				})();
			}
			addLoadEvent(callDomLoadFunctions);
		}
	}();

	function callDomLoadFunctions() {
		if (isDomLoaded) { return; }
		try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
			var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
			t.parentNode.removeChild(t);
		}
		catch (e) { return; }
		isDomLoaded = true;
		var dl = domLoadFnArr.length;
		for (var i = 0; i < dl; i++) {
			domLoadFnArr[i]();
		}
	}

	function addDomLoadEvent(fn) {
		if (isDomLoaded) {
			fn();
		}
		else {
			domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
		}
	}

	/* Cross-browser onload
		- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
		- Will fire an event as soon as a web page including all of its assets are loaded
	 */
	function addLoadEvent(fn) {
		if (typeof win.addEventListener != UNDEF) {
			win.addEventListener("load", fn, false);
		}
		else if (typeof doc.addEventListener != UNDEF) {
			doc.addEventListener("load", fn, false);
		}
		else if (typeof win.attachEvent != UNDEF) {
			addListener(win, "onload", fn);
		}
		else if (typeof win.onload == "function") {
			var fnOld = win.onload;
			win.onload = function() {
				fnOld();
				fn();
			};
		}
		else {
			win.onload = fn;
		}
	}

	/* Main function
		- Will preferably execute onDomLoad, otherwise onload (as a fallback)
	*/
	function main() {
		if (plugin) {
			testPlayerVersion();
		}
		else {
			matchVersions();
		}
	}

	/* Detect the Flash Player version for non-Internet Explorer browsers
		- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
		  a. Both release and build numbers can be detected
		  b. Avoid wrong descriptions by corrupt installers provided by Adobe
		  c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
		- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
	*/
	function testPlayerVersion() {
		var b = doc.getElementsByTagName("body")[0];
		var o = createElement(OBJECT);
		o.setAttribute("type", FLASH_MIME_TYPE);
		var t = b.appendChild(o);
		if (t) {
			var counter = 0;
			(function(){
				if (typeof t.GetVariable != UNDEF) {
					var d = t.GetVariable("$version");
					if (d) {
						d = d.split(" ")[1].split(",");
						ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				else if (counter < 10) {
					counter++;
					setTimeout(arguments.callee, 10);
					return;
				}
				b.removeChild(o);
				t = null;
				matchVersions();
			})();
		}
		else {
			matchVersions();
		}
	}

	/* Perform Flash Player and SWF version matching; static publishing only
	*/
	function matchVersions() {
		var rl = regObjArr.length;
		if (rl > 0) {
			for (var i = 0; i < rl; i++) { // for each registered object element
				var id = regObjArr[i].id;
				var cb = regObjArr[i].callbackFn;
				var cbObj = {success:false, id:id};
				if (ua.pv[0] > 0) {
					var obj = getElementById(id);
					if (obj) {
						if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
							setVisibility(id, true);
							if (cb) {
								cbObj.success = true;
								cbObj.ref = getObjectById(id);
								cb(cbObj);
							}
						}
						else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
							var att = {};
							att.data = regObjArr[i].expressInstall;
							att.width = obj.getAttribute("width") || "0";
							att.height = obj.getAttribute("height") || "0";
							if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
							if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
							var par = {};
							var p = obj.getElementsByTagName("param");
							var pl = p.length;
							for (var j = 0; j < pl; j++) {
								if (p[j].getAttribute("name").toLowerCase() != "movie") {
									par[p[j].getAttribute("name")] = p[j].getAttribute("value");
								}
							}
							showExpressInstall(att, par, id, cb);
						}
						else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
							displayAltContent(obj);
							if (cb) { cb(cbObj); }
						}
					}
				}
				else {	// if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
					setVisibility(id, true);
					if (cb) {
						var o = getObjectById(id); // test whether there is an HTML object element or not
						if (o && typeof o.SetVariable != UNDEF) {
							cbObj.success = true;
							cbObj.ref = o;
						}
						cb(cbObj);
					}
				}
			}
		}
	}

	function getObjectById(objectIdStr) {
		var r = null;
		var o = getElementById(objectIdStr);
		if (o && o.nodeName == "OBJECT") {
			if (typeof o.SetVariable != UNDEF) {
				r = o;
			}
			else {
				var n = o.getElementsByTagName(OBJECT)[0];
				if (n) {
					r = n;
				}
			}
		}
		return r;
	}

	/* Requirements for Adobe Express Install
		- only one instance can be active at a time
		- fp 6.0.65 or higher
		- Win/Mac OS only
		- no Webkit engines older than version 312
	*/
	function canExpressInstall() {
		return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
	}

	/* Show the Adobe Express Install dialog
		- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
	*/
	function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
		isExpressInstallActive = true;
		storedCallbackFn = callbackFn || null;
		storedCallbackObj = {success:false, id:replaceElemIdStr};
		var obj = getElementById(replaceElemIdStr);
		if (obj) {
			if (obj.nodeName == "OBJECT") { // static publishing
				storedAltContent = abstractAltContent(obj);
				storedAltContentId = null;
			}
			else { // dynamic publishing
				storedAltContent = obj;
				storedAltContentId = replaceElemIdStr;
			}
			att.id = EXPRESS_INSTALL_ID;
			if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
			if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
			doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
			var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
			if (typeof par.flashvars != UNDEF) {
				par.flashvars += "&" + fv;
			}
			else {
				par.flashvars = fv;
			}
			if (ua.ie && ua.win && obj.readyState != 4) {
				var newObj = createElement("div");
				replaceElemIdStr += "SWFObjectNew";
				newObj.setAttribute("id", replaceElemIdStr);
				obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						obj.parentNode.removeChild(obj);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			createSWF(att, par, replaceElemIdStr);
		}
	}

	/* Functions to abstract and display alternative content
	*/
	function displayAltContent(obj) {
		if (ua.ie && ua.win && obj.readyState != 4) {
			var el = createElement("div");
			obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
			el.parentNode.replaceChild(abstractAltContent(obj), el);
			obj.style.display = "none";
			(function(){
				if (obj.readyState == 4) {
					obj.parentNode.removeChild(obj);
				}
				else {
					setTimeout(arguments.callee, 10);
				}
			})();
		}
		else {
			obj.parentNode.replaceChild(abstractAltContent(obj), obj);
		}
	}

	function abstractAltContent(obj) {
		var ac = createElement("div");
		if (ua.win && ua.ie) {
			ac.innerHTML = obj.innerHTML;
		}
		else {
			var nestedObj = obj.getElementsByTagName(OBJECT)[0];
			if (nestedObj) {
				var c = nestedObj.childNodes;
				if (c) {
					var cl = c.length;
					for (var i = 0; i < cl; i++) {
						if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
							ac.appendChild(c[i].cloneNode(true));
						}
					}
				}
			}
		}
		return ac;
	}

	/* Cross-browser dynamic SWF creation
	*/
	function createSWF(attObj, parObj, id) {
		var r, el = getElementById(id);
		if (ua.wk && ua.wk < 312) { return r; }
		if (el) {
			if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
				attObj.id = id;
			}
			if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
				var att = "";
				for (var i in attObj) {
					if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
						if (i.toLowerCase() == "data") {
							parObj.movie = attObj[i];
						}
						else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							att += ' class="' + attObj[i] + '"';
						}
						else if (i.toLowerCase() != "classid") {
							att += ' ' + i + '="' + attObj[i] + '"';
						}
					}
				}
				var par = "";
				for (var j in parObj) {
					if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
						par += '<param name="' + j + '" value="' + parObj[j] + '" />';
					}
				}
				el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
				objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
				r = getElementById(attObj.id);
			}
			else { // well-behaving browsers
				var o = createElement(OBJECT);
				o.setAttribute("type", FLASH_MIME_TYPE);
				for (var m in attObj) {
					if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
						if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
							o.setAttribute("class", attObj[m]);
						}
						else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
							o.setAttribute(m, attObj[m]);
						}
					}
				}
				for (var n in parObj) {
					if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
						createObjParam(o, n, parObj[n]);
					}
				}
				el.parentNode.replaceChild(o, el);
				r = o;
			}
		}
		return r;
	}

	function createObjParam(el, pName, pValue) {
		var p = createElement("param");
		p.setAttribute("name", pName);
		p.setAttribute("value", pValue);
		el.appendChild(p);
	}

	/* Cross-browser SWF removal
		- Especially needed to safely and completely remove a SWF in Internet Explorer
	*/
	function removeSWF(id) {
		var obj = getElementById(id);
		if (obj && obj.nodeName == "OBJECT") {
			if (ua.ie && ua.win) {
				obj.style.display = "none";
				(function(){
					if (obj.readyState == 4) {
						removeObjectInIE(id);
					}
					else {
						setTimeout(arguments.callee, 10);
					}
				})();
			}
			else {
				obj.parentNode.removeChild(obj);
			}
		}
	}

	function removeObjectInIE(id) {
		var obj = getElementById(id);
		if (obj) {
			for (var i in obj) {
				if (typeof obj[i] == "function") {
					obj[i] = null;
				}
			}
			obj.parentNode.removeChild(obj);
		}
	}

	/* Functions to optimize JavaScript compression
	*/
	function getElementById(id) {
		var el = null;
		try {
			el = doc.getElementById(id);
		}
		catch (e) {}
		return el;
	}

	function createElement(el) {
		return doc.createElement(el);
	}

	/* Updated attachEvent function for Internet Explorer
		- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
	*/
	function addListener(target, eventType, fn) {
		target.attachEvent(eventType, fn);
		listenersArr[listenersArr.length] = [target, eventType, fn];
	}

	/* Flash Player and SWF content version matching
	*/
	function hasPlayerVersion(rv) {
		var pv = ua.pv, v = rv.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
		v[2] = parseInt(v[2], 10) || 0;
		return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
	}

	/* Cross-browser dynamic CSS creation
		- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
	*/
	function createCSS(sel, decl, media, newStyle) {
		if (ua.ie && ua.mac) { return; }
		var h = doc.getElementsByTagName("head")[0];
		if (!h) { return; } // to also support badly authored HTML pages that lack a head element
		var m = (media && typeof media == "string") ? media : "screen";
		if (newStyle) {
			dynamicStylesheet = null;
			dynamicStylesheetMedia = null;
		}
		if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
			var s = createElement("style");
			s.setAttribute("type", "text/css");
			s.setAttribute("media", m);
			dynamicStylesheet = h.appendChild(s);
			if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
				dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
			}
			dynamicStylesheetMedia = m;
		}
		if (ua.ie && ua.win) {
			if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
				dynamicStylesheet.addRule(sel, decl);
			}
		}
		else {
			if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
				dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
			}
		}
	}

	function setVisibility(id, isVisible) {
		if (!autoHideShow) { return; }
		var v = isVisible ? "visible" : "hidden";
		if (isDomLoaded && getElementById(id)) {
			getElementById(id).style.visibility = v;
		}
		else {
			createCSS("#" + id, "visibility:" + v);
		}
	}

	/* Filter to avoid XSS attacks
	*/
	function urlEncodeIfNecessary(s) {
		var regex = /[\\\"<>\.;]/;
		var hasBadChars = regex.exec(s) != null;
		return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
	}

	/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
	*/
	var cleanup = function() {
		if (ua.ie && ua.win) {
			window.attachEvent("onunload", function() {
				var ll = listenersArr.length;
				for (var i = 0; i < ll; i++) {
					listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
				}
				var il = objIdArr.length;
				for (var j = 0; j < il; j++) {
					removeSWF(objIdArr[j]);
				}
				for (var k in ua) {
					ua[k] = null;
				}
				ua = null;
				for (var l in CVP.swfobject) {
					CVP.swfobject[l] = null;
				}
				CVP.swfobject = null;
			});
		}
	}();

	return {
		/* Public API
			- Reference: http://code.google.com/p/swfobject/wiki/documentation
		*/
		registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
			if (ua.w3 && objectIdStr && swfVersionStr) {
				var regObj = {};
				regObj.id = objectIdStr;
				regObj.swfVersion = swfVersionStr;
				regObj.expressInstall = xiSwfUrlStr;
				regObj.callbackFn = callbackFn;
				regObjArr[regObjArr.length] = regObj;
				setVisibility(objectIdStr, false);
			}
			else if (callbackFn) {
				callbackFn({success:false, id:objectIdStr});
			}
		},

		getObjectById: function(objectIdStr) {
			if (ua.w3) {
				return getObjectById(objectIdStr);
			}
		},

		embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
			var callbackObj = {success:false, id:replaceElemIdStr};
			if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
				setVisibility(replaceElemIdStr, false);
				addDomLoadEvent(function() {
					widthStr += ""; // auto-convert to string
					heightStr += "";
					var att = {};
					if (attObj && typeof attObj === OBJECT) {
						for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
							att[i] = attObj[i];
						}
					}
					att.data = swfUrlStr;
					att.width = widthStr;
					att.height = heightStr;
					var par = {};
					if (parObj && typeof parObj === OBJECT) {
						for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
							par[j] = parObj[j];
						}
					}
					if (flashvarsObj && typeof flashvarsObj === OBJECT) {
						for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
							if (typeof par.flashvars != UNDEF) {
								par.flashvars += "&" + k + "=" + flashvarsObj[k];
							}
							else {
								par.flashvars = k + "=" + flashvarsObj[k];
							}
						}
					}
					if (hasPlayerVersion(swfVersionStr)) { // create SWF
						var obj = createSWF(att, par, replaceElemIdStr);
						if (att.id == replaceElemIdStr) {
							setVisibility(replaceElemIdStr, true);
						}
						callbackObj.success = true;
						callbackObj.ref = obj;
					}
					else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
						att.data = xiSwfUrlStr;
						showExpressInstall(att, par, replaceElemIdStr, callbackFn);
						return;
					}
					else { // show alternative content
						setVisibility(replaceElemIdStr, true);
					}
					if (callbackFn) { callbackFn(callbackObj); }
				});
			}
			else if (callbackFn) { callbackFn(callbackObj);	}
		},

		switchOffAutoHideShow: function() {
			autoHideShow = false;
		},

		ua: ua,

		getFlashPlayerVersion: function() {
			return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
		},

		hasFlashPlayerVersion: hasPlayerVersion,

		createSWF: function(attObj, parObj, replaceElemIdStr) {
			if (ua.w3) {
				return createSWF(attObj, parObj, replaceElemIdStr);
			}
			else {
				return undefined;
			}
		},

		showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
			if (ua.w3 && canExpressInstall()) {
				showExpressInstall(att, par, replaceElemIdStr, callbackFn);
			}
		},

		removeSWF: function(objElemIdStr) {
			if (ua.w3) {
				removeSWF(objElemIdStr);
			}
		},

		createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
			if (ua.w3) {
				createCSS(selStr, declStr, mediaStr, newStyleBoolean);
			}
		},

		addDomLoadEvent: addDomLoadEvent,

		addLoadEvent: addLoadEvent,

		getQueryParamValue: function(param) {
			var q = doc.location.search || doc.location.hash;
			if (q) {
				if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
				if (param == null) {
					return urlEncodeIfNecessary(q);
				}
				var pairs = q.split("&");
				for (var i = 0; i < pairs.length; i++) {
					if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
						return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
					}
				}
			}
			return "";
		},

		expressInstallCallback: function() {
			if (isExpressInstallActive) {
				var obj = getElementById(EXPRESS_INSTALL_ID);
				if (obj && storedAltContent) {
					obj.parentNode.replaceChild(storedAltContent, obj);
					if (storedAltContentId) {
						setVisibility(storedAltContentId, true);
						if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
					}
					if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
				}
				isExpressInstallActive = false;
			}
		}
	};
}();

/* Code for immediate execution */
addBeforeUnLoadEvent(CVP.cleanup);

})();
(function(container) {

if (!container || !container.CVP)
{
	throw "CVP is a required dependency for the HTML5 player";
}

var debug = false;

var HTML5VERSION = "0.9.4";

var FREEWHEEL_JS_URL = "http://i.cdn.turner.com/xslo/cvp/ads/freewheel/js/AdManager.js";
var OMNITURE_JS_URL = "";

var Class = CVP.Class;


var undef = CVP.Utils.undef;
var nil = CVP.Utils.isNull;
var empty = CVP.Utils.empty;
var extend = CVP.Utils.extend;
var byId = CVP.Utils.byId;
var query = CVP.Utils.query;
var bind = CVP.Utils.bind;
var slice = CVP.Utils.slice;
var template = CVP.Utils.template;
var joinKeys = CVP.Utils.joinKeys;
var isFlagActive = CVP.Utils.isFlagActive;
var print = CVP.Utils.print;

var Event = CVP.Events.CustomEvent;
var CommandQueue = CVP.Utils.CommandQueue;
var Asset = CVP.Utils.Asset;

/**
 * Prints the logs.
 */
var log = function() {
	if (window.console
		&& window.console.log
		&& debug) {
		window.console.log(slice(arguments).join(" | "));
	}
};

var ConditionalTask = function(condition, success, interval) {
	this._interval = nil(interval) ? 10 : interval;
	this._maxTries = 500;

	this._condition = condition;
	this._success = success;

	this._tries = 0;

	this.conditionWrapper = bind(function() {
		var ret = this._condition();
		if (ret == true)
		{
			clearInterval(this._timer);
			this._success();
		}
		else if (this._tries > this._maxTries)
		{
			log("condition never met!");
			clearInterval(this._timer);
		}
		this._tries++;
	}, this);;

	this.start = function() {
		this._timer = setInterval(this.conditionWrapper, this._interval);
	};
};

var ConfigUtils = {
	stringReplace : function(str, entry, uriEncode) {
			var patterns = this.getReplacementPatterns(str);

			if (!empty(patterns)) {
			for (var i = 0; i < patterns.length; i++)
			{
				var pattern = patterns[i];
				var replaceStr = "";

				replaceStr = this.getReplacementText(pattern, entry);

				if (empty(replaceStr))
					replaceStr = "";
				else if (uriEncode)
					replaceStr = encodeURI(replaceStr);

				str = str.replace(pattern, replaceStr);
			}
			}

			return str;
	},

	getReplacementPatterns : function(str) {
		return(str.match(/[$][{][^}]*[}]/g));
	},

	getReplacementText : function(pattern, entry) {
			var replaceStr = "";

			switch (pattern)
			{
				case "${page.domain}":
					if (player)
						replaceStr = document.domain;
					break;

				case "${page.url}":
					replaceStr = document.location.href;
					break;

				case "${videoId}":
				case "${video.id}":
					if (entry)
						replaceStr = entry.getId();
					break;

				default:
					var nodeName = pattern.substr(2, pattern.length - 3);

					var array = nodeName.split('.');
					var objName = array[0];

					if (array.length > 1)
					{
						nodeName = nodeName.substr(objName.length + 1);

						switch (objName)
						{
							case "video":
								if (entry)
									replaceStr = XMLUtils.getNodeValue(entry._xmlEntry, nodeName);
							break;
							default:
								replaceStr = pattern;
							break;
						}
					}
					else
						replaceStr = pattern;
				break;
			}

			return replaceStr;
		}
};
var Ajax = {
	get : function(obj) {
		obj.type = "GET";
		this._request(obj);
	},
	getXml : function(obj) {
		obj.dataType = "xml";
		this.get(obj);
	},
	post : function(obj) {
		obj.type = "POST";
		this._request(obj);
	},

	_request : function(obj)
	{
		var request = new XMLHttpRequest();

		request.onreadystatechange = function()
		{
			if (request.readyState == 4)
			{
				if (request.status <= 200 && request.status < 400)
				{
					if (obj.success)
						obj.success(request.responseText);
				}
				else
				{
					if (obj.error)
						obj.error(request);
				}
				request = null;
			}
		}

		request.open(obj.type, obj.url, true);
		request.send(obj.type == "POST" ? obj.data : null);
	}
};

var Loader = Class.extend({
	init : function() {
		this.eLoaded = new Event("LoadedEvent");
		this.eLoadError = new Event("LoadErrorEvent");
	},

	load : function(url) {
		var self = this;

		var success = function(data) {
			log("Loader", "success:", url);
			self.eLoaded.dispatch(data);
		};

		var error = function(requestObj, textStatus, errorThrown) {
			log("Loader", "error:", url,"text:",textStatus);
			self.eLoadError.dispatch(requestObj, textStatus, errorThrown);
		};

		Ajax.get({
			url : url,
			success : success,
			error : error
		});
	}
});

var XMLLoader = Class.extend({
	init : function() {
		this._loader = new Loader();
		this._loader.eLoaded.addListener(this._onLoaded, this);
		this._loader.eLoadError.addListener(this._onLoadError, this);

		this.eLoaded = new Event();
		this.eLoadError = new Event();
	},

	load : function(url) {
		this._loader.load(url);
	},
	_onLoaded : function(xmlStr) {
		xmlStr = XMLUtils.clean(xmlStr);
		var xDoc = XMLUtils.createDoc(xmlStr);

		log("XMLLoader", "XMl loaded");
		this.eLoaded.dispatch(xDoc);
	},

	_onLoadError : function(requestObj, textStatus, errorThrown) {
		log("XMLParser","onLoadError:", textStatus);

		if (textStatus == "parsererror") {
			log("XMLParser", "ParseError...attempting to create an xml doc manually");
			var xmlStr = requestObj.responseText;

			xmlStr = XMLUtils.clean(xmlStr);

			var xDoc = XMLUtils.createDoc(xmlStr);
			if (xDoc)
				this.eLoaded.dispatch(xDoc);	//TODO after cleaning up ajax impl, clean this up
			else
				this.eLoadError.dispatch(requestObj, textStatus, errorThrown);
		}
	}
});
var XMLParser = Class.extend({
	init : function() {
		this._loader = new XMLLoader();
		this._loader.eLoaded.addListener(this.onLoaded, this);
		this._loader.eLoadError.addListener(this.onLoadError, this);
	},

	/**
	 * Load and parse the xml
	 * @param url The url of the xml to load and parse
	 */
	parse : function(url) {
		log("XMLParser", "Loading the xml to parse", url);
		this.load(url);
	},

	load : function(url) {
		this._loader.load(url);
	},
	onLoaded : function(xml) {
		log("XMLParser", "XMl loaded");
		this.parseXml(xml);
	},
	onLoadError : function(requestObj, textStatus, errorThrown) {
		log("XMLParser","onLoadError:", textStatus);
	},

	parseXml : function(xml) {
	}
});

var ContainerParser = XMLParser.extend({
	init : function() {
		this.eParseCompleted = new Event();
		this.eParseError = new Event();

		this._super();
	},

	parseXml : function(xml) {
		var self = this,
			docElement = xml.documentElement,
			contextName = _params.context || "",
			contexts = null,
			context = null,
			node = null
		;

		contexts = docElement.getElementsByTagName("context");

		for (var i = 0; i < contexts.length; i++)
		{
			if (XMLUtils.getAttribute(contexts[i],"name") == contextName)
			{
				context = contexts[i];
				break;
			}
		}

		if (context)
		{
			var players = context.getElementsByTagName("player");
			var playerName = XMLUtils.getAttribute(players[players.length - 1],"instance");

			if (empty(playerName))
				playerName = contextName;

			_containerInfo.playerInstance = playerName;
		}
		CVP.Utils.print(_containerInfo, "_containerInfo");

		this.eParseCompleted.dispatch();
	}
});

var ConfigParser = XMLParser.extend({
	init : function() {
		this.eParseCompleted = new Event();
		this.eParseError = new Event();

		this._super();
	},

	parseXml : function(xml) {
		var self = this,
			docElement = xml.documentElement,
			playerInstanceName = _containerInfo.playerInstance || "",
			player = null,
			node
		;

		var players = docElement.getElementsByTagName("player");
		while (players.length) {
			node = players[players.length - 1];

			if (XMLUtils.getAttribute(node,"name") == playerInstanceName)
			{
				player = node.cloneNode(true);
			}

			docElement.removeChild(node);
		}

		var process = function(xml, objToAssign) {
			if (nil(xml))
				return;

			var childNodes = xml.childNodes,
				xmlLength = childNodes.length,
				childNode,
				obj,
				i = 0
			;

			if (xmlLength == 0)
				return;

			do {
				childNode = childNodes[i];

				obj = self.findMapping(childNode.nodeName);
				if (!nil(obj))
				{

					self.parseAttributes(obj, childNode);
					if (childNode.childNodes.length)
					{
						process(childNode, obj);
					}
				}
				else if (childNode.nodeName == "param")
				{
					self.parseParams(objToAssign,
						XMLUtils.getAttribute(childNode, "name"),
						XMLUtils.getAttribute(childNode, "value")
					);
				}

				i++;
			} while(i < xmlLength);
		};

		process(docElement, _configInfo);

		if (!nil(player))
		{
			process(player, _configInfo);
		}

		CVP.Utils.print(_configInfo, "_configInfo");

		players = player = childNodes = childNode = null;
		this.eParseCompleted.dispatch();
	},

	/**
	 * Parse the param nodes from the config.
	 * @param obj - the object to assign the property to
	 * @param xml - the xml to parse
	 */
	parseParams : function(obj, name, value) {
		switch(name)
		{
				case "ad_api":
					obj.apiUrl = value;
					break;
				case "ad_man_root_url":
					obj.adManRootUrl = value;
					break;
				case "ad_server_root_url":
					obj.adServerRootUrl = value;
					break;
				case "ad_video_root_url":
					obj.adVideoRootUrl = value;
					break;
				case "ad_video_extension":
					obj.adVideoExtension = value;
					break;
				case "additional_video_segvars":
					obj.additionalVideoSegVars = value;
					break;
				case "additional_sync_segvars":
					obj.additionalSyncSegVars = value;
					break;
				case "ad_section":
					obj.adSection = value;
					break;
				case "ad_network_id":
					obj.adNetworkId = value;
					break;
				case "ad_video_network_id":
					obj.adVideoNetworkId = value;
					break;
				case "ad_video_asset_id":
					obj.adVideoAssetId = value;
					break;
				case "ad_player_profile":
					obj.adPlayerProfile = value;
					break;
				case "renderers_url":
					obj.renderersUrl = value;
					break;
				case "external_slots":
					obj.externalSlots = isFlagActive(value, obj.externalSlots);
					break;
				case "ad_live_content_duration":
					obj.liveDuration = value;
					break;

				default:
					obj[name] = value;
		}
	},

	parseAttributes : function(obj, node)
	{
		if (node.hasAttributes)
		{
			if (!obj.attr)
				obj.attr = {};
			XMLUtils.assignAttributes(obj.attr, node.attributes);
		}
	},

	findMapping : function(nodeName) {
		var obj = null;
		switch(nodeName)
		{
			case "ad_server":
				obj = _configInfo.ads;
				break;
		}
		return obj;
	}
});
var XMLUtils = {
	createDoc : function(xmlString) {
		if (undef(window.DOMParser))
		{
			this.createDoc = function(xmlString) {
				if (empty(xmlString)) return null;

				var xDoc = new ActiveXObject("MSXML2.DOMDocument");
   				xDoc.async = false;
    			xDoc.loadXML(xmlString);
				return xDoc;
			};
		}
		else
		{
			this.createDoc = function(xmlString) {
				if (empty(xmlString)) return null;

				var parser = new DOMParser();
    			var xDoc = parser.parseFromString(xmlString, "text/xml");
    			return xDoc;
			};
		}
		return this.createDoc(xmlString);
	},

	clean : function(xmlString) {
		xmlString = xmlString.replace(/\>\s*?\</g, "><");

		xmlString = xmlString.replace(/\t*/g, "");

		xmlString = xmlString.replace(/\n*/g, "");

		xmlString = xmlString.replace(/<!--(.*?)-->/g, "");

		xmlString = xmlString.replace(/&/g, "&amp;");

		while (xmlString.indexOf('xmlns') > -1) {
			var start = xmlString.indexOf('xmlns');
			var firstQuote = xmlString.indexOf('"',start);
			var endQuote = xmlString.indexOf('"',firstQuote+1);

			xmlString = xmlString.slice(0,start) + xmlString.slice(endQuote+1);
		}

		return xmlString;
	},

	getNodeValue : function(doc, nodeName) {
		if (doc)
		{
			if (!nodeName)
				return (!nil(doc.firstChild) ? doc.firstChild.nodeValue : null);

			var nodes = doc.getElementsByTagName(nodeName);
			if (nodes.length > 0)
				return nodes[0].firstChild.nodeValue;
		}
		return null;
	},

	getAttribute : function(node, attrName) {
		if (node
			&& node.attributes
			&& !empty(attrName))
		{
			var attr = node.attributes.getNamedItem(attrName);
			return attr ? attr.nodeValue : null;
		}
	},

	assignAttributes : function(obj, attributes)
	{
		for (var a in attributes)
		{
			obj[attributes[a].nodeName] = attributes[a].nodeValue;
		}
	},

	toString : function(xmlDoc) {
		if (undef(window.XMLSerializer))
		{
			this.toString = function(xmlDoc) {
				return xmlDoc.xml;
			};
		}
		else
		{
			this.toString = function(xmlDoc) {
				return new XMLSerializer().serializeToString(xmlDoc);
			};
		}
		return this.toString(xmlDoc);
	}
};
var DependencyMananger = Class.extend({
	init : function()
	{
		this._dependencies = [];
		this._currentDependency = null;

		this.eSuccess = new Event();				// All required dependencies loaded successfully
		this.eFailure = new Event();				// One or more required dependencies failed to load
		this.eDependencySuccess = new Event();		// A dependency loaded successfully
		this.eDependencyFailure = new Event();		// A dependency failed to load
	},

	addDependency : function(dependency)
	{
		dependency.setManager(this);
		this._dependencies.push(dependency);
	},

	load : function()
	{
		this._currentDependency = this._dependencies.shift();
		this._currentDependency.eSuccess.addListener(this._onDependencySuccess, this);
		this._currentDependency.eFailure.addListener(this._onDependencyFailure, this);

		var self = this;
		setTimeout(function() { self._currentDependency.load() }, 10);
	},

	_onDependencySuccess : function()
	{
		this.eDependencySuccess.dispatch(this._currentDependency.getDesc());
		this._next();
	},

	_onDependencyFailure : function()
	{
		this.eDependencyFailure.dispatch(this._currentDependency.getDesc());

		if (this._currentDependency.required)
		{
			this._onFailure(this._currentDependency.getDesc());
			return;
		}

		this._next();
	},

	_next : function()
	{
		if (this._dependencies.length)
			this.load();
		else
			this._onSuccess();
	},
	_onSuccess : function(desc)
	{
		this.eSuccess.dispatch(desc);
	},
	_onFailure : function(desc)
	{
		this.eFailure.dispatch(desc);
	}
});

var Dependency = Class.extend({
	init : function(assetUrl, required)
	{
		this._assetUrl = assetUrl;
		this.required = required;

		this._manager = null;

		this.eSuccess = new Event();
		this.eFailure = new Event();
	},

	load : function()
	{
		log("Dependency", "loading asset", this._assetUrl);
		var assetLoader = new Asset(this._assetUrl);
		assetLoader.eSuccess.addListener(this._success, this);
		assetLoader.eFailure.addListener(this._failure, this);
		assetLoader.load();
	},

	_success : function()
	{
		log("Dependency", "successfully loaded dependency", this._assetUrl);
		this.eSuccess.dispatch();
	},

	_failure : function()
	{
		log("Dependency", "failed to load dependency", this._assetUrl);
		this.eFailure.dispatch();
	},

	setManager : function(manager)
	{
		this._manager = manager;
	},

	getDesc : function()
	{
		return this._assetUrl;
	}
});

var ContainerDependency = Dependency.extend({
	init : function()
	{
		this._super.apply(this, arguments);
		this.required = true;

		this._parser = new ContainerParser();
		this._parser.eParseCompleted.addListener(this._onParseCompleted, this);
		this._parser.eParseError.addListener(this._onParseError, this);
	},

	load : function()
	{
		log("ContainerDependency", "loading xml", this._assetUrl);
		this._parser.parse(this._assetUrl);
	},

	_onParseCompleted : function()
	{
		this._success();
	},

	_onParseError : function()
	{
		this._failure();
	},

	getDesc : function()
	{
		return "ContainerDependency: " + this._assetUrl;
	}
});

var ConfigDependency = Dependency.extend({
	init : function()
	{
		this._super.apply(this, arguments);
		this.required = true;

		this._parser = new ConfigParser();
		this._parser.eParseCompleted.addListener(this._onParseCompleted, this);
		this._parser.eParseError.addListener(this._onParseError, this);
	},

	load : function()
	{
		this._parser.parse(this._assetUrl);
	},

	_onParseCompleted : function()
	{
		if (this._manager)
		{
			if (_configInfo.ads.attr.type.toLowerCase() == "freewheel")
			{
				log("ContainerDependency", "adding FW dependency");
				this._manager.addDependency(new Dependency(FREEWHEEL_JS_URL, true));
			}

			if (!empty(_configInfo.omniture.omniture_account))
			{
				log("ContainerDependency", "adding Omniture dependency");
			}
		}

		this._success();
	},

	_onParseError : function()
	{
		this._failure();
	},

	getDesc : function()
	{
		return "ConfigDependency: " + this._assetUrl;
	}
});

var CMS = Class.extend({
	init : function() {
		this._catalogDataURL = "";
		this._requestPendingQueue = [];
		this._videoCatalog = [];
		this._requests = {};

		this._loader = new XMLLoader();
		this._loader.eLoaded.addListener(this._onContentIdRequestComplete, this);
		this._loader.eLoadError.addListener(this._onContentIdRequestIOError, this);

		this.eRequestCompleted = new Event("CmsRequestCompletedEvent");
	},

	addContentId : function(contentId) {
		var entry = this.getContentId(contentId);

		if (nil(entry))
			this._requestPendingQueue.push(contentId);
		else {
			var index = contentId.split("|")[1];
                            contentId = contentId.split("|")[0];
			this._notifyListeners(contentId, contentId, index);
		}

		if (this._requestPendingQueue.length == 1)
			this._processNextRequest();
	},

	getContentId : function(contentId) {
		contentId = contentId.split("|")[0];

		var ct = this._videoCatalog.length;

		for (var i = 0; i < ct; i++)
		{
			var entry = this._videoCatalog[i];

			if (entry.getId() == contentId)
				return entry;
		}
		return null;
	},

	clear : function() {
		this._videoCatalog = [];
	},

	setDataUrl : function(url) {
		this._catalogDataURL = url;
	},

	_getRequestUrl : function(contentId) {
		var requestUrl;

		var start = this._catalogDataURL.substr(0).search(/\$\{/);
		if (start != -1)
		{
			var end = this._catalogDataURL.substr(start).search(/\}/);

			if (end != -1)
			{
				var pattern = this._catalogDataURL.substr(start, end + 1);
				requestUrl = this._catalogDataURL.replace(pattern, contentId);
			}
		}
		else
		{
			requestUrl = this._catalogDataURL + "/" + contentId + ".xml";
		}

		return requestUrl;
	},

	_createContentEntryFromRequestData : function(requestData, fileId) {
		try
		{
			return new ContentCatalogEntry(requestData);
		}
		catch(e)
		{
			alert(this._catalogDataURL + "/" + fileId + ".xml: " + e.message);
		}

		return null;
	},

	_processNextRequest : function() {
		if (this._requestPendingQueue.length > 0)
		{
			var contentId = this._requestPendingQueue[0].split("|")[0];
			this._requestVideoWithId(contentId);
		}
	},

	_requestVideoWithId : function(contentId) {
		var url = this._getRequestUrl(contentId);
		this._loader.load(url);
	},

	_onContentIdRequestComplete : function(data) {
		var request = this._requestPendingQueue[0].split("|");
		var contentId = request[0];
		var requestIndex = request[1];

		if (data != null)
		{
			var entry = this._createContentEntryFromRequestData(data, contentId);

			if (!entry)
			{
				this._notifyListeners(contentId, contentId, requestIndex, "No URLLoader");
			}
			else
			{
				entry.requestId = contentId;

				this._videoCatalog.push(entry);
				this._notifyListeners(entry.getId(), entry.requestId, requestIndex, "");
			}
		}
		else
		{
			this._notifyListeners(contentId, contentId, requestIndex, "No URLLoader");
		}

		this._requestPendingQueue.shift();
		this._processNextRequest();
	},

	_onContentIdRequestIOError : function() {
		var request = this._requestPendingQueue[0].split("|");
		var contentId = request[0];
		var requestIndex = request[1];


		this._notifyListeners(contentId, contentId, "IOError : " + event.text);

		this._requestPendingQueue.shift();
		this._processNextRequest();
	},

	_notifyListeners : function(contentId, requestId, index, errorMsg) {
		this.eRequestCompleted.dispatch(contentId, requestId, index, errorMsg);
	}
});
var ContentCatalogEntry = Class.extend({
	init : function(xml) {
		this._xmlEntry = xml.documentElement;
		this._requestId = null;

		this._name = XMLUtils.getNodeValue(this._xmlEntry, "slug");
		this._title = XMLUtils.getNodeValue(this._xmlEntry, "headline");
		this._category = XMLUtils.getNodeValue(this._xmlEntry, "category");
		this._trt = XMLUtils.getNodeValue(this._xmlEntry, "trt") * 1;

		var files = this._xmlEntry.getElementsByTagName("files");
		if (files.length)
		{
			files = files[0].childNodes;

			this._fileList = {};
			for (var f in files)
			{
				if (files[f].nodeType != 1) continue;

				var fKey = XMLUtils.getAttribute(files[f], "bitrate");
				var fFallback = XMLUtils.getAttribute(files[f], "fallback");
				var fValue = XMLUtils.getNodeValue(files[f]);
				var ext = empty(fValue) ? "" : fValue.substring(fValue.lastIndexOf(".") + 1);
				log("ContentCatalogEntry", fKey, fValue, ext);

				this._fileList[fKey] = {
					url : fValue,
					fallback : fFallback,
					ext : ext
				};
			}
		}
		else
		{
			log("ContentCatalogEntry", "No file entries found!");
		}

		var images = this._xmlEntry.getElementsByTagName("images");
		if (images.length)
		{
			images = images[0].childNodes;

			this._imageList = {};
			for (var image in images)
			{
				if (images[image].nodeType != 1) continue;

				var iWidth = XMLUtils.getAttribute(images[image], "width");
				var iHeight = XMLUtils.getAttribute(images[image], "height");

				var iValue = XMLUtils.getNodeValue(images[image]);
				log("ContentCatalogEntry", "images", iWidth, iHeight, iValue);

				var iKey = iWidth + "|" + iHeight;
				this._imageList[iKey] = {
					url : iValue,
					width : iWidth,
					height : iHeight
				};
			}
		}
	},

	getId : function()
	{
		var attr = XMLUtils.getAttribute(this._xmlEntry, "id");
		if (!empty(attr))
			return attr;
		else
			return this._requestId;
	},

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

	getTitle : function()
	{
		return this._title;
	},

	getCategory : function()
	{
		return this._category;
	},

	getTrt : function()
	{
		return this._trt;
	},

	getContentUrl : function(quality)
	{
		var url = null;
		var dbgFound = false;

		for (var bitrate in this._fileList)
		{
			if (bitrate == quality)
			{
				url = this._fileList[bitrate].url;
				if (url.indexOf("://") == -1)
					url = _configInfo.media_src + url;
				dbgFound = true;
				break;
			}
		}

		if (!dbgFound)
			log("Config XML specifies a " + quality + " bitrate entry, but there is no <file> node in video XML where bitrate attribute = " + quality + ".");
		else if (empty(url))
			log("text attribute is empty in the video XML for the <file> node whose quality is " + quality + ".");

		return url;
	},

	getContentUrlFromFallback : function(fallback) {
		var url = null;
		for (var bitrate in this._fileList) {
			if (!(empty(this._fileList[bitrate].fallback)) && fallback == this._fileList[bitrate].fallback) {
				url = this._fileList[bitrate].url;
				if (url.indexOf("://") == -1)
					url = _configInfo.media_src + url;
				break;
			}
		}
		return url;
	},

	getContentUrlFromType : function(ext)
	{
		var url = null;
		for (var bitrate in this._fileList)
		{
			if (ext == this._fileList[bitrate].ext)
			{
				url = this._fileList[bitrate].url;
				if (url.indexOf("://") == -1)
					url = _configInfo.media_src + url;
				break;
			}
		}
		return url;
	},

	getThumbnailUrl : function(width, height)
	{
		var url = null;
		var key = width + "|" + height;

		for (var k in this._imageList)
		{
			if (k == key)
			{
				url = this._imageList[k].url;
				break;
			}
		}
		return url;
	},

	getXML : function()
	{
		return this._xmlEntry;
	}
});

var BasePlayer = Class.extend({
	init : function(options) {
		this.options = extend({
			containerElement : '',
			elementId : '',
			autoplay : true,
			controls : true,
			width : 0,
			height : 0
		}, options || {});

		this.containerElement = byId(this.options.containerElement);

		this.elementId = this.options.elementId;
		if (empty(this.elementId))
		{
			log("Invalid element id...exiting");
			throw new "Invalid element id";
		}

		this.playerOptions = {};

		this.width = this.options.width;
		this.height = this.options.height;
		this.airplay = (!CVP.Browser.apple_mobile && (undef(_params.ios_airplay) || undef(_configInfo.ios_airplay))) ? "" : "x-webkit-airplay=\"allow\"";
		this.perVideoFallbacks = (undef(_params.perVideoFallbacks)) ? new Array() : _params.perVideoFallbacks;


		this.element = null;

		this.rendering = false;

		this.position = {
			top : null,
			right : null,
			bottom : null,
			left : null
		};

		this.eRendered = new Event();			// After the markup for the media element has been rendered
		this.eContentBegin = new Event();		// When play is called, before the ad preroll plays
		this.eContentPlay = new Event();		// After any prerolls, before the video starts playing
		this.eContentEnded = new Event();		// After the video has completed, before any postrolls have been played
		this.eContentCompleted = new Event();	// After any postrolls have been played
	},

	getMarkup : function() {
		return "";
	},

	render : function(containerElement) {
		log("Player", "render");
		if (!empty(containerElement))
			this.containerElement = byId(containerElement);

		if (nil(this.containerElement))
		{
			log("Container element could not be found...cannot render...exiting");
			throw new "Container could not be found";
		}

		var markup = this.getMarkup();
		this.rendering = true;
		this.containerElement.innerHTML = markup;

		var self = this;
		new ConditionalTask(
			function() {
				if (!nil(byId(self.elementId)))
					return true;
			},
			function () {
				self.rendering = false;
				self.element = byId(self.elementId);
				self.fireRendered();
			}
		).start();
	},

	rendered : function() {
		return this.element && this.element.parentNode==this.containerElement;
	},

	show : function() {
		if (this.element)
		{
			if (this.position.left)
				this.element.style.left = this.position.left
		}
	},
	hide : function() {
		if (this.element)
		{
			this.position.left = this.element.style.left;
			this.element.style.left = "-5000px";
		}
	},

	addPlayerListeners : function() {
	},
	removePlayerListeners : function() {
	},

	fireRendered : function() {
		this.eRendered.dispatch("video");
	},
	fireBegin : function() {
		this.eContentBegin.dispatch(this._catalogEntry.getId());
	},
	firePlay : function() {
		this.eContentPlay.dispatch();
	},
	fireEnded : function() {
		this.eContentEnded.dispatch();
	},
	fireCompleted : function() {
		this.eContentCompleted.dispatch();
	}
});

var VideoPlayer = BasePlayer.extend({
	init : function(options) {
		this._super(options);

		this.playerOptions["controls"] = this.options.controls;
		this.playerOptions["autoplay"] = this.options.autostart;

		this._supportedFileTypes = this._determineSupportedFileTypes();

		this._firePlay = false;
		this._addedListeners = false;

		this._onLoadedMetaDataBind = bind(this._onLoadedMetaData, this);
		this._onContentPlayBind = bind(this._onContentPlay, this);
		this._onContentEndedBind = bind(this._onContentEnded, this);
	},

	getMarkup : function() {
		return template("<video id=\"{0}\" width={1} height={2} controls {3}></video>", this.elementId, this.width, this.height, this.airplay);
	},

	play : function(catalogEntry) {
		if (CVP.Browser.apple_mobile)
		{
			this.play = function(catalogEntry)
			{
				this._catalogEntry = catalogEntry;
				var url = this._getFileUrl(catalogEntry);
				this.element.src = url;

				this._setOptions();
				this._setThumbnail();
				this.element.load();

				this.element.play();

				this._firePlay = true;
			}
		}
		else
		{
			this.play = function(catalogEntry)
			{
				this._catalogEntry = catalogEntry;
				var url = this._getFileUrl(catalogEntry);
				this.element.src = url;

				if (!this.playerOptions.autoplay)
				{
					this._setThumbnail();
				}
				this.element.load();
			}
		}
		this.play(catalogEntry);
	},

	pause : function()
	{
		this.element.pause();
	},

	resume : function()
	{
		if (this.element.paused)
			this.element.play();
	},

	setVolume : function(v)
	{
		this.element.volume = v;
	},

	getVolume : function()
	{
		return this.element.volume;
	},

	mute : function()
	{
		this.element.muted = true;
	},

	unmute : function()
	{
		this.element.muted = false;
	},

	_setOptions : function()
	{
		for (var option in this.playerOptions)
		{
			this.element[option] = this.playerOptions[option];
		}
	},

	_setThumbnail : function()
	{
		var image = this._catalogEntry.getThumbnailUrl(this.width, this.height);
		log("thumb", image);
		if (image)
		{
			this.element.poster = image;
		}
	},

	_onLoadedMetaData : function()
	{
		if (CVP.Browser.apple_mobile)
		{
			this._onLoadedMetaData = function()
			{
				log("VideoPlayer", "_onLoadedMetaData");
			}
		}
		else
		{
			this._onLoadedMetaData = function()
			{
				log("VideoPlayer", "_onLoadedMetaData");
				this._onLoaded();
			}
		}
		this._onLoadedMetaData();
	},

	_onLoaded : function()
	{
		this._setOptions();
		this._firePlay = true;
		this.element.play();
	},

	_onReadyStateChange : function()
	{
		log("VideoPlayer", "_onReadyStateChange");
	},

	_onContentPlay : function()
	{
		log("VideoPlayer", "_onContentPlay");
		if (this._firePlay)
		{
			this.firePlay();
			this._firePlay = false;
		}
	},

	_onContentPlaying : function()
	{
		log("VideoPlayer", "_onContentPlaying");
	},

	_onContentPaused : function()
	{
		log("VideoPlayer", "_onContentPaused");
	},

	_onContentTimeUpdate : function()
	{
	},

	_onContentEnded : function()
	{
		log("VideoPlayer", "_onContentEnded");
		this.fireEnded();
	},

	addPlayerListeners : function() {
		if (this.element
			&& !this._addedListeners)
		{
			log("VideoPlayer", "addPlayerListeners");
			this.element.addEventListener("loadedmetadata", this._onLoadedMetaDataBind, false);
			this.element.addEventListener("play", this._onContentPlayBind, false);
			this.element.addEventListener("ended", this._onContentEndedBind, false);

			this.element.addEventListener("readystatechange", this._onReadyStateChange, false);
			this.element.addEventListener("playing", this._onContentPlaying, false);
			this.element.addEventListener("pause", this._onContentPaused, false);
			this.element.addEventListener("timeupdate", this._onContentTimeUpdate, false);

			this._addedListeners = true;
		}
	},
	removePlayerListeners : function() {
		if (this.element
			&& this._addedListeners)
		{
			log("VideoPlayer", "removePlayerListeners");
			this.element.removeEventListener("loadedmetadata", this._onLoadedMetaDataBind, false);
			this.element.removeEventListener("play", this._onContentPlayBind, false);
			this.element.removeEventListener("ended", this._onContentEndedBind, false);

			this.element.removeEventListener("readystatechange", this._onReadyStateChange, false);
			this.element.removeEventListener("playing", this._onContentPlaying, false);
			this.element.removeEventListener("pause", this._onContentPaused, false);
			this.element.removeEventListener("timeupdate", this._onContentTimeUpdate, false);

			this._addedListeners = false;
		}
	},

	_getFileUrl : function(catalogEntry)
	{
		var url = this._getFileByParameterFallback(catalogEntry);
		if (url == null)
			url = this._getFileByFallback(catalogEntry)
		if (url == null) {
			var url = catalogEntry.getContentUrl(_configInfo.low_bitrate);
			if (!this._isFileSupported(url))
			{
				log("VideoPlayer", "File is not supported!", url);
				log("VideoPlayer", "Attempting to find a compatible file");
				url = this._getFileBySupportedType(catalogEntry);
				if (empty(url))
					url = this._getFileByConfigFallbackParam(catalogEntry);
				log("VideoPlayer", "Found:", url);
			}
		} else {
			log("VideoPlayer", "Fallback file found", url);
		}
		return url;
	},

	_getFileByParameterFallback : function(catalogEntry) {
		var foundit = -1;
		for (var i = 0; i < this.perVideoFallbacks.length; i++) {
			var dvf = this.perVideoFallbacks[i];
			var criteria = ConfigUtils.stringReplace(dvf.criteria, catalogEntry, false);
			if (dvf.evaluate(criteria)) {
				return ConfigUtils.stringReplace(dvf.filter, catalogEntry, false);
			}
		}
		return null;
	},

	_getFileByConfigFallbackParam : function(catalogEntry) {
		var str = _configInfo.fallback_filename_iOS;
		if (empty(str))
			return str;
		else
			str = ConfigUtils.stringReplace(str, catalogEntry, false);
		return str;
	},

	_getFileByFallback : function(catalogEntry) {
		if (CVP.Browser.apple_mobile) {
			return catalogEntry.getContentUrlFromFallback("iOS");
		}
	},

	_getFileBySupportedType : function(catalogEntry)
	{
		var url = null;
		var len = this._supportedFileTypes.length;
		for (var i = 0; i < len; i++)
		{
			url = catalogEntry.getContentUrlFromType(this._supportedFileTypes[i]);
		}
		return url;
	},

	_isFileSupported : function(url)
	{
		if (!empty(url))
		{
			var ext = url.indexOf(".") > -1 ? url.substring(url.lastIndexOf(".") + 1) : url;
			var len = this._supportedFileTypes.length;
			for (var i = 0; i < len; i++)
			{
				if (ext == this._supportedFileTypes[i])
				{
					return true;
				}
			}
		}
		return false;
	},

	_determineSupportedFileTypes : function()
	{
		if (CVP.Browser.apple_mobile)
		{
			return ["m3u8", "mp4"]
		}
		else
		{
			return ["mp4"];
		}
	}
});
var PlayerController = Class.extend({
	init : function()
	{
		this._commandQ = new CommandQueue();;

		this._videoPlayer = new VideoPlayer({
			containerElement : "player_container",
			elementId : "videoPlayer",
			width : _params.width,
			height : _params.height,
			controls : true,
			autostart : _params.autostart
		});

		this._videoPlayer.eRendered.addListener(this._onRendered, this);
		this._videoPlayer.eContentPlay.addListener(this._onContentPlay, this);
		this._videoPlayer.eContentEnded.addListener(this._onContentEnded, this);

		this._adProxy = new AdServerProxy();
		this._adProxy.eAdStarted.addListener(this._onAdStarted, this);
		this._adProxy.eAdEnded.addListener(this._onAdEnded, this);
		this._adProxy.eAdError.addListener(this._onAdError, this);

		this._contentQueue = [];

		this.eRendered = new Event();
		this.eContentBegin = new Event();
		this.eContentPlay = new Event();
		this.eContentCompleted = new Event();

		this.eAdStarted = new Event();
		this.eAdFinished = new Event();
		this.eAdError = new Event();

	},

	render : function(containerElement)
	{
		this._adProxy.setVideoDisplayBase(containerElement);
		this._videoPlayer.render(containerElement)
	},

	_onRendered : function(type)
	{
		log("_onRendered", type);

		this._commandQ.execute();
		this.eRendered.dispatch();
	},

	play : function(catalogEntry) {
		log("PlayerController", "play", catalogEntry.getId());
		this._catalogEntry = catalogEntry;

		if (!this._videoPlayer.rendered())
		{
			this._commandQ.push(this.play, this, arguments);
			return;
		}

		this._onContentBegin();
		if (this._adProxy.enabled)
			this._adProxy.loadAds(catalogEntry);
		else
			this._playContent();
	},

	_playContent : function()
	{
		log("PlayerController", "_playContent");
		this._videoPlayer.addPlayerListeners();
		this._videoPlayer.play(this._catalogEntry);
	},

	queue : function(catalogEntry)
	{
		log("PlayerController", "queue", catalogEntry.getId());
		this._contentQueue.push(catalogEntry);
	},

	emptyQueue : function()
	{
		this._contentQueue = [];
	},

	pause : function()
	{
		this._videoPlayer.pause();
	},

	resume : function()
	{
		this._videoPlayer.resume();
	},

	setVolume : function(v)
	{
		this._videoPlayer.setVolume(v);
	},

	getVolume : function()
	{
		return this._videoPlayer.getVolume();
	},

	mute : function()
	{
		this._videoPlayer.mute();
	},

	unmute : function()
	{
		this._videoPlayer.unmute();
	},

	_onContentBegin : function()
	{
		log("PlayerController", "_onContentBegin");
		this.eContentBegin.dispatch(this._catalogEntry.getId());
	},

	_onContentPlay : function()
	{
		log("PlayerController", "_onContentPlay");
		this.eContentPlay.dispatch(this._catalogEntry.getId());
	},

	_onContentEnded : function()
	{
		log("PlayerController", "_onContentEnded");

		if (this._adProxy.enabled)
			this._adProxy.playPostroll();
		else
			this._onContentCompleted();
	},

	_onContentCompleted : function()
	{
		log("PlayerController", "_onContentCompleted");
		this.eContentCompleted.dispatch(this._catalogEntry.getId());
		if (this._contentQueue.length)
			this.play(this._contentQueue.shift());
	},

	_onAdStarted : function(e)
	{
		log("PlayerController", "_onAdStarted");
		this.eAdStarted.dispatch();
		this._videoPlayer.removePlayerListeners();
	},
	_onAdEnded : function(e)
	{
		timePositionClass = e && e.timePositionClass ? e.timePositionClass : null;

		log("PlayerController", "_onAdEnded", timePositionClass);

		this.eAdFinished.dispatch();
		if (timePositionClass == AdServerProxy.PREROLL
			|| this._adProxy.currentAdType == AdServerProxy.PREROLL)
			this._playContent();
		else
			this._onContentCompleted();
	},
	_onAdError : function(e)
	{
		this.eAdError.dispatch();
	}
});
var AdServerProxy = Class.extend({
	init : function()
	{
		this._adServerInfo = _configInfo.ads;

		this._freewheel = null;
		if (!undef(window.tv) && window.tv.freewheel)
		{
			if (!AdServerProxy.initialized)
			{
				AdServerProxy.PREROLL = tv.freewheel.SDK.TIME_POSITION_CLASS_PREROLL;
				AdServerProxy.POSTROLL = tv.freewheel.SDK.TIME_POSITION_CLASS_POSTROLL;
				AdServerProxy.initialized = true;
			}

		}
		else
		{
			this.enabled = false;
		}

		this.currentAdType = null;
	   	this._adSection = this.getConfigProperty("ad_section");

	   	this.eAdStarted = new Event();
	   	this.eAdEnded = new Event();
	   	this.eAdError = new Event();
	},

	setVideoDisplayBase : function(videoDisplayBase)
	{
		if (!undef(window.tv) && window.tv.freewheel)
		{
			this.displayBase = videoDisplayBase;
			this.enabled = true;
		}
	},

	loadAds : function(catalogEntry)
	{
		this._freewheel = new tv.freewheel.SDK.AdManager();

  	var serverUrl = this.getConfigProperty("ad_server_root_url") + "/ad/g/1?nw=" + this.getConfigProperty("ad_network_id") +
			"&prof=" + this.getConfigProperty("ad_network_id") + ":" + this.getConfigProperty("ad_player_profile") + "&flag=+sltp+exvt+slcb+unka+unks;";

		this._freewheel.setServerURL(serverUrl);
		this._freewheel.registerVideoDisplayBase(this.displayBase);

		var duration = catalogEntry.getTrt();

		var adId = "";
		if (empty(this._adServerInfo.adVideoAssetId))
			adId = catalogEntry.getId();
		else
			adId = ConfigUtils.stringReplace(this._adServerInfo.adVideoAssetId, catalogEntry, false);

		this._freewheel.setVideoAsset(adId, duration);

    	this._freewheel.setSiteSection(this._adSection);
     	this._freewheel.submitRequest(bind(this.onRequestCompleted, this), 2000);
	},

	onRequestCompleted : function(e)
	{
		log("AdServerProxy", "onRequestCompleted");
		this.currentAdType = AdServerProxy.PREROLL;
	    this._startAd(AdServerProxy.PREROLL, bind(this.onPrerollCompleted, this), e);
	},

	onPrerollCompleted : function(e)
	{
		log("AdServerProxy", "onPrerollCompleted");
		this._adEnded(e);
	},

	playPostroll : function()
	{
		this.currentAdType = AdServerProxy.POSTROLL;
		this._startAd(AdServerProxy.POSTROLL, bind(this.onPostrollCompleted, this));
	},

	onPostrollCompleted : function(e)
	{
		log("AdServerProxy", "onPostrollCompleted");
		this._adEnded(e);
	},

	_startAd : function(slot, cb, e)
	{
		 this.eAdStarted.dispatch(e);
		 this._freewheel.playSlots(slot, cb);
	},

	_adEnded : function(e)
	{
		this.eAdEnded.dispatch(e);
		this._freewheel.dispose();
	},

	onAdPlayHead : function(playhead, duration)
	{

	},

	getConfigProperty : function(key)
	{
		var value = null;
		var required = false;
		switch (key)
		{
			case 'ad_api':
				value = this._adServerInfo.apiUrl;
				required = true;
				break;
			case 'ad_server_root_url':
				value = this._adServerInfo.adServerRootUrl;
				required = true;
				break;
			case 'ad_network_id':
				value = this._adServerInfo.adNetworkId;
				required = true;
				break;
			case 'ad_video_network_id':
				value = this._adServerInfo.adVideoNetworkId;
				required = true;
				break;
			case 'ad_video_asset_id':
				value = this._adServerInfo.adVideoAssetId;
				required = true;
				break;
			case 'ad_section':
				value = this._adServerInfo.adSection;
				required = true;
				break;
			case 'ad_player_profile':
				value = this._adServerInfo.adPlayerProfile;
				required = true;
				break;
			case 'renderers_url':
				value = this._adServerInfo.renderersUrl;
				required = false;
				break;
			case 'sensitive_fallback_id':
				value = this._adServerInfo.sensitiveFallbackId;
				required = false;
				break;
			case 'ad_live_content_duration':
				value = this._adServerInfo.liveDuration;
				required = false;
				break;
		}

		if (empty(value))
		{
			if (required)
				log("the following required ad server config value is missing:", key);
			else
				log("the following optional ad server config value is missing:", key);
		}

		return value;
	}
});
AdServerProxy.PREROLL = null;
AdServerProxy.POSTROLL = null;
var MainController = {
		init : function(options)
		{
		_params = this._options = options;
		_params.context =_params.flashVars && _params.flashVars.context;
		_params.contentId = _params.flashVars && _params.flashVars.contentId;
		_params.autoplay = _params.flashVars && isFlagActive(_params.flashVars.autostart);
		_params.containerUrl = _params.flashVars && _params.flashVars.containerUrl;
		_params.configUrl = _params.flashVars && _params.flashVars.configUrl;

		this._bootStrapper = new DependencyMananger();
		this._bootStrapper.addDependency(new ContainerDependency(this._options.containerUrl), true);
		this._bootStrapper.addDependency(new ConfigDependency(this._options.configUrl), true);

		this._bootStrapper.eSuccess.addListener(this._bootStrapSuccess, this);
		this._bootStrapper.eFailure.addListener(this._bootStrapFailure, this);
		this._bootStrapper.load();

		this.ePlayerLoaded = new Event();
		this.ePlayerRendered =  new Event();
		this.eContentBegin = new Event();
		this.eContentPlay =  new Event();
		this.eContentCompleted =  new Event();

		this.eAdStarted = new Event();
		this.eAdFinished = new Event();
		this.eAdError = new Event();
	},

	/**
	 * At this point, the following should true:
	 * - the container is loaded and parsed
	 * - the config is loaded and parsed
	 * - any other initially required dependencies are loaded
	 * 		i.e., FW, Omniture, UI-related tools, etc..
	 */
	_bootStrapSuccess : function()
	{
		log("MainController", "_bootStrapSuccess");

		this._playerController = new PlayerController();
		this._playerController.eRendered.addListener(this._onRendered, this);
		this._playerController.eContentBegin.addListener(this._onContentBegin, this);
		this._playerController.eContentPlay.addListener(this._onContentPlay, this);
		this._playerController.eContentCompleted.addListener(this._onContentCompleted, this);

		this._playerController.eAdStarted.addListener(this._onAdStarted, this);
		this._playerController.eAdFinished.addListener(this._onAdFinished, this);
		this._playerController.eAdError.addListener(this._onAdError, this);

		this._cms = new CMS();
		this._cms.setDataUrl(_configInfo.data_src);
		this._cms.eRequestCompleted.addListener(this._onCmsRequestCompleted, this);

		this._loadingEntryQueue = {};

		this.ePlayerLoaded.dispatch();
	},

	/**
	 * Catastraphic failure, some required dependencies were not loaded
	 * Send an error event back to the page
	 */
	_bootStrapFailure : function()
	{
		log("MainController", "_bootStrapFailure");
	},

	render : function(containerElement)
	{
		this._playerController.render(containerElement);
	},

	_onRendered : function()
	{
		if (!nil(_params.contentId))
		{
			this.playContentWithId(_params.contentId);
		}

		this.ePlayerRendered.dispatch();
	},

	playContentWithId : function(id, options)
	{
		log("MainController", "playContentWithId", id);
		var index = this._getNextIndexForId(id);
		var key = id + "|" + index;
		log("MainController", "playContentWithId", "key", key);
		this._loadingEntryQueue[key] = {play: true, additionalParams: options};
		this._cms.addContentId(key, options);
	},

	queueContentWithId : function(id, options)
	{
		log("MainController", "queueContentWithId", id);
		var index = this._getNextIndexForId(id);
		var key = id + "|" + index;
		log("MainController", "queueContentWithId", "key", key);
		this._loadingEntryQueue[key] = {play: false, additionalParams: options};
		this._cms.addContentId(key, options);
	},

	pause : function()
	{
		this._playerController.pause();
	},

	resume : function()
	{
		this._playerController.resume();
	},

	setVolume : function(v)
	{
		this._playerController.setVolume(v);
	},

	getVolume : function()
	{
		return this._playerController.getVolume();
	},

	mute : function()
	{
		this._playerController.mute();
	},

	unmute : function()
	{
		this._playerController.unmute();
	},

	getContentEntry:function(id)
	{
		var catalogEntry = this._cms.getContentId(id);
		return CVP.Utils.JsonConverter.encodeXmlObject(catalogEntry.getXML());
	},

	_onContentBegin : function()
	{
		this.eContentBegin.dispatch(arguments[0]);
	},

	_onContentPlay : function()
	{
		this.eContentPlay.dispatch(arguments[0]);
	},

	_onContentCompleted : function()
	{
		this.eContentCompleted.dispatch(arguments[0]);
	},

	_onAdStarted : function()
	{
		this.eAdStarted.dispatch();
	},

	_onAdFinished : function()
	{
		this.eAdFinished.dispatch();
	},

	_onAdError : function()
	{
		this.eAdError.dispatch();
	},

	_onCmsRequestCompleted : function(contentId, requestId, index, errorMsg)
	{
		log("_onCmsRequestCompleted", contentId, requestId, index, errorMsg);

		var key = requestId + "|" + index;
		var catalogEntry = this._cms.getContentId((contentId + "|" + index));

		if (this._loadingEntryQueue[key] != null)
		{
			log("onCmsRequestCompleted", contentId, this._loadingEntryQueue[key].play);
			if (this._loadingEntryQueue[key].play)
			{
				this._playerController.emptyQueue();
				this._playerController.play(catalogEntry, this._loadingEntryQueue[key].additionalParams);
			}
			else
			{
				this._playerController.queue(catalogEntry, this._loadingEntryQueue[key].additionalParams);
			}

			delete this._loadingEntryQueue[key];
		}
	},

	_getNextIndexForId : function(contentId)
	{
		var index = 0;
		for (var p in this._loadingEntryQueue)
		{
			var q = p.split("|");
				if (q[0] == contentId
					&& q[1] >= index)
				{
					index = q[1] + 1;
				}
			}
			return index;
		}
};
/**
 * A CVP wrapper for html5.
 * @name HTML5Player
 * @class A CVP wrapper for html5.
 * Here is where the player is created and its behaviors are defined.
 */
container.CVP.Players._HTML5Player = Class.extend({

	/**
	 * Initializes the player by registering its event
	 * listeners.
	 * @param {Object} options Additional optional parameters.
	 * @memberOf HTML5Player
	 */
	init : function(options)
	{
		MainController.init(options);

		MainController.ePlayerLoaded.addListener(this._onPlayerLoaded, this);
		MainController.ePlayerRendered.addListener(this._onPlayerRendered, this);
		MainController.eContentBegin.addListener(this._onContentBegin, this);
		MainController.eContentPlay.addListener(this._onContentPlay, this);
		MainController.eContentCompleted.addListener(this._onContentCompleted, this);

		MainController.eAdStarted.addListener(this._onAdStarted, this);
		MainController.eAdFinished.addListener(this._onAdFinished, this);
		MainController.eAdError.addListener(this._onAdError, this);

		this.ePlayerLoaded = new Event();
		this.ePlayerReady = new Event();
		this.eContentBegin = new Event();
		this.eContentPlay = new Event();
		this.eContentCompleted = new Event();

		this.eAdStarted = new Event();
		this.eAdFinished = new Event();
		this.eAdError = new Event();
	},

	/**
	 * Renders a player element to the page.
	 * @param {Object} containerElement The object to render.
	 * @memberOf HTML5Player
	 */
	render : function(containerElement)
	{
		MainController.render(containerElement);
	},

	/**
	 * Plays the content based on it's id.
	 * @param {String} id The content's unique string identifier.
	 * @param {Object} options Additional optional parameters.
	 * @memberOf HTML5Player
	 */
	play : function(id, options)
	{
		MainController.playContentWithId(id, options);
	},

	/**
	 * Adds the content to the queue.
	 * @param {String} id The content's unique string identifier.
	 * @param {Object} options Additional optional parameters.
	 * @memberOf HTML5Player
	 */
	queue : function(id, options)
	{
		MainController.queueContentWithId(id, options);
	},

	/**
	 * Pauses the current content.
	 * @memberOf HTML5Player
	 */
	pause : function()
	{
		MainController.pause();
	},

	/**
	 * Resumes the current content from pause.
	 * @memberOf HTML5Player
	 */
	resume : function()
	{
		MainController.resume();
	},

	/**
	 * Set the current content's volume.
	 * @param {float} v The value to set the volume to.
	 * @memberOf HTML5Player
	 */
	setVolume : function(v)
	{
		MainController.setVolume(v);
	},

	/**
	 * Returns the current content's volume.
	 * @returns The current volume.
	 * @type float
	 * @memberOf HTML5Player
	 */
	getVolume : function()
	{
		return MainController.getVolume();
	},

	/**
	 * Mutes the current content's volume.
	 * @memberOf HTML5Player
	 */
	mute : function()
	{
		MainController.mute();
	},

	/**
	 * Un-mutes the current content's volume.
	 * @memberOf HTML5Player
	 */
	unmute : function()
	{
		MainController.unmute();
	},

	/**
	 * Retrieve's the current content entry based on the
	 * provided id.
	 * @param {String} id The content's unique identifier.
	 * @returns The content's entry.
	 * @type Object
	 * @memberOf HTML5Player
	 */
	getContentEntry:function(id)
	{
		return MainController.getContentEntry(id);
	},

	_onPlayerLoaded : function()
	{
		this.ePlayerLoaded.dispatch();
	},

	_onPlayerRendered : function()
	{
		this.ePlayerReady.dispatch();
	},

	_onContentBegin : function()
	{
		this.eContentBegin.dispatch(arguments[0]);
	},

	_onContentPlay : function()
	{
		this.eContentPlay.dispatch(arguments[0]);
	},

	_onContentCompleted : function()
	{
		this.eContentCompleted.dispatch(arguments[0]);
	},

	_onAdStarted : function()
	{
		this.eAdStarted.dispatch();
	},

	_onAdFinished : function()
	{
		this.eAdFinished.dispatch();
	},

	_onAdError : function()
	{
		this.eAdError.dispatch();
	}
});

var _containerInfo = {

};

var _configInfo = {
	ads : {
		attr : {
			type : "NONE"
		}
	},
	omniture : {},
	share : {},
	embed : {}
};

})(window);

