//****************************************************************************
// Copyright (C) thePlatform for Media, Inc. All Rights Reserved.
//****************************************************************************

////// UTIL FUNCTIONS ////////////

// dynamically resize an element, for example, the category list
function tpResize(divID, height, width)
{
	var element = document.getElementById(divID);
	element.style.height = height + "px";
	element.style.width = width + "px";
}

// helper function for getting the "top" coordinate of an object
function tpGetTop(obj)
{
	result = 0;
	while (obj)
	{
		result += obj.offsetTop;
		obj = obj.offsetParent;
	}
	return result;
}

// helper function for getting the "left" coordinate of an object
function tpGetLeft(obj)
{
	result = 0;
	while(obj)
	{
		result += obj.offsetLeft;
		obj = obj.offsetParent;
	}
	return result;
}
tpThisMovie = function(movieName)
{
	var oDoc
	if (window.frame)
	{
		oDoc = frame.contentWindow.document || frame.contentDocument.document ;
	}
	else
	{
		oDoc = document
	}
	return oDoc.getElementById(movieName);
}
function tpDebug(str)
{
	if (document.getElementById("debugDiv"))
	{
		document.getElementById("debugDiv").innerHTML += str + "<br>";
	}
}

// open a new pop-up window
function tpOpenNewWindow(URLtoOpen, windowName, windowFeatures)
{
	var newWindow=window.open(URLtoOpen, windowName, windowFeatures); 
}

// handle tracking URLs
var tpTrackingImage = new Image();
function tpCallTrackingUrl(url)
{
	url = unescape(url);
	tpTrackingImage.src = url;
	for (i = 0; ((!tpTrackingImage.complete) && (i < 100000)); i++)
	{
	}
}

///// INIT tpController //////////
function tpGetUseJS() { return "true" }
function tpGetInstanceID() { return tpInstanceID; }
function tpGetCommManagerID() { return tpCommID; }
tpLogLevel = "warn";
function tpSetLogLevel(level){ tpLogLevel = level };
function tpGetLogLevel() { return tpLogLevel }
function tpGetProperties()
{
	//each pdk controller will call this to get the default properties
	var props = new Object();
	props.commManagerId = tpGetCommManagerID();
	props.instanceId = tpGetInstanceID();
	props.useJS = tpGetUseJS();
	props.registeredComponents = tpGetRegisteredIDs();
	props.logLevel = tpGetLogLevel();
	return props;
}

var registeredIDs = new Array();
function tpRegisterID(swfName)
{
	for (var i = 0; i < registeredIDs.length; i++)
	{
		if (registeredIDs[i] == swfName) return;
	}
	registeredIDs.push(swfName);
}
function tpGetRegisteredIDs()
{
	return registeredIDs;
}

// handle references to the communication manager
var tpController;
var tpCommID;
var tpBridgeID;
var tpExternalController;

//the kicks off the creation of the tpController, must be called before any pdk components are set on the page
function tpSetCommManagerID(commID, embed, commManagerUrl)
{
	// create a unique token for each set of player controls
	tpInstanceID = (new Date()).getTime() + "|" + Math.round(Math.random() * 100000000000000000);
	if (commID && embed)
	{
		//get rid of any existing commManager
		var divEl = window.document.getElementById("commManagerDiv");
		if (divEl)
		{
			divEl.parentNode.removeChild(divEl);
			divEl = null;
		}
		//create a commManager as the first element in the body
		divEl = window.document.createElement('div');
		divEl.id = "commManagerDiv";
		divEl.style.position = "absolute";
		var bodyTag = window.document.getElementsByTagName('body')[0];
		bodyTag.insertBefore(divEl, bodyTag.firstChild);
		var url = commManagerUrl ? commManagerUrl : "swf/commManager.swf"
		var so = new SWFObject(url, commID, "1", "1", "9.0.0.0");
		so.addParam("allowScriptAccess", "always");
		so.addParam("wmode", "transparent");
		so.write("commManagerDiv");
	}
	tpController = new tpControllerClass();
	tpCommID = commID;
	tpBridgeID = commID ? commID : "unknown";
	//external controller
	//tpCleanupExternal();
}

///// TPCONTROLLER CLASS /////////////

// implementation of the controller proxy in javascript
function tpControllerClass()
{
	//vars loaded at bottom
	/////// Send Messages to the rest of the app //////////////
	///////////////////////////////////////////////////////////
	
	//all communication to the communication manager happens here
	this.sendMessage = function(destination, message, skipBus)
	{
		//tpDebug("putting message on queue: " + message.name + " skipBus?" + skipBus + " canMessage?" + this.canMessage + " isLoading?" + this.isLoading);
		var sendObj = new Object();
		sendObj.message = message;
		sendObj.destination = destination;
		if (this.isLoading && !skipBus)
		{
			//these are low priority messages that should be sent only after OnPlayerLoaded is fired
			this.messageQueue.push(sendObj);
		}
		else if (!this.canMessage)
		{
			//these are high priority messages (like addEventListener or registerFunction) that usually need to be sent before OnPlayerLoaded is fired
			//but we still have to wait until after the communication manager has loaded or they'll just disappear
			this.priorityQueue.push(sendObj);
		}
		else
		{
			this.doSendMessage(sendObj);
		}
	}
	
	this.doSendMessage = function(sendObj)//private function
	{
		if (this.isShutDown) return;
		var obj = tpThisMovie(sendObj.destination);
		
		//tpDebug("sending: " + sendObj.message.name + " dest:" + sendObj.destination);
		// Flash ExternalInterface will convert any "" or " " string to null.  However,
		// in the PDK, null and "" mean different things.  So, if there are blank strings,
		// convert to a signal value, and then unconvert on the way out.
		/*for (var i=0; i<sendObj.parameters.length; i++)
		{
			var param = sendObj.parameters[i];
			if (typeof param == "string" && (param.length == 0 || param == " "))
			{
				sendObj.parameters[i] = this.blankString;
			}
		}*/
		//tpDebug("do send message: " + sendObj.message.name);
		obj.executeMessage(sendObj.message);	
	}
	
	this.checkMessageQueue = function()//private function
	{
		var len = this.messageQueue.length
		while (this.messageQueue.length > 0)
		{
			this.doSendMessage(this.messageQueue.shift());
		}
	}
	
	this.checkPriorityQueue = function()
	{
		while (this.priorityQueue.length > 0)
		{
			var sendObj = this.priorityQueue.shift();
			if (sendObj.destination == "unknown") sendObj.destination = tpBridgeID;
			this.doSendMessage(sendObj);
		}
	}
		
	this.wrapMessage = function(messageName, payload)
	{
		var comm = {globalDataType:this.getDataTypeName("CommInfo"), id:"javascript"}
		var message = {globalDataType:this.getDataTypeName("MessageInfo"), name:messageName, payload:payload, comm:comm};
		return message;
	}
	this.getDataTypeName = function(shortType)
	{
		switch(shortType)
		{
			case "ScopeInfo":return "com.theplatform.pdk.communication::ScopeInfo";
			case "MessageInfo":return "com.theplatform.pdk.communication::MessageInfo";
			case "DispatchInfo":return "com.theplatform.pdk.communication::DispatchInfo";
			case "HandlerInfo":return "com.theplatform.pdk.communication::HandlerInfo";
			case "CommInfo":return "com.theplatform.pdk.communication::CommInfo";
			case "CallInfo":return "com.theplatform.pdk.communication::CallInfo";
			case "FunctionInfo":return "com.theplatform.pdk.communication::FunctionInfo";
			case "PdkEvent":return "com.theplatform.pdk.events::PdkEvent";
			case "Clip":return "com.theplatform.pdk.data::Clip";
			case "BaseClip":return "com.theplatform.pdk.data::BaseClip";
			case "Banner":return "com.theplatform.pdk.data::Banner";
			case "Overlay":return "com.theplatform.pdk.data::Overlay";
			case "HyperLink":return "com.theplatform.pdk.data::HyperLink";
			case "TrackingUrl":return "com.theplatform.pdk.data::TrackingUrl";
			case "CustomData":return "com.theplatform.pdk.data::CustomData";
			case "Subtitles":return "com.theplatform.pdk.data::Subtitles";
			case "AdPattern":return "com.theplatform.pdk.data::AdPattern";
			case "Range":return "com.theplatform.pdk.data::Range";
			case "Sort":return "com.theplatform.pdk.data::Sort";
		}
	}
	this.createScope = function(scope)
	{
		if (scope == undefined) return this.defaultScope;
		else
		{
			scope.push("javascript");
			tpDebug("creating scopes: " + scope.toString());
			return {globalDataType:this.getDataTypeName("ScopeInfo"),controlId:"javascript",isGlobal:"true",isAny:"false",isEmpty:"false",scopeIds:scope};
		}
	}
	
	//////// handle communication to the rest of the app ///////
	////////////////////////////////////////////////////////////
	
	//register a function
	this.registerFunction = function(funcName, callback, scopes)
	{
		var scopeObj = this.createScope(scopes);
		var informComm = false;
		if (this.functions[funcName] == undefined)
		{
			this.functions[funcName] = new Object();
			informComm = true;
		}
		for (var i = 0; i < scopeObj.scopeIds.length; i++)
		{
			var s = scopeObj.scopeIds[i];
			if (s == "*") return;//can't register a scope of any
			this.functions[funcName][s] = callback;
		}
		if (informComm)
		{
			//send the registered function to the commManager
			var func = {globalDataType:this.getDataTypeName("FunctionInfo"),name:funcName,scope:scopeObj};
			var message = this.wrapMessage("registerFunction", func);
			this.sendMessage(tpBridgeID,message,true);
		}
	}
	
	this.unregisterFunction = function(funcName, scope)
	{
		var scopeObj = this.createScope(scopes);
		if (this.functions[funcName] != undefined)
		{
			var funcs = this.functions[funcName];
			for (var i = 0; i < scopeObj.scopeIds.length; i++)
			{
				var s = scopeObj.scopeIds[i];
				if (s == "*")
				{
					delete funcs;//delete them all
					break;
				}
				if (funcs[s] != undefined) delete funcs[s];//delete each scope
			}
			var funcsLeft = false;
			if (funcs != undefined)//prune the object
			{
				for (var sc in funcs)
				{
					funcsLeft = true;
					break;
				}
				if (!funcsLeft) delete this.functions[funcName];
			}
		}
		if (!funcsLeft)
		{
			var func = {globalDataType:this.getDataTypeName("FunctionInfo"),name:funcName,scope:scopeObj};
			var message = this.wrapMessage("unregisterFunction", func);
			this.sendMessage(tpBridgeID,message,true);
		}
	}
	
	this.addEventListener = function(eventName, callback, scope)
	{
		tpDebug("adding event listener: " + eventName + " callback:" + callback);
		var scopeObj = this.createScope(scope);
		var handler = {globalDataType:this.getDataTypeName("HandlerInfo"),name:eventName,handler:callback,scope:scopeObj}
		var informComm = false;
		if (this.events[eventName] == undefined)
		{
			this.events[eventName] = new Array();
			informComm = true;
		}
		var evts = this.events[eventName];
		var repeat = false;
		for (var i = 0; i < evts.length; i++)//repeats?
		{
			if (evts[i].handler == callback)
			{
				evts[i] = handler;//replace the scopes
				repeat = true;
				break;
			}
		}
		if (!repeat) evts.push(handler);
		tpDebug("how many listeners? " + evts.length);
		if (informComm)
		{
			tpDebug("informing comm of event listener");
			var message = this.wrapMessage("addEventListener", handler);
			this.sendMessage(tpBridgeID,message,true);
		}
	}
	this.removeEventListener = function(eventName, callback, scope)
	{
		if (this.events[eventName] != undefined)
		{
			var scopeObj = this.createScope(scope);
			var handler = {globalDataType:this.getDataTypeName("HandlerInfo"),name:eventName,handler:callback,scope:scopeObj}
		
			var eventArray = this.events[eventName];
			for (var i = 0; i < eventArray.length; i++)
			{
				var h = eventArray[i];
				if (h.handler == handler.handler)
				{
					eventArray = eventArray.splice(i, 1);
					break;
				}
			}
			if (eventArray.length == 0)
			{
				//no callbacks left, zap the variable
				delete this.events[eventName];
				var message = this.wrapMessage("removeEventListener", handler);
				this.sendMessage(tpBridgeID, message, true)
			}
		}
	}
	
	this.dispatchEvent = function(eventName, value, scope)
	{
		var scopeObj = this.createScope(scope);
		var evt = {globalDataType:this.getDataTypeName("PdkEvent"),type:eventName, data:value};
		var dispatch = {globalDataType:this.getDataTypeName("DispatchInfo"),evt:evt,scope:scopeObj};
		//check local events
		this.doDispatchEvent(dispatch);
		
		var message = this.wrapMessage("dispatchEvent", dispatch);
		this.sendMessage(tpBridgeID, message, true);
	}
		
	this.callFunction = function(funcName, args, scope)
	{
		var scopeObj = this.createScope(scope);
		var call = {globalDataType:this.getDataTypeName("CallInfo"),name:funcName,args:args,scope:scopeObj};
		this.doCallFunction(call);
		
		var message = this.wrapMessage("callFunction", call);
		this.sendMessage(tpBridgeID, message, true);
	}
	
	this.doDispatchEvent = function(dispatch)
	{
		tpDebug("do dispatching event: " + dispatch.evt.type);
		if (this.events[dispatch.evt.type] != undefined)
		{
			var handlers = this.events[dispatch.evt.type]
			for (var i = 0; i < handlers.length; i++)
			{
				var handler = handlers[i];
				tpDebug("checking handler? " + handler.handler)
				if (dispatch.scope.isAny)
				{
					eval(handler.handler)(dispatch.evt);
					continue;
				}
				for (var j = 0; j < handler.scope.scopeIds.length; j++)
				{
					var s = handler.scope.scopeIds[j];
					var fired = false;
					if (s == "*")
					{
						eval(handler.handler)(dispatch.evt);
						break;
					}
					for (var k = 0; k < dispatch.scope.scopeIds.length; k++)
					{
						tpDebug("running through scopes handler: " + s + " dispatch: " + dispatch.scope.scopeIds[k]);
						if (s == dispatch.scope.scopeIds[k])
						{
							fired = true;
							tpDebug("firing");
							eval(handler.handler)(dispatch.evt);
							break;
						}
					}
					if (fired) break;//go to next handler
				}
			}
		}
	}
	
	this.doCallFunction = function(call)
	{
		if (this.functions[call.name] != undefined)
		{
			//check local functions
			var funcsToCall = new Object();
			for (var i = 0; i < call.scope.scopeIds.length; i++)
			{
				var s = call.scope.scopeIds[i];
				if (this.functions[call.name][s] != undefined)
				{
					funcsToCall[this.functions[call.name][s]] = "f";//we need lookup
				}
			}
			for (var f in funcsToCall)
			{
				eval(f)(call.args);
			}
		}
	}
	
	
	
	
	//////// RECEIVE CALLS FROM AS //////////////
	////////////////////////////////////////////
	
	//all communication from the communication manager happens here
	this.receiveMessage = function(destination, message)
	{
		if (destination == "javascript")
		{
			//tpDebug("receiving message: " + message.name)
			var messStr = message.name;
			switch(messStr)
			{
				
				case "commReady":
					tpBridgeID = tpCommID;
					this.canMessage = true;
					this.checkPriorityQueue();
					break;
				case "bridgeReady":
					tpBridgeID = message.comm.id;
					this.canMessage = true;
					this.checkPriorityQueue();
					break;
				case "dispatchEvent":
					var dispatch = message.payload;
					this.receiveEvent(dispatch);
					break;
				case "callFunction":
					var call = message.payload;
					this.doCallFunction(call);
					break;
				default:
					break;
			}
		}
		else
		{
			//transfer the message to its final destination
			this.sendMessage(destination, message, true);
		}
	}
	this.receiveEvent = function(dispatch)
	{
		//tpDebug("RECIEVED:" + dispatch.evt.type)

		if (dispatch.evt.type == "OnPlayerLoaded")
		{
			this.isLoading = false;
			this.checkMessageQueue();
		}
		this.doDispatchEvent(dispatch);
	}
	
	//create a list of direct calls
	
	// PLAYER
	
	this.setRelease = function(release, replaceDefault, scope)
	{
		var args = [release, replaceDefault];
		this.callFunction("setRelease", args, scope);
	}
	this.loadRelease = function(release, replaceDefault, scope)
	{
		var args = [release, replaceDefault];
		this.callFunction("loadRelease", args, scope);
	}
	this.loadReleaseURL = function(releaseURL, replaceDefault, scope)
	{
		var args = [releaseURL, replaceDefault];
		this.callFunction("loadReleaseURL", args, scope);
	}
	this.setReleaseURL = function(url, replaceDefault, scope)
	{
		var args = [url, replaceDefault];
		this.callFunction("setReleaseURL", args, scope);
	}
	this.loadSmil = function(smil, replaceDefault, scope)
	{
		if (replaceDefault == undefined) replaceDefault = true;
		this.callFunction("loadSmil", [smil, replaceDefault], scope);
	}
	this.setSmil = function(smil, scope)
	{
		this.callFunction("setSmil", [smil], scope);
	}
	this.resetPlayer = function(scope)
	{
		this.callFunction("resetPlayer", [], scope);
	}
	this.setCurrentReleaseList = function(id, scope)
	{
		var args = [id];
		this.callFunction("setCurrentReleaseList", args, scope);
	}
	this.seekToPosition = function(position, scope)
	{
		var args = [position];
		this.callFunction("seekToPosition", args, scope);
	}
	this.seekToPercentage = function(percent, scope)
	{
		var args = [percent];
		this.callFunction("seekToPercentage", args, scope);
	}
	this.nextClip = function(scope)
	{
		var args = ["javascript"];
		this.callFunction("nextClip", args, scope);
	}
	this.previousClip = function(scope)
	{
		var args = [];
		this.callFunction("previousClip", args, scope);
	}
	this.mute = function(muted, scope)
	{
		var args = [muted];
		this.callFunction("mute", args, scope);
	}
	this.pause = function(paused, scope)
	{
		var args = [paused];
		this.callFunction("pause", args, scope);
	}
	this.setPreviewImageUrl = function(url, scope)
	{
		var args = [url];
		this.callFunction("setPreviewImageUrl", args, scope);
	}
	this.showFullScreen = function(isFullScreen, scope)
	{
		var args = [isFullScreen];
		this.callFunction("showFullScreen", args, scope);
	}
	this.showEmailForm = function(visible, scope)
	{
		var args = [visible];
		this.callFunction("showEmailForm", args, scope);
	}
	this.showLinkForm = function(visible, scope)
	{
		var args = [visible];
		this.callFunction("showLinkForm", args, scope);
	}
	this.trace = function(str, className, level)
	{
		var args = [str, className, level];
		this.callFunction("trace", args, null); 
	}
	this.useDefaultPlayOverlay = function(useDefault, scope)
	{
		var args = [useDefault];
		this.callFunction("useDefaultPlayOverlay", args, scope);
	}
	this.getUseDefaultPlayOverlay = function(scope)
	{
		var args = [];
		this.callFunction("getUseDefaultPlayOverlay", args, scope);
	}
	this.useDefaultLinkForm = function(useDefault, scope)
	{
		var args = [useDefault];
		this.callFunction("useDefaultLinkForm", args, scope);
	}
	this.useDefaultEmailForm = function(useDefault, scope)
	{
		var args = [useDefault];
		this.callFunction("useDefaultEmailForm", args, scope);
	}
	this.getSubtitleLanguage = function(requestor, scope)
	{
		var args = [requestor];
		this.callFunction("getSubtitleLanguage", args, scope);
	}
	this.clickPlayButton = function(scope)
	{
		var args = [];
		this.callFunction("clickPlayButton", args, scope);
	}
	this.disablePlayerControls = function(disable, exceptions, scope)
	{
		var args = [disable, exceptions];
		this.callFunction("disablePlayerControls", args, scope);
	}
	this.setSubtitleLanguage = function(language, scope)
	{
		var args = [language];
		this.callFunction("setSubtitleLanguage", args, scope);
	}	
	this.getPlayerVariables = function(names, scope)
	{
		var args = [names];
		this.callFunction("getPlayerVariables", args, scope);	
	}
	this.setVolume = function(volume, scope)
	{
		var args = [volume];
		this.callFunction("setVolume", args, scope);
	}
	
	// RELEASE MODEL
	
	this.refreshReleaseModel = function(category, search, sort, range, params, secondaryParams, scope)
	{
		if (sort) sort.globalDataType = this.getDataTypeName("Sort");
		if (range) range.globalDataType = this.getDataTypeName("Range");
		var args = [category, search, sort, range, params, secondaryParams];
		this.callFunction("refreshReleaseModel", args, scope);
	}
	
	// CATEGORY MODEL
	
	this.refreshCategoryModel = function(params, scope)
	{
		var args = [params];
		this.callFunction("refreshCategoryModel", args, scope);
	}
	
	// NAVIGATION
	
	this.nextRange = function(scope)
	{
		var args = [];
		this.callFunction("nextRange", args, scope);
	}
	this.previousRange = function(scope)
	{
		var args = [];
		this.callFunction("previousRange", args, scope);
	}
	
	
	// CLIP INFO
	
	this.setClipInfo = function(clip, isDefault, scope)
	{
		clip = this.modClip(clip);
		var args = [clip, isDefault];
		this.callFunction("setClipInfo", args, scope);
	}
	
	// CATEGORY LIST

	this.clearCategorySelection = function(scope) 
	{		
		var args = [];
		this.callFunction("clearCategorySelection", args, scope);
	}

	// RELEASE LIST
	
	this.suspendPlayAll = function(suspend, scope) 
	{		
		var args = [suspend];
		this.callFunction("suspendPlayAll", args, scope);
	}
	this.playNext = function(wrapAround, naturalEnd, scope) 
	{		
		var args = [wrapAround, naturalEnd];
		this.callFunction("playNext", args, scope);
	}
	this.playPrevious = function(wrapAround, scope) 
	{		
		var args = [wrapAround];
		this.callFunction("playPrevious", args, scope);
	}
	
	// GENERAL
	this.modClip = function(clip)
	{
		if (clip)
		{
			//set the class names correctly for casting in as3.
			clip.globalDataType = this.getDataTypeName("Clip");
			var baseClip = clip.baseClip;
			if (!baseClip) baseClip = new Object();
			if (clip.banners) baseClip.banners = clip.banners;
			if (clip.overlays) baseClip.overlays = clip.overlays;
			clip.baseClip = this.modBaseClip(baseClip);
			if (clip.chapter) clip.chapter.globalDataType = this.getDataTypeName("Chapter");
		}
		return clip;
	}
	this.modBaseClip = function(baseClip)
	{
		if (!baseClip) baseClip = new Object();
		baseClip.globalDataType = this.getDataTypeName("BaseClip");
		if (baseClip.moreInfo)
		{
			baseClip.moreInfo.globalDataType = this.getDataTypeName("HyperLink");
			if (baseClip.moreInfo.clickTrackingUrls) baseClip.moreInfo.clickTrackingUrls = this.modTracking(baseClip.moreInfo.clickTrackingUrls);
		}
		if (baseClip.banners)
		{
			for (var i = 0; i < baseClip.banners.length; i++)
			{
				baseClip.banners[i].globalDataType = this.getDataTypeName("Banner");
				if (baseClip.banners[i].clickTrackingUrls) baseClip.banners[i].clickTrackingUrls = this.modTracking(baseClip.banners[i].clickTrackingUrls)
			}
		}
		if (baseClip.overlays)
		{
			for (var i = 0; i < baseClip.overlays.length; i++)
			{
				baseClip.overlays[i].globalDataType = this.getDataTypeName("Overlay");
				if (baseClip.overlays[i].clickTrackingUrls) baseClip.overlays[i].clickTrackingUrls = this.modTracking(baseClip.overlays[i].clickTrackingUrls)
			}
		}
		if (baseClip.availableSubtitles)
		{
			for (var i = 0; i < baseClip.availableSubtitles; i++)
			{
				baseClip.availableSubtitles[i].globalDataType = this.getDataTypeName("Subtitles");
			}
		}
		if (baseClip.adPattern) baseClip.adPattern.globalDataType = this.getDataTypeName("AdPattern");
		if (baseClip.trackingURLs) baseClip.trackingURLs = this.modTracking(baseClip.trackingURLs);
		if (baseClip.contentCustomData) baseClip.contentCustomData.globalDataType = this.getDataTypeName("CustomData");
		if (baseClip.ownerCustomData) baseClip.ownerCustomData.globalDataType = this.getDataTypeName("CustomData");
		if (baseClip.outletCustomData) baseClip.outletCustomData.globalDataType = this.getDataTypeName("CustomData");
		return baseClip;
	}
	this.modTracking = function(trackingUrls)
	{
		for (var i = 0;i < trackingUrls.length;i++)
		{
			trackingUrls.globalDataType = this.getDataTypeName("TrackingUrl")
		}
		return trackingUrls
	}
	this.shutDown = function()
	{
		var args = [];
		this.callFunction("shutDown", args, ["*"]);
		this.isShutDown = true;//prevent any more messages		
	}
	//vars initialized
	this.events = new Object();
	this.functions = new Object();
	this.isLoading = true;
	this.canMessage = false;
	this.messageQueue = new Array();
	this.priorityQueue = new Array();
	this.sendQueue = new Array();//yet another queue for timing externalInterface calls
	this.isSending = false;
	this.sendInterval;
	this.shutdownIDs;//array to keep all the controller ids for shutdown
	this.isShutDown = false;
	this.blankString = "__blank_string__";
	this.defaultScope = {globalDataType:this.getDataTypeName("ScopeInfo"),controlId:"javascript",isGlobal:true,isAny:false,isEmpty:false,scopeIds:["javascript","default"]};
	
}

function tpReceiveMessage(destination, message)
{
	//tpDebug("---message received---" + message.name + " dest:" + destination)
	tpController.receiveMessage(destination, message);
}


//functions for controlling external players

var tpHolderName = "pdkHolder";
var tpExternalJS;

function tpSetPlayerIDForExternal(playerName){};//no longer needed

function tpSetHolderIDForExternal(holderName)//needed
{
	tpHolderName = holderName;
}

function tpLoadExternalMediaJS()
{
	tpExternalJS = tpLoadExternalMediaJS.arguments;
	
	for (var i = 0; i < tpExternalJS.length; i++)
	{
		tpLoadScript(tpExternalJS[i]);
	}
}

function tpCleanupExternal()
{
	if (tpExternalJS)//if there's no external js, then nothing was loaded in
	{
		var scripts = window.document.getElementsByTagName('head')[0].getElementsByTagName('script');
		for (var i = 0; i < scripts.length;i++)
		{
			for (var j = 0; j < tpExternalJS.length; j++)
			{
				if (scripts[i].src == tpExternalJS[j])
				{
					window.document.getElementsByTagName('head')[0].removeChild(scripts[i]);
					break;
				}
			}
		}
		tpExternalJS.length = 0;
	}
	if (tpExternalController)
	{
		tpExternalController.cleanup();
	}
}


/////////////////////////////////////////////////////////////////////

tpScriptLoader = new ScriptLoader();

// called from flash via ExternalInterface
function tpLoadJScript(scriptFile, callback, id, atts)
{
	tpScriptLoader.addScript(scriptFile, callback, id, atts);
}

// need to wrap method to fix scoping issue on callback
function callbackDispatcher(loadObj) { tpScriptLoader.callbackDispatcher(loadObj) }
function invokeCallbacks(loadObj) { tpScriptLoader.invokeCallbacks() }

/////////////////////////////////////////////////////////////////////
//					L O A D   O B J E C T
/////////////////////////////////////////////////////////////////////
function LoadObj(scriptFile, callback, id, atts)
{
	this.script = scriptFile;
	this.callback = callback;
	this.id = id;
	this.atts = atts;
}

/////////////////////////////////////////////////////////////////////
//					S C R I P T   L O A D E R
/////////////////////////////////////////////////////////////////////


// constructor
function ScriptLoader()
{
	// queued up for loading scripts
	this.scriptQueue = new Array();
	
	// queued up for invoking callbacks
	this.callbackQueue = new Array();
}

/////////////////////////////////////////////////////////////////////
ScriptLoader.prototype.addScript = function(scriptFile, callback, id, atts)
{
	var loadObj = new LoadObj( scriptFile, callback, id, atts );
	this.scriptQueue.push(loadObj);
	
	// if the queue was empty, we need to kick
	// off the queue processing again.
	
	if (this.scriptQueue.length == 1)
		this.checkScriptQueue();
}

/////////////////////////////////////////////////////////////////////
ScriptLoader.prototype.checkScriptQueue = function()
{
	if (this.scriptQueue.length)
	{
		var loadObj = this.scriptQueue.shift();
		this.loadScript(loadObj);
	}
	else
	{
		// as a timing precaution, we wait until the queue
		// empties out before we invoke callbacks
		interval_id = setInterval("invokeCallbacks()",100) // more timing precautions :-/
		//this.invokeCallbacks();
	}
}
	
/////////////////////////////////////////////////////////////////////
ScriptLoader.prototype.callbackDispatcher = function(loadObj)
{
	for (var i in this.callbackQueue)
	{
		if (this.callbackQueue[i] == loadObj)
		{
			this.checkScriptQueue();
			return;
		}
	}
	this.callbackQueue.push(loadObj);
	this.checkScriptQueue();
}

/////////////////////////////////////////////////////////////////////
ScriptLoader.prototype.invokeCallbacks = function()
{
	clearInterval(interval_id);
	while (this.callbackQueue.length)
	{
		var loadObj = this.callbackQueue.shift();
		eval(loadObj.callback)(loadObj.script);
	}
}

/////////////////////////////////////////////////////////////////////
ScriptLoader.prototype.loadScript = function(loadObj)
{
	var scriptFilename = loadObj.script;
	var callbackFunction = loadObj.callback;
	var id = loadObj.id;
	var atts = loadObj.atts;
	
	// Create script element and set it to load the requested script
	var scriptEl = window.document.createElement('script');
	scriptEl.charset = "utf-8";
	if (id) scriptEl.id = id;
	scriptEl.type = "text/javascript";
	//scriptEl.defer = true;
	if (atts)
	{
		for (var i = 0; i < atts.length; i++)
			scriptEl.setAttribute(atts[i].att, atts[i].value);
	}
	scriptEl.src = scriptFilename;
	
	if (callbackFunction)
	{
		// Function to be called when script has finished loading
		var _onFinished = function(_loadObj, _callback)
		{
			// Invoke the callback function
			_callback(_loadObj)

			// Clean up event handlers
			this.onreadystatechange = null;
			this.onload = null;
			this.onerror = null;
		};

		// Set callback for IE
		// In defiance of MSDN documentation IE's script object has no onload handler
		scriptEl.onreadystatechange = function()
		{
			_onFinished(loadObj, callbackDispatcher);
		};

		// Set callback for W3C-compatible browsers
		scriptEl.onload = function()
		{
			_onFinished(loadObj, callbackDispatcher);
		};
		// Set another callback for W3C-compatible browsers
		// since onreadystatechange for IE also fires in case of an error
		scriptEl.onerror = function()
		{
			_onFinished(loadObj, callbackDispatcher);
		};
	}

	// Add script element to the document
	window.document.getElementsByTagName('head')[0].appendChild(scriptEl);
}

/////////////////////////////////////////////////////////////////////
// ORIGINAL LOADSCRIPT - USED BY MOVENETWORKS 
/////////////////////////////////////////////////////////////////////
function tpLoadScript(scriptFilename, callbackFunction, id, atts)
{
   	// Create script element and set it to load the requested script
   	var scriptEl = window.document.createElement('script');
   	scriptEl.charset = "utf-8";
   	if (id) scriptEl.id = id;
   	scriptEl.type = "text/javascript";
   	//scriptEl.defer = true;
   	if (atts)
   	{
   		for (var i = 0; i < atts.length; i++)
   		{
   			scriptEl.setAttribute(atts[i].att, atts[i].value);
   		}
   	}
   	scriptEl.src = scriptFilename;
   	if (callbackFunction)
   	{
   		// Function to be called when script has finished loading
   		var _onFinished = function(_callbackFunction, _scriptFilename)
   		{
   			// Invoke the callback function
   			_callbackFunction(_scriptFilename);
   
   			// Clean up event handlers
   			this.onreadystatechange = null;
   			this.onload = null;
   			this.onerror = null;
   		};
   
   		// Set callback for IE
   		// In defiance of MSDN documentation IE's script object has no onload handler
   		scriptEl.onreadystatechange = function()
   		{
   			_onFinished(callbackFunction, scriptFilename);
   		};
   
   		// Set callback for W3C-compatible browsers
   		scriptEl.onload = function()
   		{
   			_onFinished(callbackFunction, scriptFilename);
   		};
   		// Set another callback for W3C-compatible browsers
   		// since onreadystatechange for IE also fires in case of an error
   		scriptEl.onerror = function()
   		{
   			_onFinished(callbackFunction, scriptFilename);
   		};
   	}
   
   	// Add script element to the document
   	window.document.getElementsByTagName('head')[0].appendChild(scriptEl);
}

/////////////////////////////////////////////////////////////////////



//constructor for tpExternalControl
function tpExternalControllerClass()
{
	this.playerTypes = new Object();//keep a lookup of classes
	this.extPlayers = new Object();//keep the instances here
	
	this.registerExternalPlayer = function(type, playerClass)
	{
		this.playerTypes[type] = playerClass;//keep the class as a string
	}
	
	this.routeMessage = function(swfId, controllerId, streamType, funcName, args)
	{
		var curController = this.extPlayers[controllerId];//see if we have the existing controller lookup
		if (!curController) curController = this.extPlayers[controllerId] = {};//make a lookup for the controller
		var curPlayer = curController[streamType];//now see if we have the actual player object instantiated
		if (!curPlayer)
		{
			var playerClass = this.playerTypes[streamType];
			if (!playerClass) return; //we don't have the correct stream type on hand
			curPlayer = eval("new " + playerClass + "('" + swfId + "', '" + controllerId + "');");//create a new instance of the class
			if (!curPlayer) return;//abort here, too
			curController[streamType] = curPlayer;
		}
		curPlayer[funcName](args);
	}
	
	this.returnMessage = function(swfId, controllerId, funcName, args)
	{
		var obj = tpThisMovie(swfId);
		obj.receiveJSMessage(controllerId, funcName, args);
	}
	
	this.cleanup = function()
	{
		for (var controllerId in this.extPlayers)
		{
			var players = this.extPlayers[controllerId];
			for (var player in players)
			{
				players[player].cleanup();
				delete players[player];
			}
			delete this.extPlayers[controllerId];
		}
	}
}

function tpExternalMessage(swfId, controllerId, streamType, funcName, args)
{
	tpExternalController.routeMessage(swfId, controllerId, streamType, funcName, args);
}

//build this without needing the commManager
tpExternalController = new tpExternalControllerClass();

function tpShowAlert(alertCode) 
{
	switch(alertCode)
	{
		case "FULLSCREEN_DISABLED":
		//if (deconcept.SWFObjectUtil.getPlayerVersion().major < 9)
		alert("Full screen is only available with Flash 9 or later")
		break;
	}
}

