Syn.VideoPlayer.Plugins.JwControls = Syn.VideoPlayer.AbstractDecorator.extend(
{
	/**
	 * @constructor
	 */
	init: function(adapter)
	{
		this._super(adapter);
	},

	/**
	 * Override the super method in order to modify the config to make the
	 * player load the controls.swf plugin.
	 *
	 * @param {String} div The HTML DIV the player will be loaded into
	 * @param {Object} config The configuration vars for the player
	 */
	insertVideoPlayer: function(div, config)
	{
		//add plugin to flashVars
		if(!config.flashVars)
		{
			config.flashVars = {};
		}
		if(!config.flashVars.plugins)
		{
			config.flashVars.plugins = "";
		}
		else
		{
			config.flashVars.plugins += ",";
		}
		config.flashVars.plugins += Syn.Config.Framework.AssetsRoot  + "en_US/components/video/players/jw_player/plugins/controls/controls.swf";

		//pass on controls params
		if (config.plugins && config.plugins.controls)
		{
			if (config.plugins.controls.share)
			{
				config.flashVars["controls.share"] = true;
			}
			if (config.plugins.controls["alert"])
			{
				config.flashVars["controls.alert"] = true;
			}
			if (config.plugins.controls.addtodesktop)
			{
				config.flashVars["controls.addtodesktop"] = true;
			}
			if (config.plugins.controls.debug)
			{
				config.flashVars["controls.debug"] = true;
			}
		}

		this._super(div, config);

		//Register global instance
		Syn.VideoPlayer.Plugins.JwControls.setInstance(this.getPlayerId(), this);
	},

	/**
	 * This method is called when the user clicks the "email" button
	 * on the controlBar in the video player.
	 */
	email: function()
	{
		this.notifyCallbacks(Syn.VideoPlayer.Events.EMAIL);
	}
});

/*
Manage instances of the JwControls class so that they are globally
accessible by id.  This is so the JW Player can communicate
with the instance that owns it.
*/
Syn.VideoPlayer.Plugins.JwControls.instances = [];
Syn.VideoPlayer.Plugins.JwControls.setInstance = function(id, instance)
{
	Syn.VideoPlayer.Plugins.JwControls.instances[id] = instance;
};
Syn.VideoPlayer.Plugins.JwControls.getInstance = function(id)
{
	return Syn.VideoPlayer.Plugins.JwControls.instances[id];
};

/*
JW Player javascript callbacks
*/
Syn.VideoPlayer.Plugins.JwControls.email = function(dat)
{
	Syn.VideoPlayer.Plugins.JwControls.getInstance(dat.id).email();
};
;
Syn.VideoPlayer.AbstractAdapter = Class.extend(
{
	/**
	 * @constructor
	 */
	init: function()
	{
		this.isPlayerReady = false;
		this.listeners = [];
		this.queuedEvents = [];
		this.playApi = new Syn.VideoPlayer.PlayApi();
	},
	
	/**
	 * Adds the video player SWF into an HTML div element.  Must be overridden
	 * in a subclass to do the actual work.  This method merely saves a reference
	 * to this instance so it can be accessed by the player later.
	 *
	 * Takes as its argument a config object that contains all settings for the
	 * video player.  This instance should be of Syn.VideoPlayer.Config or one of
	 * it's subclasses that is specifically for this type of adapter.
	 *
	 * @param {Object} config Usually a subclass of Syn.VideoPlayer.Config (e.g. JwConfig)
	 */
	insertVideoPlayer: function(div, config)
	{
		this.uniqueId = div;
		Syn.VideoPlayer.AbstractAdapter.setInstance(this.uniqueId, this);
	},

	
	/*
	These methods can be used to control the player.
	*/

	/**
	 * Load a video, but do not play unless autostart is set to true.
	 *
	 * <p>
	 * This abstract method queues this method invokation if the player
	 * is not loaded yet.  If the method is queued, this returns false
	 * so that the super method will not carry out its actions.  The
	 * queued methods are called once the player fires its ready event.
	 * </p>
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 */
	load: function(video)
	{
		return this.maybeQueue(this.load, arguments);
	},
	/**
	 * If the item parameter is null, resumes the playback of the currently
	 * loaded file.  If not null, the item is loaded then played.
	 *
	 * <p>See comments in video_player.js for item format info</p>
	 *
	 * @param {String|Object} (optional) The video to play
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 * @see Syn.VideoPlayer#play
	 */
	play: function(video)
	{
		return this.maybeQueue(this.play, arguments);
	},
	/**
	 * Pause.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	pause: function()
	{
		return this.maybeQueue(this.pause, arguments);
	},
	/**
	 * Stop the video. Set the playhead position back to the start.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	stop: function()
	{
		return this.maybeQueue(this.stop, arguments);
	},
	/**
	 * Mute.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	mute: function()
	{
		return this.maybeQueue(this.mute, arguments);
	},
	/**
	 * Unmute.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	unmute: function()
	{
		return this.maybeQueue(this.unmute, arguments);
	},
	/**
	 * Advances the player to the next item in its playlist.  This might not
	 * be enabled for all players since most of the playlisting will be done
	 * in javascript.  This is only for player-internal playlists.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	next: function()
	{
		return this.maybeQueue(this.next, arguments);
	},
	/**
	 * Retreats the player to the previous item in its playlist.  This might
	 * not be enabled for all playlist since most playlisting will be done
	 * in javascript.  This is only for player-internal playlists.
	 *
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	previous: function()
	{
		return this.maybeQueue(this.previous, arguments);
	},
	/**
	 * @return Whether or not the super method should perform its actions
	 * @type Boolean
	 * @see #load
	 */
	error: function()
	{
		return this.maybeQueue(this.error, arguments);
	},


	/*
	Helper methods for handling Synacor video
	*/

	/**
	 * Plays a video id.  Calls the Play API to get the URL for a video id.
	 *
	 * @param {String} id The id of the video
	 * @param {Object} (optional) scope_obj The scope of the callback
	 * @param {function} (optional) success_callback The callback for Play  API success
	 * @param {function} (optional) error_callback The callback for Play API failure
	 */
	playVideoId: function(id, scope_obj, success_callback, error_callback)
	{
		var playerReady = this.maybeQueue(this.playVideoId, arguments);

		if (playerReady)
		{
			if(!scope_obj && !success_callback && !error_callback)
			{
				scope_obj = this;
				success_callback = this.onPlayApiSuccess;
				error_callback = this.onPlayApiError;
			}

			this.playApi.getUrlFromId(id, scope_obj, success_callback, error_callback);
		}
	},
	/**
	 * Default success callback for the Play API as invoked by playVideoId()
	 *
	 * @param {Object} result The result of the Play API request
	 * @see #playVideoId
	 */
	onPlayApiSuccess: function(result)
	{
		this.play(result.url);
	},
	/**
	 * Default error callback for the Play API as invoked by playVideoId()
	 *
	 * @param {Object} result The result of the Play API request
	 * @see #playVideoId
	 */
	onPlayApiError: function(result)
	{
		this.error(result);
	},


	/*
	Queue methods
	*/

	/**
	 * Checks to see if the player has been initialized and is ready
	 * for interaction.  If not, this adds the command to a queue to be
	 * called later once the player is initialized.
	 *
	 * @param {function} func The function to be queued if the player is not ready
	 * @param {Array} args Arguments to be passed to the function
	 * @returns whether or not the player is ready for communication
	 * @type Boolean
	 */
	maybeQueue: function(func, args)
	{	
		if(!this.isPlayerReady)
		{
			this.queuedEvents.push({"func":func, "args":args});
		}
		return this.isPlayerReady;
	},
	/**
	 * When the player notifies us that it is initialized and ready, we
	 * invoke this method to call all the methods (with their args) that
	 * were queued up while the player was not yet ready.
	 *
	 * @see #setPlayerReady
	 */
	processQueuedEvents: function()
	{
		if(this.isPlayerReady)
		{
			while(this.queuedEvents.length > 0)
			{
				var obj = this.queuedEvents.shift();
				obj.func.apply(this, obj.args);
			}
		}
	},


	/*
	Event Handling
	*/

	/**
	 * A function to be called when we want to listen for a specific event
	 * from the video player.
	 *
	 * @param {String} type The type of event (listed in Syn.VideoPlayer.Events)
	 * @param {Object} scopeObj The object on which to call func
	 * @param {function} func The func to be called (on scopeObj) when the event occurs
	 * @see #removeCallback
	 */
	addCallback: function(type, scopeObj, func)
	{
		this.listeners.push({type:type,scopeObj:scopeObj,func:func});
	},
	/**
	 * Removes a callback that was added with addCallback(). Will only
	 * remove one matching listener.  If addCallback() was called more
	 * than once with the same paramters, removeCallback() must be
	 * called the same number of times.
	 *
	 * @param {String} type The type of event (in Syn.VideoPlayer.Events)
	 * @param {Object} scopeObj The object passed to addCallback()
	 * @param {function} func The func passed to addCallback()
	 * @see #addCallback
	 */
	removeCallback: function(type, scopeObj, func)
	{
		var n = this.listeners.length;
		for (var i = 0; i < n; i++)
		{
			var obj = this.listeners[i];
			if (obj.type == type && obj.scopeObj == scopeObj && obj.func == func)
			{
				this.listeners.splice(i, 1);
				return true;
			}
		}
		return false;
	},
	/**
	 * When a video player event occurs, we call this method which
	 * invokes all callbacks listening for that specific event type.
	 *
	 * @param {String} type The type of event (in Syn.VideoPlayer.Events)
	 * @param {Array} (optional) args Arguments to be passed to the callbacks
	 */
	notifyCallbacks: function(type, args)
	{
		//IE does not like null args
		if(!args)
		{
			args = [];
		}

		//IE also does not like for each
		var n = this.listeners.length;
		for (var i = 0; i < n; i++)
		{
			var obj = this.listeners[i];

			if(obj.type == type)
			{
				obj.func.apply(obj.scopeObj, args);
			}
		}
	},


	/**
	 * Call this when the player has initialized and is ready for
	 * interaction. Processes any method invokations that occurred
	 * while the player was initializing and notifies any listeners
	 * that the player is now ready.
	 */
	setPlayerReady: function()
	{
		if(!this.isPlayerReady)
		{
			this.isPlayerReady = true;
			this.processQueuedEvents();
			this.notifyCallbacks(Syn.VideoPlayer.Events.READY);
		}
	},

	/**
	 * Allow access to the id of the player.  Enables communication
	 * between Flash and JavaScript.
	 */
	getPlayerId: function()
	{
		return this.uniqueId;
	}
});

Syn.VideoPlayer.AbstractAdapter.instances = [];
Syn.VideoPlayer.AbstractAdapter.setInstance = function(id, instance)
{
	Syn.VideoPlayer.AbstractAdapter.instances[id] = instance;
};
Syn.VideoPlayer.AbstractAdapter.getInstance = function(id)
{
	return Syn.VideoPlayer.AbstractAdapter.instances[id];
};
;
Syn.VideoPlayer.JwAdapter = Syn.VideoPlayer.AbstractAdapter.extend(
{
	HOME_DIR: Syn.Config.Framework.AssetsRoot + "en_US/components/video/players/jw_player/",
	SKINS_DIR: Syn.Config.Framework.AssetsRoot + "en_US/components/video/players/jw_player/skins/",

	/**
	 * @constructor
	 */
	init: function()
	{
		this._super();
	},

	/**
	 * Inserts the video player into a div with the specified configuration
	 *
	 * @param {String} div HTML DIV that the player should be inserted into
	 * @param {Object} config The configuration object for the player
	 */
	insertVideoPlayer: function(div, config)
	{
		//The uniqueId for the jw_wrapper should not contain any ":"s. They
		//were making it hard to reference the player.  So strip the value
		//passed as the uniqueId, but make sure the div value is unchanged
		//so the the player is still written into a valid div.
		this._super(div.concat("_player").replace(/:/g,""), config);

		var so = new SWFObject(
				this.HOME_DIR + "player.swf",
				this.uniqueId,
				"100%",
				"100%",
				config.version,
				false);

		if (config.autostart === true)
		{
			config.flashVars.autostart = "true";
		}
		if (config.scaling && !config.flashVars.stretching)
		{
			config.flashVars.stretching = config.scaling;
		}
		config.flashVars.skin = this.SKINS_DIR + "stylish_small.swf";
		config.flashVars.controlbar = "over";
		config.flashVars.repeat = "list";
		config.flashVars.debug = "none";
		config.flashVars.shuffle = "true";

		for (var key in config.flashVars)
		{
			if (config.flashVars.hasOwnProperty(key))
			{
				so.addVariable(key, config.flashVars[key]);
			}
		}
		for (key in config.params)
		{
			if (config.params.hasOwnProperty(key))
			{
				so.addParam(key, config.params[key]);
			}
		}
		for (key in config.attributes)
		{
			if (config.attributes.hasOwnProperty(key))
			{
				so.addAttribute(key, config.attributes[key]);
			}
		}

		so.write(div);

		//Mimic configuration
		if (config.item)
		{
			this.load(config.item);
		}
		if (config.mute === true)
		{
			this.mute();
		}
		else if (config.mute === false)
		{
			this.unmute();
		}
	},

	/**
	 * Loads an item into the player.  The call to the super method sees
	 * if the player is ready to be communicated with.  If so, call the
	 * method on the player to load the item.  If not, do nothing, as the
	 * call has been queued and will be invoked once the player is ready.
	 *
	 * @param {String|Object} The item to load
	 */
	load: function(item)
	{
		if (this._super(item))
		{
			if (item)
			{
				this.getPlayer().sendEvent('LOAD', item);
			}
		}
	},
	/**
	 * Loads then plays the item if an item is specified.  Otherwise,
	 * resumes the playback of the currently loaded item.
	 *
	 * @param {String|Object} (optional) The item to play
	 */
	play: function(item)
	{
		if (this._super(item))
		{
			this.load(item);
			this.getPlayer().sendEvent('PLAY', 'true');
		}
	},
	/**
	 * Pauses the currently playing item.
	 */
	pause: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('PLAY', 'false');
		}
	},
	/**
	 * Stops the currently loaded item and sets the playhead back to
	 * the start.
	 */
	stop: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('STOP');
		}
	},
	/**
	 * Mute
	 */
	mute: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('MUTE', 'true');
		}	
	},
	/**
	 * Unmute
	 */
	unmute: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('MUTE', 'false');
		}
	},
	/**
	 * Advances the player to the next item in the playlist.  This
	 * is only for player-internal playlists.  Keep in mind that most
	 * of our playlisting is done in javascript, so the player knows
	 * nothing about them.
	 */
	next: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('NEXT');
		}
	},
	/**
	 * Retreats the player to the previous item in the playlist. This
	 * is only for player-internal playlist.  Keep in mind that most
	 * of our playlisting in done in javascript, so the player knows
	 * nothing about them.
	 */
	previous: function()
	{
		if (this._super())
		{
			this.getPlayer().sendEvent('PREV');
		}
	},
	/**
	 * A generic error handler
	 *
	 * @param {String} msg The error message
	 */
	error: function(msg)
	{
		if (this._super(msg))
		{
			//this.getPlayer().sendEvent('LOAD', msg);
		}
	},


	/*
	Event Handling
	*/

	/**
	 * Called by the global handler playerReady() when the JW Player is
	 * fully initialized and ready to be communicated with.  Call the setPlayerReady()
	 * function to process the queued method calls, then add our event listeners to
	 * the player itself.
	 *
	 * @param {Object} obj Info sent from the player.  See JW Player docs for its contents.
	 */
	playerReady: function(obj)
	{
		this.setPlayerReady();

		var player = this.getPlayer();
		player.addModelListener("ERROR", "Syn.VideoPlayer.JwAdapter.modelErrorHandler");
		player.addModelListener("STATE", "Syn.VideoPlayer.JwAdapter.modelStateHandler");
	},
	/**
	 * Handles error events broadcast by the JW Player.
	 *
	 * @param {Object} dat Info sent from the JW Player.  See JW Player docs for its contents.
	 */
	modelErrorHandler: function(dat)
	{
		this.notifyCallbacks(Syn.VideoPlayer.Events.ERROR);
	},
	/**
	 * Handles state change events broadcast by the JW Player.
	 *
	 * @param {Object} dat Info sent from the JW Player.  See JW Player docs for its contents.
	 */
	modelStateHandler: function(dat)
	{
		if (dat.newstate == "COMPLETED")
		{
			this.notifyCallbacks(Syn.VideoPlayer.Events.COMPLETE);
		}
	},

	/**
	 * Player Accessor.
	 *
	 * @return The JW Player SWF instance
	*/
	getPlayer: function()
	{
		return document.getElementById(this.uniqueId);
	}
});

/*
Global Handlers
*/
Syn.VideoPlayer.JwAdapter.modelErrorHandler = function(dat)
{
	Syn.VideoPlayer.AbstractAdapter.getInstance(dat.id).modelErrorHandler(dat);
};
Syn.VideoPlayer.JwAdapter.modelStateHandler = function(dat)
{
	Syn.VideoPlayer.AbstractAdapter.getInstance(dat.id).modelStateHandler(dat);
};

/**
 * JW Player hardcoded handler.  This method cannot be namespaced since the JW Player
 * calls the playerReady function in the global namesapce by default and it cannot be
 * changed.
 *
 * @param {Object} obj Info sent by the JW Player. Contains the id of the player.
 */
var playerReady = function(obj)
{
	Syn.VideoPlayer.AbstractAdapter.getInstance(obj.id).playerReady(obj);
};
;
/**
 * jCarouselLite - jQuery plugin to navigate images/any content in a carousel style widget.
 * @requires jQuery v1.2 or above
 *
 * http://gmarwaha.com/jquery/jcarousellite/
 *
 * Copyright (c) 2007 Ganeshji Marwaha (gmarwaha.com)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Version: 1.0.1
 * Note: Requires jquery 1.2 or above from version 1.0.1
 */

/**
 * Creates a carousel-style navigation widget for images/any-content from a simple HTML markup.
 *
 * The HTML markup that is used to build the carousel can be as simple as...
 *
 *  <div class="carousel">
 *      <ul>
 *          <li><img src="image/1.jpg" alt="1"></li>
 *          <li><img src="image/2.jpg" alt="2"></li>
 *          <li><img src="image/3.jpg" alt="3"></li>
 *      </ul>
 *  </div>
 *
 * As you can see, this snippet is nothing but a simple div containing an unordered list of images.
 * You don't need any special "class" attribute, or a special "css" file for this plugin.
 * I am using a class attribute just for the sake of explanation here.
 *
 * To navigate the elements of the carousel, you need some kind of navigation buttons.
 * For example, you will need a "previous" button to go backward, and a "next" button to go forward.
 * This need not be part of the carousel "div" itself. It can be any element in your page.
 * Lets assume that the following elements in your document can be used as next, and prev buttons...
 *
 * <button class="prev">&lt;&lt;</button>
 * <button class="next">&gt;&gt;</button>
 *
 * Now, all you need to do is call the carousel component on the div element that represents it, and pass in the
 * navigation buttons as options.
 *
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev"
 * });
 *
 * That's it, you would have now converted your raw div, into a magnificient carousel.
 *
 * There are quite a few other options that you can use to customize it though.
 * Each will be explained with an example below.
 *
 * @param an options object - You can specify all the options shown below as an options object param.
 *
 * @option btnPrev, btnNext : string - no defaults
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev"
 * });
 * @desc Creates a basic carousel. Clicking "btnPrev" navigates backwards and "btnNext" navigates forward.
 *
 * @option btnGo - array - no defaults
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      btnGo: [".0", ".1", ".2"]
 * });
 * @desc If you don't want next and previous buttons for navigation, instead you prefer custom navigation based on
 * the item number within the carousel, you can use this option. Just supply an array of selectors for each element
 * in the carousel. The index of the array represents the index of the element. What i mean is, if the
 * first element in the array is ".0", it means that when the element represented by ".0" is clicked, the carousel
 * will slide to the first element and so on and so forth. This feature is very powerful. For example, i made a tabbed
 * interface out of it by making my navigation elements styled like tabs in css. As the carousel is capable of holding
 * any content, not just images, you can have a very simple tabbed navigation in minutes without using any other plugin.
 * The best part is that, the tab will "slide" based on the provided effect. :-)
 *
 * @option mouseWheel : boolean - default is false
 * @example
 * $(".carousel").jCarouselLite({
 *      mouseWheel: true
 * });
 * @desc The carousel can also be navigated using the mouse wheel interface of a scroll mouse instead of using buttons.
 * To get this feature working, you have to do 2 things. First, you have to include the mouse-wheel plugin from brandon.
 * Second, you will have to set the option "mouseWheel" to true. That's it, now you will be able to navigate your carousel
 * using the mouse wheel. Using buttons and mouseWheel or not mutually exclusive. You can still have buttons for navigation
 * as well. They complement each other. To use both together, just supply the options required for both as shown below.
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      mouseWheel: true
 * });
 *
 * @option auto : number - default is null, meaning autoscroll is disabled by default
 * @example
 * $(".carousel").jCarouselLite({
 *      auto: 800,
 *      speed: 500
 * });
 * @desc You can make your carousel auto-navigate itself by specfying a millisecond value in this option.
 * The value you specify is the amount of time between 2 slides. The default is null, and that disables auto scrolling.
 * Specify this value and magically your carousel will start auto scrolling.
 *
 * @option speed : number - 200 is default
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      speed: 800
 * });
 * @desc Specifying a speed will slow-down or speed-up the sliding speed of your carousel. Try it out with
 * different speeds like 800, 600, 1500 etc. Providing 0, will remove the slide effect.
 *
 * @option easing : string - no easing effects by default.
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      easing: "bounceout"
 * });
 * @desc You can specify any easing effect. Note: You need easing plugin for that. Once specified,
 * the carousel will slide based on the provided easing effect.
 *
 * @option vertical : boolean - default is false
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      vertical: true
 * });
 * @desc Determines the direction of the carousel. true, means the carousel will display vertically. The next and
 * prev buttons will slide the items vertically as well. The default is false, which means that the carousel will
 * display horizontally. The next and prev items will slide the items from left-right in this case.
 *
 * @option circular : boolean - default is true
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      circular: false
 * });
 * @desc Setting it to true enables circular navigation. This means, if you click "next" after you reach the last
 * element, you will automatically slide to the first element and vice versa. If you set circular to false, then
 * if you click on the "next" button after you reach the last element, you will stay in the last element itself
 * and similarly for "previous" button and first element.
 *
 * @option visible : number - default is 3
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      visible: 4
 * });
 * @desc This specifies the number of items visible at all times within the carousel. The default is 3.
 * You are even free to experiment with real numbers. Eg: "3.5" will have 3 items fully visible and the
 * last item half visible. This gives you the effect of showing the user that there are more images to the right.
 *
 * @option start : number - default is 0
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      start: 2
 * });
 * @desc You can specify from which item the carousel should start. Remember, the first item in the carousel
 * has a start of 0, and so on.
 *
 * @option scrool : number - default is 1
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      scroll: 2
 * });
 * @desc The number of items that should scroll/slide when you click the next/prev navigation buttons. By
 * default, only one item is scrolled, but you may set it to any number. Eg: setting it to "2" will scroll
 * 2 items when you click the next or previous buttons.
 *
 * @option beforeStart, afterEnd : function - callbacks
 * @example
 * $(".carousel").jCarouselLite({
 *      btnNext: ".next",
 *      btnPrev: ".prev",
 *      beforeStart: function(a) {
 *          alert("Before animation starts:" + a);
 *      },
 *      afterEnd: function(a) {
 *          alert("After animation ends:" + a);
 *      }
 * });
 * @desc If you wanted to do some logic in your page before the slide starts and after the slide ends, you can
 * register these 2 callbacks. The functions will be passed an argument that represents an array of elements that
 * are visible at the time of callback.
 *
 *
 * @cat Plugins/Image Gallery
 * @author Ganeshji Marwaha/ganeshread@gmail.com
 */

(function($) {                                          // Compliant with jquery.noConflict()
$.fn.jCarouselLite = function(o) {
    o = $.extend({
        btnPrev: null,
        btnNext: null,
        btnGo: null,
        mouseWheel: false,
        auto: null,

        speed: 200,
        easing: null,

        vertical: false,
        circular: true,
        visible: 3,
        start: 0,
        scroll: 1,

        beforeStart: null,
        afterEnd: null
    }, o || {});

    return this.each(function() {                           // Returns the element collection. Chainable.
		this.o = o;

        var running = false, animCss=o.vertical?"top":"left", sizeCss=o.vertical?"height":"width";
        var div = $(this), ul = $("ul", div), tLi = $("li", ul), tl = tLi.size(), v = o.visible;

        if(o.circular) {
            ul.prepend(tLi.slice(tl-v-1+1).clone())
              .append(tLi.slice(0,v).clone());
            o.start += v;
        }

        var li = $("li", ul), itemLength = li.size(), curr = o.start;
        div.css("visibility", "visible");

        li.css({overflow: "hidden", float: o.vertical ? "none" : "left"});
        ul.css({margin: "0", padding: "0", position: "relative", "list-style-type": "none", "z-index": "1"});
        div.css({overflow: "hidden", position: "relative", "z-index": "2", left: "0px"});

        var liSize = o.vertical ? height(li) : width(li);   // Full li size(incl margin)-Used for animation
        var ulSize = liSize * itemLength;                   // size of full ul(total length, not just for the visible items)
        var divSize = liSize * v;                           // size of entire div(total length for just the visible items)

        li.css({width: li.width(), height: li.height()});
        ul.css(sizeCss, ulSize+"px").css(animCss, -(curr*liSize));

        div.css(sizeCss, divSize+"px");                     // Width of the DIV. length of visible images

		var self = this;
        if(o.btnPrev)
            $(o.btnPrev).click(function() {
                return self.go(curr-o.scroll);
            });

        if(o.btnNext)
            $(o.btnNext).click(function() {
                return self.go(curr+o.scroll);
            });

        if(o.btnGo)
            $.each(o.btnGo, function(i, val) {
                $(val).click(function() {
                    return self.go(o.circular ? o.visible+i : i);
                });
            });

        if(o.mouseWheel && div.mousewheel)
            div.mousewheel(function(e, d) {
                return d>0 ? self.go(curr-o.scroll) : self.go(curr+o.scroll);
            });

        if(o.auto)
            setInterval(function() {
                self.go(curr+o.scroll);
            }, o.auto+o.speed);

        function vis() {
            return li.slice(curr).slice(0,v);
        };

        this.go = function(to) {
            if(!running) {

                if(o.beforeStart)
                    o.beforeStart.call(this, vis());

                if(o.circular) {            // If circular we are in first or last, then goto the other end
                    if(to<=o.start-v-1) {           // If first, then goto last
                        ul.css(animCss, -((itemLength-(v*2))*liSize)+"px");
                        // If "scroll" > 1, then the "to" might not be equal to the condition; it can be lesser depending on the number of elements.
                        curr = to==o.start-v-1 ? itemLength-(v*2)-1 : itemLength-(v*2)-o.scroll;
                    } else if(to>=itemLength-v+1) { // If last, then goto first
                        ul.css(animCss, -( (v) * liSize ) + "px" );
                        // If "scroll" > 1, then the "to" might not be equal to the condition; it can be greater depending on the number of elements.
                        curr = to==itemLength-v+1 ? v+1 : v+o.scroll;
                    } else curr = to;
                } else {                    // If non-circular and to points to first or last, we just return.
                    if(to<0 || to>itemLength-v) return;
                    else curr = to;
                }                           // If neither overrides it, the curr will still be "to" and we can proceed.

                running = true;

                ul.animate(
                    animCss == "left" ? { left: -(curr*liSize) } : { top: -(curr*liSize) } , o.speed, o.easing,
                    function() {
                        if(o.afterEnd)
                            o.afterEnd.call(this, vis());
                        running = false;
                    }
                );
                // Disable buttons when the carousel reaches the last/first, and enable when not
                if(!o.circular) {
                    $(o.btnPrev + "," + o.btnNext).removeClass("disabled");
                    $( (curr-o.scroll<0 && o.btnPrev)
                        ||
                       (curr+o.scroll > itemLength-v && o.btnNext)
                        ||
                       []
                     ).addClass("disabled");
                }

            }
            return false;
        };
    });
};

function css(el, prop) {
    return parseInt($.css(el[0], prop)) || 0;
};
function width(el) {
    return  el[0].offsetWidth + css(el, 'marginLeft') + css(el, 'marginRight');
};
function height(el) {
    return el[0].offsetHeight + css(el, 'marginTop') + css(el, 'marginBottom');
};

})(jQuery);
;
/**
 * Video Embedded Component JavaScript
 */
Syn.VideoEmbedded = Syn.VideoEmbeddedBase.extend(
{
/**
	 * Contains a reference to the javascript interval or timeout used to assist banner ad display.
	 * @var mixed
	 */
	ad_animation_timeout: 0,

	/**
	 * Contains the click url of the preroll ad companion banner, which should be opened in a new window when the banner is clicked.
	 * Sent from ad provider to Flash player, which sets this var.
	 * @var string
	 */
	ad_clickurl: "",

	/**
	 * The height of the preroll ad companion banner, in pixels.
	 * @var integer
	 */
	ad_h: 250,

	/**
	 * Ad playing
	 * @var bool
	 */
	ad_playing: false,

	/**
	 * Contains a reference to the SWFObject that is used to write a Flash companion banner, if necessary.
	 * @var mixed
	 */
	ad_so: null,

	/**
	 * Contains the url of the source of the preroll ad companion banner.  Sent from ad provider to Flash player, which sets this var.
	 * @var string
	 */
	ad_url: "",

	/**
	 * The width of the preroll ad companion banner, in pixels.
	 * @var integer
	 */
	ad_w: 300,

	/**
	 * Carousel
	 * @var bool
	 */
	carousel: false,

	/**
	 * Playlist ID
	 * @var string
	 */
	pid: '',

	/**
	 * Video player object.
	 * @var object
	 */
	video_player: null,

	/**
	 * This holds the video ID that is currently being played (or, the initial video ID that will load when the player initializes).
	 * @var integer
	 */
	player_vid_id: 0,

	/**
	 * Contains a path to the video api that will convey skin and video information to the Flash player.
	 * @var string
	 */
	player_video_api_url: Syn.Config.Framework.PortalRoot + "files/video/video_api.php",

	/**
	 * Contains the selected plist item
	 * @var mixed
	 */
	selected_plist_item: 0,

	/**
	 * Tracking ID
	 * @var string
	 */
	tid: '',

	/**
	 * Is the carousel vertical?
	 * @var bool
	 */
	vertical: false,

	/**
	 * Visible videos
	 * @var int
	 */
	visible: 3,

	/**
	 * Constructor
	 * @param {object} config
	 */
	init: function(config)
	{
		this._super(config);
		var self = this;

		// Setup configuration
		this.carousel       = (config.carousel) ? config.carousel : false;
		this.pid            = (config.pid) ? config.pid : '';
		this.player_skin_id = (config.sid) ? config.sid : this.player_skin_id;
		this.tid            = (config.tid) ? config.tid : '';
		this.vertical       = (config.vertical) ? config.vertical : this.vertical;
		this.visible        = (config.visible) ? config.visible : this.visible;
		this.player_height  = (config.player_height) ? config.player_height : this.player_height;
		this.player_width   = (config.player_width) ? config.player_width : this.player_width;
		this.video_ads      = (config.video_ads) ? config.video_ads : {};
		this.video_urls     = (config.video_urls) ? config.video_urls : {};

		if ($.browser.msie && parseFloat($.browser.version) < 7.0)
		{
			var fn = function()
			{
				this.style.backgroundImage = '';
				$(this).toggleClass("embd_flash_play_on");
				self.uniqueElmt("flash_placeholder").find(".embd_flash_play").fixPngBackground();
			};
			this.uniqueElmt("flash_placeholder").find(".embd_flash_play").mouseover(fn).mouseout(fn);
		}

		if (this.carousel)
		{
			this.uniqueElmt("carousel").jCarouselLite({
				btnPrev: "#"+this.uniqueKey("previous"),
				btnNext: "#"+this.uniqueKey("next"),
				circular: false,
				scroll: parseFloat(this.visible),
				vertical: this.vertical,
				visible: parseFloat(this.visible),
				beforeStart: function()
				{
					self.beforeStartCallback("carousel");
				}
			});
			var retfalse = function()
			{
				return false;
			};
			this.uniqueElmt("previous").click(retfalse);
			this.uniqueElmt("next").click(retfalse);
		}

		var i = 0;
		this.uniqueElmt("carousel").find(">ul>li").each(function()
		{
			this.lipos = i++;
		});

		// Depending on the playlist, the title may be shown in the player area.  We
		// only need to worry about it if that element (embd_flash_title) exists.
		if (this.uniqueElmt("flash_placeholder").find(".embd_flash_placeholder_title").size())
		{
			if ($(this.selected_plist_item).size())
			{
				this.uniqueElmt("flash_placeholder").find(".embd_flash_placeholder_title span").html($(this.selected_plist_item).find("a.title").attr("title"));
				this.uniqueElmt("flash_placeholder").find(".embd_flash_placeholder_provider_date").html($(this.selected_plist_item).find("div.provider_date").html());
			}
		}

		// We need to politely ask the user to upgrade flash
		if (this.userHasFlash())
		{
			this.uniqueElmt("install").hide();
			this.uniqueElmt("flash_container").show();
			this.find("li.plist-item").connect("click", this, "playVideo");
			this.uniqueElmt("flash_placeholder").connect("click", this, "startVideo");
			this.uniqueElmt("flash_ad_close").connect("click", this, "hideBannerAd");
			this.uniqueElmt("flash_placeholder").find(".embd_flash_play").fixPngBackground();
		}
		else
		{
			var retfalse = function()
			{
				return false;
			};

			this.find("li.plist-item").click(retfalse);
			this.uniqueElmt("flash_placeholder").click(retfalse);
		}

		// First video will be loaded in a stopped state with no preroll ad.
		this.playNextVideo(true);
	},

	/**
	 * Plays the next video in the playlist
	 * @param {bool} dont_play
	 */
	playNextVideo: function(dont_play)
	{
		var element = (this.selected_plist_item) ? $(this.selected_plist_item).next() : this.uniqueElmt().find("li.plist-item").eq(0);
		if (element.size() && !element.hasClass("void"))
		{
			if (!dont_play && this.carousel)
			{
				// maybe animate the jCarouselLite maybe?
				var lipos = element.get(0).lipos;
				var to = lipos - (lipos % this.uniqueElmt("carousel").get(0).o.scroll);
				this.uniqueElmt("carousel").get(0).go( to );
			}
			this.playVideo(element, null, dont_play);
		}
	},

	/**
	 * Plays the next video in the playlist
	 */
	startVideo: function(dont_play)
	{
		var element = this.uniqueElmt().find(".vidembd-plist li").eq(0);
		if (element.size())
		{
			this.playVideo(element, null);
		}
	},

	/**
	 * Plays a video
	 * @param {Object} element The element which was clicked
	 * @param {Object} e An event object
	 * @param {bool} dont_play
	 */
	playVideo: function(element, e, dont_play)
	{
		if (!dont_play && !this.video_player)
		{
			this.createPlayer();
		}

		if (this.selected_plist_item)
		{
			if (this.selected_plist_item.display_title)
			{
				$(this.selected_plist_item).find("a.title").html(this.selected_plist_item.display_title);
			}
		}

		$(this.selected_plist_item).removeClass("plist-item-selected");
		this.selected_plist_item = element;
		$(element).addClass("plist-item-selected");

		var video_id      	= $(element).find("img").attr("alt");
		this.player_vid_id	= video_id;
		var title         	= $(element).find("a.title").attr("title");
		var display_title 	= $(element).find("a.title").html();
		var desc          	= $(element).find("div.desc").html();
		var duration      	= $(element).find("span.duration").html();

		element.display_title = display_title;
		$(element).find("a.title").html($("<strong></strong>").html("Now Playing..."));
		this.uniqueElmt("playing-video-title").html(title);
		this.uniqueElmt("playing-video-desc").html(desc);
		this.uniqueElmt("playing-video-duration").html(duration);

		// Aw man! a double negative - only log if you are playing a video
		if (!dont_play)
		{
			var lipos = (element.lipos >= 0) ? element.lipos : element.get(0).lipos;
			// Log
			this.log(video_id, this.pid, this.tid, lipos+1);

			this.video_player.play(
			{
				url: this.video_urls[video_id],
				ads: this.video_ads[video_id],
				type: 'video'
			});
		}
	},

	/**
	 * Creates the Flash video player instance with SWFObject and writes it to the appropriate div.
	 */
	createPlayer: function()
	{
		if (this.video_player)
		{
			return;
		}

		var video_config = new Syn.VideoPlayer.Config();
		video_config.plugins = {
			ads: true
		};

		this.video_player   = new Syn.VideoPlayer(
		{
			targetdiv: this.uniqueKey('flash_container', true),
			videoConfig: video_config
		});

		this.video_player.addCallback("showBannerAd", this, this.showBannerAd);
		this.video_player.addCallback("hideBannerAd", this, this.hideBannerAd);
		this.video_player.addCallback('complete', this, this.videoComplete);
		this.uniqueElmt("flash_container").css("background", "transparent");
	},

	/**
	 * Shows a banner ad
	 */
	showBannerAd: function(display_url, click_url)
	{
		// If the ad is currently playing, then we shouldn't animate the ad
		var should_animate_ad = !this.ad_playing;
		this.ad_playing = true;

		if (should_animate_ad)
		{
			var self = this;
			var anicoords = this.getAdAnimationCoords();
			var animation_object = {
				width: "311px",
				height: "260px",
				left: anicoords.left + "px"
			};

			var callback_fn = function()
			{
				self.insertAd(display_url, click_url);
			};

			// Only perform the slideout animation if the slideout isn't visible right now.
			this.uniqueElmt('flash_ad').css({
				"width": "1px",
				"left": anicoords.start_left + "px",
				"display": "block"
			}).animate(animation_object, 1500, callback_fn);
		}
		else
		{
			// No Animation
			this.insertAd(display_url, click_url);
		}

	},

	/**
	 * Does the actual inserting of the ad
	 */
	insertAd: function(display_url, click_url)
	{
		this.video_player.insertAd(this.uniqueElmt("flash_ad_container"), display_url, click_url, this.ad_w, this.ad_h);
	},

	/**
	 * Hides the banner ad
	 */
	hideBannerAd: function()
	{
		this.ad_playing = false;
		this.uniqueElmt("flash_ad_container").html("");

		if (this.uniqueElmt('flash_ad').css("display") == "block")
		{
			this.hideAd_animate();
		}
	},

	/**
	 * Logs a video
	 * @param {vid} vid
	 * @param {String} pid
	 * @param {String} tid
	 * @param {int} pos Position of the video clicked
	 */
	log: function(vid, pid, tid, pos)
	{
		// Post the video and playlist_id to the api
		$.get(this.player_video_api_url, {
			op: "log",
			vid: vid,
			pid: pid,
			tid: tid,
			pos: pos,
			source: "playlist"
		});
	},

	/**
	 * Video complete event received from the Flash video player. Actions that should occur when a video has completed playback should occur here.
	 */
	videoComplete: function()
	{
		this.playNextVideo(false);
	},

	/**
	 * Animate banner ad flyout to closed (invisible) state.  Called by hideAd to retract
	 * the companion banner flyout.
	 */
	hideAd_animate: function()
	{
		var self = this;
		var anicoords = this.getAdAnimationCoords();

		// The jQuery animation object
		var animation_obj = {
			width: "1px",
			height: "260px",
			left: anicoords.start_left //this.uniqueElmt('flash_ad_popoutloc').offset().left  + "px"
		};

		// Callback when the animation ends
		var callback_fn = function()
		{
			self.hideAd_finish();
		};

		this.uniqueElmt('flash_ad').animate(animation_obj, 400, callback_fn);

		clearInterval(this.ad_animation_timeout);
	},

	/**
	 * Finish up hiding companion banner ad.  Assures banner ad flyout is not visible and cleans up
	 * the timeout.
	 */
	hideAd_finish: function()
	{
		this.uniqueElmt("flash_ad").css("display", "none").css("width", "0px");
		this.uniqueElmt("flash_ad_container").html("");
		clearInterval(this.ad_animation_timeout);
	},

	/**
	 * Retrieve the starting and ending coordinates for the ad animation
	 * @return {Object}
	 */
	getAdAnimationCoords: function()
	{
		var comp_container = this.wrapper().eq(0);
		var offset = comp_container.getPosition();
		var border_left = parseFloat(comp_container.css("border-left-width")).NaN0();

		var start_left = offset.left - 6 + border_left;
		var left = offset.left - 317 + border_left;

		if (left < 0)
		{
			var border_right = parseFloat(comp_container.css("border-right-width")).NaN0();
			left = offset.left + comp_container.outerWidth();
			start_left = left;
			this.uniqueElmt("flash_ad").addClass("embd_player_ad_right");
		}
		else
		{
			this.uniqueElmt("flash_ad").removeClass("embd_player_ad_right");
		}

		return {
			left: left,
			start_left: start_left
		};
	},

	/**
	 * Detects whether the user has flash.
	 * @return bool
	 */
	userHasFlash: function()
	{
		var version = deconcept.SWFObjectUtil.getPlayerVersion();
		return (version.major >= 9);
	}
});
;
