/**
@author steve.collings@bbc.co.uk
@requires http://www.bbc.co.uk/glow/gloader.js
*/

/**
@name cbbc.emp
@namespace
@description EMP embed and preview functions
	Automagically adds a preview overlay to the first IMG within all elements with class "emp-preview"
	
	TODO:
		- DONE get look of the preview from config xml
		- DONE possibly don't feature any buttons on the preview image so we don't need so many different images
		- DONE don't restrict embed settings to the current ones as these will change.
			DONE will require getting settings from classnames by cycling through them
		- DONE add popout functionality
		- add preview for audio + radio (works but needs an image to replace when maybe that shouldn't be necessary)
		- multilingual support (would require additional pngs thanks to ie6 + background position of 'click to play')
		- add guidance support
		- make animated .gif of loading circles
*/
(function()
{
	if ( window.cbbc != undefined && cbbc.emp != undefined )
		return;
	
	// consts
	var PNG_ASSETS = "/radio2/images/emp/emp_preview.png"
	,	MIN_FLASH_VERSION = "7.0.0"
		// list of scripts required to embed emp. may change.
	,	EMP_SCRIPT_PATHS = [
			"http://www.bbc.co.uk/emp/swfobject.js",
			"http://www.bbc.co.uk/emp/embed.js"
		]
		// conditions to test if emp scripts are present. may change.
	,	IS_EMP_SCRIPTS_LOADED = function()
		{
			return ( !window.embeddedMedia || !embeddedMedia.Player || !embeddedMedia.console ) ? false : true;
		}
		// height of emp toolbar. may change
	,	EMP_TOOLBAR_H = 35;
	
	// private vars
	var glow
	,	previewCount = 0			// count of items replaced by preview
	,	embedCount = 0				// count of items replaced by embed
	,	previewStack = []; 			// stores attributes passed to cbbc.emp.preview before glow is loaded
	
	// set up namespaces
	if ( window.cbbc == undefined )
		window.cbbc = {};
	cbbc.emp = {};
	
	
	/**
	@name hasRequiredFlashVersion
	@private
	@function
	@description Returns true if player version >= MIN_FLASH_VERSION. From swfobject 2.0
		TODO use new glow.embed.Flash.version() instead
	
	@returns {Boolean}
	*/
	var hasRequiredFlashVersion = (function()
	{
		var UNDEF = "undefined",
			OBJECT = "object",
			SHOCKWAVE_FLASH = "Shockwave Flash",
			SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
			FLASH_MIME_TYPE = "application/x-shockwave-flash",
			win = window,
			doc = document,
			nav = navigator,
			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) {
				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] = /r/.test(d) ? parseInt(d.replace(/^.*r(.*)$/, "$1"), 10) : 0;
			}
		}
		else if (typeof win.ActiveXObject != UNDEF) {
			var a = null, fp6Crash = false;
			try {
				a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".7");
			}
			catch(e) {
				try { 
					a = new ActiveXObject(SHOCKWAVE_FLASH_AX + ".6");
					playerVersion = [6,0,21];
					a.AllowScriptAccess = "always";  // Introduced in fp6.0.47
				}
				catch(e) {
					if (playerVersion[0] == 6) {
						fp6Crash = true;
					}
				}
				if (!fp6Crash) {
					try {
						a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
					}
					catch(e) {}
				}
			}
			if (!fp6Crash && a) { // a will return null when ActiveX is disabled
				try {
					d = a.GetVariable("$version");  // Will crash fp6.0.21/23/29
					if (d) {
						d = d.split(" ")[1].split(",");
						playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
					}
				}
				catch(e) {}
			}
		}
		
		var pv = playerVersion, v = MIN_FLASH_VERSION.split(".");
		v[0] = parseInt(v[0], 10);
		v[1] = parseInt(v[1], 10);
		v[2] = parseInt(v[2], 10);
		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;
	})();
	
	
	/**
	@name executeWhen
	@private
	@function
	@description Executes a function when another function returns true.
		Polls checkFunc every 50ms
	
	@param {function}  			[func]			Function to execute when condition is met
	@param {function|object}  	[funcScope]		Context to run func in
	@param {object}  			[funcArgs]		Arguments to pass to func
	@param {function}  			[checkFunc]		Returns true when condition is met
	
	*/
	var executeWhen = function(func, funcScope, funcArgs, checkFunc)
	{
		var intRef;
		
		function executeCheck()	{
			if ( !checkFunc() )
			{
				if (!intRef)
					intRef = setInterval(executeCheck,50);
				return;
			}
			clearInterval(intRef);
			func.apply(funcScope, funcArgs); // only reaches here when checkFunc is satisfied.
		}
		executeCheck();
	};
	
	
	/**
	@name getDataFromClassNames
	@private
	@function
	@description Returns hash of settings from the classNames of element el that begin with classPrefix.
		IE: class="prefix-setting1-value1 prefix-setting2-value2" returns:
		{
			setting1: value1,
			setting2: value2
		}
	
	@returns {object}
	
	@param {HTMLElement}  		[el]			Element to look at classes
	@param {string}  			[classPrefix]	Prefix on classes that are data
	
	*/
	var getDataFromClassNames = function(el, classPrefix)
	{
		var classes = el.className.split(" "),
			data = {},
			numHyphensInPrefix = classPrefix.split("-").length-1,
			setting,
			val;
		for ( var i=0, len=classes.length, curClass; i<len; i++ )
		{
			curClass = glow.lang.trim(classes[i]);
			if ( curClass.indexOf(classPrefix) === 0 )
			{
				setting = curClass.split("-").slice(numHyphensInPrefix);
				if ( setting.length > 1 )
				{
					val = setting[1];
					var testBoolean = glow.lang.trim(val.toLowerCase());
					if ( testBoolean == "true" )
						val = true;
					else if ( testBoolean == "false" )
						val = false;
					data[setting[0]] = val;
				}
			}
		}
		return data;
	}
	
	
	/**
	@name getAbsoluteUrl
	@private
	@function
	@description Returns an absolute URL using the host of the current page if required
	@returns {string}
	
	@param {string}  			[url]			URL to fix
	
	*/
	var getAbsoluteUrl = function(url)
	{
		if ( url.indexOf("://") < 0 )
		{
			if ( url.indexOf("//") !== 0 )
				url = window.location.host + ( url.indexOf("/") < 0 ? "/" : "" ) + url;
			url = window.location.protocol + "//" + url;
		}
		return url;
	}
	
	
	/**
	@name splitUrlToParts
	@private
	@function
	@description See: http://blog.stevenlevithan.com/archives/parseuri-split-url 
	@returns {object}
	
	@param {string}  			[url]			URL to fix
	
	*/
	var splitUrlToParts = function(sourceUri)
	{
		var uriPartNames = ["source","protocol","authority","domain","port","path","directoryPath","fileName","query","anchor"],
			uriParts = new RegExp("^(?:([^:/?#.]+):)?(?://)?(([^:/?#]*)(?::(\\d*))?)((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[\\?#]|$)))*/?)?([^?#/]*))?(?:\\?([^#]*))?(?:#(.*))?").exec(sourceUri),
			uri = {};
		
		for(var i = 0; i < 10; i++){
			uri[uriPartNames[i]] = (uriParts[i] ? uriParts[i] : "");
		}
		if(uri.directoryPath.length > 0){
			uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/");
		}
		
		return uri;
	}
	
	
	
	/* ------------------------------------------------------------- */
	
	/**
	@name cbbc.emp.canEmbed
	@function
	@description True if browser has the required flash version for the EMP (currently 7.0.0) otherwise false
	@returns {Boolean}
	*/
	cbbc.emp.canEmbed = function()
	{
		return hasRequiredFlashVersion;
	};
	
	
	
	/**
	@name cbbc.emp.hasEmbeded
	@function
	@description True if cbbc.emp.embed has already been called on this page
	@returns {Boolean}
	*/
	cbbc.emp.hasEmbeded = function()
	{
		return embedCount > 0;
	};
	
	
	/**
	@name cbbc.emp.embed
	@function
	@description Embeds EMP.
		
		All required EMP scripts are downloaded automagically.
		Can be called before DOM is ready.
	
	@param {selector|NodeList}  [empEl]	Contents are replaced with EMP swf
	
	@param {Object} [opts]
		Any of the following as properties of an object. width, height + playlistUrl are required.
		See: http://www.bbc.co.uk/emp/docs/guides/configurationGuide.html
		
		@param {Number}	[opts.width]	width of EMP
		@param {Number}	[opts.height]	height of EMP
		@param {String}	[opts.playlistUrl="http://..."]		A full URL to a playlist XML file
			If not fully qualified (IE: /cbbc/play.xml"), gets from host current page is on.
		@param {String}	[opts.configUrl="http://..."]		A full URL to a configuration XML file.
		@param {String}	[opts.skin="silver"]				User interface colour scheme
			"black" or "silver"
		@param {String}	[opts.displayMode="standard"]		Determines appearance of player.
			Must be set to audio when embedding the 106px high audio-only player. See sizes	standard / audio / radioConsole
		@param {String}	[opts.suppressItemKind=""]			Do not show item from playlist.
			Comma separated list e.g. programme, ident
		@param {String}	[opts.suppressRelatedLinks=""]		Don't show the two 'more like this' options at the end of a clip
		@param {Boolean}	[opts.showFullScreenCta=false]	Show the message below the click to play button to go full screen.
		@param {Boolean}	[opts.showPopoutButton=false]	Show the button to launch current player in a pop-out window.
		@param {Boolean}	[opts.showShareButton=false]	Show the share button
	
	@example
		cbbc.emp.embed("#emp-replace", {
			width:512,
			height:323,
			playlistUrl: "http://www.bbc.co.uk/..."
		});
	*/
	cbbc.emp.embed = function(empEl, opts)
	{
		if ( !cbbc.emp.canEmbed() || empEl == undefined )
			return;
		
		if ( !glow.ready )
		{
			executeWhen(cbbc.emp.embed, cbbc.emp.embed, arguments, function() { return !!glow.ready; } );
			return;
		}
		if ( !IS_EMP_SCRIPTS_LOADED() )
		{
			for ( var i=0, len=EMP_SCRIPT_PATHS.length; i<len; i++ )
			{
				glow.net.loadScript(EMP_SCRIPT_PATHS[i], {
					useCache: true
				});
			}
			executeWhen(cbbc.emp.embed, cbbc.emp.embed, arguments, IS_EMP_SCRIPTS_LOADED );
			return;
		}
		var $ = glow.dom.get;
		empEl = $(empEl);
		
		if ( !empEl.length )
			return;
		
		opts = opts || {};
		
		// replace each element seperately
		if ( empEl.length > 1 ) {
			empEl.each(function(i) {
				cbbc.emp.embed(empEl.slice(i,i+1), opts);
			});
			return;
		}
		
		if ( !empEl.hasAttr("id") )
			empEl.attr("id", "emp-embed" + embedCount);
		embedCount++;
		
		var emp = new bbc.Emp();
		emp.setDomId(empEl.attr("id"));
		emp.setWidth(opts.width);  
		emp.setHeight(opts.height);
		emp.setPlaylist(getAbsoluteUrl(opts.playlistUrl));
		
		for ( var i in opts )
		{
			if ( opts[i] != null )
			{
				switch ( i )
				{
					case "width":
					case "height":
					case "playlistUrl":
						break;
					case "configUrl":
						emp.setConfig(getAbsoluteUrl(opts.configUrl)); break;
					default:
						emp.set("config_settings_"+i, opts[i]);
				}
			}
		}
		emp.write();
	};
	
	/**
	@name cbbc.emp.preview
	@function
	@description Adds overlay and click handler to an image to make it look like the EMP. EMP proper is launched when image is clicked.
		
		All required EMP scripts are downloaded automagically. The preview + subsequent embed can be configured via tha opts parameter,
		through classNames on the elements to be replaced and through key/value pairs on the query string of the src of the IMG
		element to be replaced.
		
		Uses the width + height of the IMG if non is set with opts, classNames or Img src query params
		
		Can be called before DOM has loaded as calls are cached + run when everything's ready.
	
	@param {selector|NodeList} [empEl]	HTML Elements containing IMG to add overlay to
	
	@param {Object} [opts]
		Any of the following as properties of an object.
		See: http://www.bbc.co.uk/emp/docs/guides/configurationGuide.html
		
		@param {Number}		[opts.width]						width of EMP
		@param {Number}		[opts.height]						height of EMP
		@param {String}		[opts.playlistUrl="http://..."]		A full URL to a playlist XML file
			If not fully qualified (IE: /cbbc/play.xml"), gets from host current page is on.
		@param {String}		[opts.configUrl="http://..."]		A full URL to a configuration XML file.
		@param {Boolean}	[opts.useConfigXml=false]			Use config XML to get look of preview overlay
		@param {Boolean}	[opts.embedIfCached=true]			Embed EMP directly if embed has already been called on page
		@param {String}		[opts.skin="silver"]				User interface colour scheme
			"black" or "silver"
		@param {String}		[opts.displayMode="standard"]		Determines appearance of player.
			Must be set to audio when embedding the 106px high audio-only player. See sizes	standard / audio / radioConsole
		@param {String}		[opts.suppressItemKind=""]			Do not show item from playlist.
			Comma separated list e.g. programme, ident
		@param {Boolean}	[opts.suppressRelatedLinks=false]	Don't show the two 'more like this' options at the end of a clip
		@param {Boolean}	[opts.showPopoutButton=true]		Show the button to launch current player in a pop-out window.
		@param {Boolean}	[opts.showFullScreenCta=false]		Show the message below the click to play button to go full screen.
		@param {Boolean}	[opts.showShareButton=false]		Show the share button
	
	@example
		cbbc.emp.preview("#emp-replace", {
			width:512,
			height:323,
			playlistUrl: "http://www.bbc.co.uk/..."
		});
	*/
	cbbc.emp.preview = function(empEl, opts)
	{
		if ( !cbbc.emp.canEmbed() || empEl == undefined )
			return;
		
		opts = opts || {};
		
		// glow not arrived yet, store the request to do it later
		if ( !glow || !glow.isReady )
		{
			previewStack.push([empEl, opts]);
			return;
		}
		
		var $ = glow.dom.get;
		
		empEl = $(empEl);
		
		if ( !empEl.length )
			return;
		
		// replace each element seperately
		if ( empEl.length > 1 ) {
			empEl.each(function(i) {
				cbbc.emp.preview(empEl.slice(i,i+1), opts);
			});
			return;
		}
		
		// if not an image, get the first img child
		var imgEl = empEl;
		if ( !imgEl.is("img")) {
			imgEl = imgEl.get("img");
			if ( !imgEl.length ) {
				return;
			}
			imgEl = imgEl.slice(0, 1);
		}
		
		var defaultOpts =
		{
			width: parseInt(imgEl.attr("width")),
			height: parseInt(imgEl.attr("height")) + EMP_TOOLBAR_H,
			skin: "silver",
			displayMode: "standard",
			useConfigXml: false,
			showPopoutButton: true,
			embedIfCached: true
		}
		
		var src = imgEl.attr("src"),
			srcOpts,
			configUrl;
		
		// get params from query string of img src url
		if ( src.indexOf("?") >= 0 )
		{
			var query = src.substr(src.indexOf("?") + 1);
			query = query.split("&amp;").join("&");
			srcOpts = glow.data.decodeUrl(query);
		}
		
		// get settings from config xml
		// don't need to do this if player is already downloaded, this feature hasn't been enabled or we've already done it
		if ( !cbbc.emp.hasEmbeded() && opts.useConfigXml != false && opts.useConfigXml != "false" && opts.useConfigXml != undefined )
		{
			configUrl = opts.configUrl || unescape(srcOpts.configUrl);
			
			if ( !!configUrl )
			{
				// if configURL is absolute, need to adjust it so it's from the same host
				var o = splitUrlToParts(configUrl);
				if ( o.domain != "" && o.domain != window.location.host ) {
					configUrl = o.protocol + "://" + window.location.host + o.path
				}
				opts.useConfigXml = false;
				glow.net.get(configUrl,
				{
					async: true,
					useCache: true,
					onError: function(r)
					{
						cbbc.emp.preview(empEl, opts);
					},
					onLoad: function(response)
					{
						var settings = glow.dom.get(response.xml()).get("settings");
						settings.children().each(function(i, kids) {
							var setting = kids.slice(i, i+1),
								key = setting.attr("name");
							// passed in opts override ones from config xml;
							opts[key] = ( opts[key] != undefined ) ? opts[key] : setting.text();
						});
						cbbc.emp.preview(empEl, opts);
					}
				});
				return;
			}
			
		}
		
		// get settings from the css classes of the element to be replaced
		var theOpts = glow.lang.apply(defaultOpts, getDataFromClassNames(empEl[0], "emp-"));
		
		// get settings from the query string of the placeholder imgEl src attribute
		if ( srcOpts ) {
			theOpts = glow.lang.apply(theOpts, srcOpts);
		}
		
		if ( !!theOpts.playlistUrl ) {
			theOpts.playlistUrl = unescape(theOpts.playlistUrl);
		}
		if ( !!theOpts.configUrl ) {
			theOpts.configUrl = unescape(theOpts.configUrl);
		}
		
		// finally, the options passed in manually override everything
		theOpts = glow.lang.apply(theOpts, opts);
		
		//  can't do owt without a playlist
		if ( !theOpts.playlistUrl ) {
			return;
		}
		
		theOpts.playlistUrl = getAbsoluteUrl(theOpts.playlistUrl);
		if ( !!theOpts.configUrl ) {
			theOpts.configUrl = getAbsoluteUrl(theOpts.configUrl);
		}
		
		// if emp already downloaded. go straight to embed
		if ( cbbc.emp.hasEmbeded() ) {
			cbbc.emp.embed(empEl, theOpts);
			return;
		}
		
		// create the preview
		var id = "emp-preview" + previewCount++,
			create = glow.dom.create,
			holder = create("<span id='" + id + "'></span>"),
			image = create("<span></span>"),
			toolbar = image.clone(),
			toolbarRight = image.clone(),
			clickToPlay = create("<a title='Click to play' href='#'></a>"),
			btn = create("<a title='' href='#'></a>"),
			disabledBtn = image.clone(),
			btns = new glow.dom.NodeList(),
			imageH = theOpts.height-EMP_TOOLBAR_H,
			clickToPlayH = 92,
			clickToPlayW = 109,
			clickToPlayX = (theOpts.width - clickToPlayW) / 2,
			clickToPlayY = (imageH - clickToPlayH) / 2,
			playBtnX = -109,
			popoutBtnX = -190,
			btnW = 27,
			btnH = 22,
			skinBgPositions = {
				black: {
					toolbarY: -127,
					btnY: -btnH
				},
				silver: {
					toolbarY: -92,
					btnY: 0
				}
			},
			bgPositions = skinBgPositions[theOpts.skin] || skinBgPositions[defaultOpts.skin];
		
		btns.push(btn);
		btns.push(disabledBtn);
		btns.css("display", "block")
			.css("width", btnW + "px")
			.css("height", btnH + "px")
			.css("position", "relative")
			.css("top", "3px")
			.css("margin-left", "4px");
		
		var playBtn = btn.clone(),
			rewind = disabledBtn.clone();
		playBtn.attr("title", "Play");
		
		// TODO - make animated gif of emp loading circles
		// holder.css("background", "#000 url(/cbbc/img/f/emp/loading.gif) no-repeat center " + ((imageH - 32)/2) + "px")
		holder.css("background", "#000")
			.css("display", "block")
			.css("position", "relative")
			.css("width", theOpts.width + "px")
			.css("height", theOpts.height + "px");
		
		image.css("background", "url(" + imgEl.attr("src") + ") no-repeat scroll 0 0")
			.css("display", "block")
			.css("position", "relative")
			.css("height", imageH + "px");
		
		toolbar.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll 0 " + bgPositions.toolbarY + "px")
			.css("display", "block")
			.css("position", "relative")
			.css("height", EMP_TOOLBAR_H + "px");
		
		toolbarRight.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll right " + bgPositions.toolbarY + "px")
			.css("display", "block")
			.css("float", "right")
			.css("width", "5px")
			.css("height", EMP_TOOLBAR_H + "px");
		
		playBtn.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll " + playBtnX + "px " + bgPositions.btnY + "px")
			.css("float", "left");
		
		rewind.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll -271px " + bgPositions.btnY + "px")
			.css("float", "left");
		
		toolbar.append(toolbarRight);
		
		// add "click to play" cta and disabled fullscreen button
		if ( theOpts.displayMode == "standard" ) {
			clickToPlay.css("display", "block")
				.css("position", "relative")
				.css("cursor", "pointer")
				.css("overflow", "hidden")
				.css("left", clickToPlayX + "px")
				.css("top", clickToPlayY + "px")
				.css("width", clickToPlayW + "px")
				.css("margin", "0")
				.css("height", clickToPlayH + "px");
			
			if ( glow.env.ie < 7 ) {
				clickToPlay[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + PNG_ASSETS + "', sizingMethod='crop');"
			} else {
				clickToPlay.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll top left");
			}
			image.append(clickToPlay);
			
			var fullscreen = disabledBtn.clone();
			fullscreen.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll -163px " + bgPositions.btnY + "px")
				.css("float", "right");
			toolbar.append(fullscreen);
		}
		
		// add popout button
		if ( theOpts.showPopoutButton ) {
			var popout = btn.clone();
			popout.attr("title", "Popout");
			popout.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll " + popoutBtnX + "px " + bgPositions.btnY + "px")
				.css("float", "right");
			toolbar.append(popout);
			
			glow.events.addListener(popout, "mouseover", function(e) {
				$(e.attachedTo).css("background-position", (popoutBtnX - btnW) + "px " + bgPositions.btnY + "px");
			});
			
			glow.events.addListener(popout, "mouseout", function(e) {
				$(e.attachedTo).css("background-position", popoutBtnX + "px " + bgPositions.btnY + "px");
			});
			
			glow.events.addListener(popout, "click", function(e) {
				e.preventDefault();
				popout();
			});
			
			var popout = function()
			{
				if ( !IS_EMP_SCRIPTS_LOADED() )
				{
					for ( var i=0, len=EMP_SCRIPT_PATHS.length; i<len; i++ )
					{
						glow.net.loadScript(EMP_SCRIPT_PATHS[i], {
							useCache: true
						});
					}
					executeWhen(popout, popout, arguments, IS_EMP_SCRIPTS_LOADED );
					return;
				}
				var settings = "playlist=" + theOpts.playlistUrl;
				if ( !!theOpts.configUrl ) {
					settings += "&amp;config=" + theOpts.configUrl;
				}
				
				embeddedMedia.console.popoutVideo(settings, theOpts.height, theOpts.width);
			}
		}
		
		var volume = disabledBtn.clone();
		volume.css("background", "url(" + PNG_ASSETS + ") no-repeat scroll -244px " + bgPositions.btnY + "px")
			.css("float", "right");
		
		toolbar.append(volume).append(playBtn).append(rewind);
		holder.append(image).append(toolbar);
		empEl.empty().append(holder);
		
		// delete options not relevant to embedding
		delete theOpts.embedIfCached;
		delete theOpts.useConfigXml;
		
		// add handlers to play button + "click to play"
		glow.events.addListener($([clickToPlay, playBtn]), "click", function(e) {
			e.preventDefault();
			holder.empty();
			theOpts.autoPlay = "true";
			cbbc.emp.embed("#"+id, theOpts);
		});
		
		glow.events.addListener(playBtn, "mouseover", function(e) {
			$(e.attachedTo).css("background-position", (playBtnX - btnW) + "px " + bgPositions.btnY + "px");
		});
		
		glow.events.addListener(playBtn, "mouseout", function(e) {
			$(e.attachedTo).css("background-position", playBtnX + "px " + bgPositions.btnY + "px");
		});
	};
	
	
	gloader.load(
		["glow", "1", "glow.dom", "glow.data", "glow.events", "glow.net"],
		{
			async: true,
			onLoad: function(g)
			{
				// pass glow to our closure scope
				glow = g;
				
				// do the preview for any previous calls
				glow.ready(function()
				{
					for ( var i=0, len=previewStack.length; i<len; i++ ) {
						cbbc.emp.preview.apply(cbbc.emp.preview, previewStack[i]);
					}
					previewStack = [];
					// do preview for all "emp-preview" classes
					setTimeout(function(){
						cbbc.emp.preview(".emp-preview")
					}, 0);
				});
				
			}
		}
	);
})();