text: /*
   * jPlayer Plugin for jQuery JavaScript Library
   * http://www.jplayer.org
   *
   * Copyright (c) 2009 - 2011 Happyworm Ltd
   * Dual licensed under the MIT and GPL licenses.
   *  - http://www.opensource.org/licenses/mit-license.php
   *  - http://www.gnu.org/copyleft/gpl.html
   *
   * Author: Mark J Panaghiston
   * Version: 2.0.22
   * Date: 13th July 2011
   */
  
  /* Code verified using http://www.jshint.com/ */
  /*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, nomem:false, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false */
  /*global jQuery:false, ActiveXObject:false, alert:false */
  
  (function($, undefined) {
  
  	// Adapted from jquery.ui.widget.js (1.8.7): $.widget.bridge
  	$.fn.jPlayer = function( options ) {
  		var name = "jPlayer";
  		var isMethodCall = typeof options === "string",
  			args = Array.prototype.slice.call( arguments, 1 ),
  			returnValue = this;
  
  		// allow multiple hashes to be passed on init
  		options = !isMethodCall && args.length ?
  			$.extend.apply( null, [ true, options ].concat(args) ) :
  			options;
  
  		// prevent calls to internal methods
  		if ( isMethodCall && options.charAt( 0 ) === "_" ) {
  			return returnValue;
  		}
  
  		if ( isMethodCall ) {
  			this.each(function() {
  				var instance = $.data( this, name ),
  					methodValue = instance && $.isFunction( instance[options] ) ?
  						instance[ options ].apply( instance, args ) :
  						instance;
  				if ( methodValue !== instance && methodValue !== undefined ) {
  					returnValue = methodValue;
  					return false;
  				}
  			});
  		} else {
  			this.each(function() {
  				var instance = $.data( this, name );
  				if ( instance ) {
  					// instance.option( options || {} )._init(); // Orig jquery.ui.widget.js code: Not recommend for jPlayer. ie., Applying new options to an existing instance (via the jPlayer constructor) and performing the _init(). The _init() is what concerns me. It would leave a lot of event handlers acting on jPlayer instance and the interface.
  					instance.option( options || {} ); // The new constructor only changes the options. Changing options only has basic support atm.
  				} else {
  					$.data( this, name, new $.jPlayer( options, this ) );
  				}
  			});
  		}
  
  		return returnValue;
  	};
  
  	$.jPlayer = function( options, element ) {
  		// allow instantiation without initializing for simple inheritance
  		if ( arguments.length ) {
  			this.element = $(element);
  			this.options = $.extend(true, {},
  				this.options,
  				options
  			);
  			var self = this;
  			this.element.bind( "remove.jPlayer", function() {
  				self.destroy();
  			});
  			this._init();
  		}
  	};
  	// End of: (Adapted from jquery.ui.widget.js (1.8.7))
  
  	// Emulated HTML5 methods and properties
  	$.jPlayer.emulateMethods = "load play pause";
  	$.jPlayer.emulateStatus = "src readyState networkState currentTime duration paused ended playbackRate";
  	$.jPlayer.emulateOptions = "muted volume";
  
  	// Reserved event names generated by jPlayer that are not part of the HTML5 Media element spec
  	$.jPlayer.reservedEvent = "ready flashreset resize repeat error warning";
  
  	// Events generated by jPlayer
  	$.jPlayer.event = {
  		ready: "jPlayer_ready",
  		flashreset: "jPlayer_flashreset", // Similar to the ready event if the Flash solution is set to display:none and then shown again or if it's reloaded for another reason by the browser. For example, using CSS position:fixed on Firefox for the full screen feature.
  		resize: "jPlayer_resize", // Occurs when the size changes through a full/restore screen operation or if the size/sizeFull options are changed.
  		repeat: "jPlayer_repeat", // Occurs when the repeat status changes. Usually through clicks on the repeat button of the interface.
  		error: "jPlayer_error", // Event error code in event.jPlayer.error.type. See $.jPlayer.error
  		warning: "jPlayer_warning", // Event warning code in event.jPlayer.warning.type. See $.jPlayer.warning
  
  		// Other events match HTML5 spec.
  		loadstart: "jPlayer_loadstart",
  		progress: "jPlayer_progress",
  		suspend: "jPlayer_suspend",
  		abort: "jPlayer_abort",
  		emptied: "jPlayer_emptied",
  		stalled: "jPlayer_stalled",
  		play: "jPlayer_play",
  		pause: "jPlayer_pause",
  		loadedmetadata: "jPlayer_loadedmetadata",
  		loadeddata: "jPlayer_loadeddata",
  		waiting: "jPlayer_waiting",
  		playing: "jPlayer_playing",
  		canplay: "jPlayer_canplay",
  		canplaythrough: "jPlayer_canplaythrough",
  		seeking: "jPlayer_seeking",
  		seeked: "jPlayer_seeked",
  		timeupdate: "jPlayer_timeupdate",
  		ended: "jPlayer_ended",
  		ratechange: "jPlayer_ratechange",
  		durationchange: "jPlayer_durationchange",
  		volumechange: "jPlayer_volumechange"
  	};
  
  	$.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action.
  		"loadstart",
  		// "progress", // jPlayer uses internally before bubbling.
  		// "suspend", // jPlayer uses internally before bubbling.
  		"abort",
  		// "error", // jPlayer uses internally before bubbling.
  		"emptied",
  		"stalled",
  		// "play", // jPlayer uses internally before bubbling.
  		// "pause", // jPlayer uses internally before bubbling.
  		"loadedmetadata",
  		"loadeddata",
  		// "waiting", // jPlayer uses internally before bubbling.
  		// "playing", // jPlayer uses internally before bubbling.
  		"canplay",
  		"canplaythrough",
  		// "seeking", // jPlayer uses internally before bubbling.
  		// "seeked", // jPlayer uses internally before bubbling.
  		// "timeupdate", // jPlayer uses internally before bubbling.
  		// "ended", // jPlayer uses internally before bubbling.
  		"ratechange"
  		// "durationchange" // jPlayer uses internally before bubbling.
  		// "volumechange" // jPlayer uses internally before bubbling.
  	];
  
  	$.jPlayer.pause = function() {
  		// $.each($.jPlayer.instances, function(i, element) {
  		$.each($.jPlayer.prototype.instances, function(i, element) {
  			if(element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event.
  				element.jPlayer("pause");
  			}
  		});
  	};
  	
  	$.jPlayer.timeFormat = {
  		showHour: false,
  		showMin: true,
  		showSec: true,
  		padHour: false,
  		padMin: true,
  		padSec: true,
  		sepHour: ":",
  		sepMin: ":",
  		sepSec: ""
  	};
  
  	$.jPlayer.convertTime = function(s) {
  		var myTime = new Date(s * 1000);
  		var hour = myTime.getUTCHours();
  		var min = myTime.getUTCMinutes();
  		var sec = myTime.getUTCSeconds();
  		var strHour = ($.jPlayer.timeFormat.padHour && hour < 10) ? "0" + hour : hour;
  		var strMin = ($.jPlayer.timeFormat.padMin && min < 10) ? "0" + min : min;
  		var strSec = ($.jPlayer.timeFormat.padSec && sec < 10) ? "0" + sec : sec;
  		return (($.jPlayer.timeFormat.showHour) ? strHour + $.jPlayer.timeFormat.sepHour : "") + (($.jPlayer.timeFormat.showMin) ? strMin + $.jPlayer.timeFormat.sepMin : "") + (($.jPlayer.timeFormat.showSec) ? strSec + $.jPlayer.timeFormat.sepSec : "");
  	};
  
  	// Adapting jQuery 1.4.4 code for jQuery.browser. Required since jQuery 1.3.2 does not detect Chrome as webkit.
  	$.jPlayer.uaBrowser = function( userAgent ) {
  		var ua = userAgent.toLowerCase();
  
  		// Useragent RegExp
  		var rwebkit = /(webkit)[ \/]([\w.]+)/;
  		var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
  		var rmsie = /(msie) ([\w.]+)/;
  		var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
  
  		var match = rwebkit.exec( ua ) ||
  			ropera.exec( ua ) ||
  			rmsie.exec( ua ) ||
  			ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
  			[];
  
  		return { browser: match[1] || "", version: match[2] || "0" };
  	};
  
  	// Platform sniffer for detecting mobile devices
  	$.jPlayer.uaPlatform = function( userAgent ) {
  		var ua = userAgent.toLowerCase();
  
  		// Useragent RegExp
  		var rplatform = /(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/;
  		var rtablet = /(ipad|playbook)/;
  		var randroid = /(android)/;
  		var rmobile = /(mobile)/;
  
  		var platform = rplatform.exec( ua ) || [];
  		var tablet = rtablet.exec( ua ) ||
  			!rmobile.exec( ua ) && randroid.exec( ua ) ||
  			[];
  
  		return { platform: platform[1] || "", tablet: tablet[1] || "" };
  	};
  
  	$.jPlayer.browser = {
  	};
  	$.jPlayer.platform = {
  	};
  
  	var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent);
  	if ( browserMatch.browser ) {
  		$.jPlayer.browser[ browserMatch.browser ] = true;
  		$.jPlayer.browser.version = browserMatch.version;
  	}
  	var platformMatch = $.jPlayer.uaPlatform(navigator.userAgent);
  	if ( platformMatch.platform ) {
  		$.jPlayer.platform[ platformMatch.platform ] = true;
  		$.jPlayer.platform.mobile = !platformMatch.tablet;
  		$.jPlayer.platform.tablet = !!platformMatch.tablet;
  	}
  
  	$.jPlayer.prototype = {
  		count: 0, // Static Variable: Change it via prototype.
  		version: { // Static Object
  			script: "2.0.22",
  			needFlash: "2.0.9",
  			flash: "unknown"
  		},
  		options: { // Instanced in $.jPlayer() constructor
  			swfPath: "js", // Path to Jplayer.swf. Can be relative, absolute or server root relative.
  			solution: "html, flash", // Valid solutions: html, flash. Order defines priority. 1st is highest,
  			supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest,
  			preload: 'metadata',  // HTML5 Spec values: none, metadata, auto.
  			volume: 0.8, // The volume. Number 0 to 1.
  			muted: false,
  			wmode: "opaque", // Valid wmode: window, transparent, opaque, direct, gpu. 
  			backgroundColor: "#000000", // To define the jPlayer div and Flash background color.
  			cssSelectorAncestor: "#jp_container_1",
  			cssSelector: { // * denotes properties that should only be required when video media type required. _cssSelector() would require changes to enable splitting these into Audio and Video defaults.
  				videoPlay: ".jp-video-play", // *
  				play: ".jp-play",
  				pause: ".jp-pause",
  				stop: ".jp-stop",
  				seekBar: ".jp-seek-bar",
  				playBar: ".jp-play-bar",
  				mute: ".jp-mute",
  				unmute: ".jp-unmute",
  				volumeBar: ".jp-volume-bar",
  				volumeBarValue: ".jp-volume-bar-value",
  				volumeMax: ".jp-volume-max",
  				currentTime: ".jp-current-time",
  				duration: ".jp-duration",
  				fullScreen: ".jp-full-screen", // *
  				restoreScreen: ".jp-restore-screen", // *
  				repeat: ".jp-repeat",
  				repeatOff: ".jp-repeat-off",
  				gui: ".jp-gui" // The interface used with autohide feature.
  			},
  			fullScreen: false,
  			autohide: {
  				restored: false, // Controls the interface autohide feature.
  				full: true, // Controls the interface autohide feature.
  				fadeIn: 200, // Milliseconds. The period of the fadeIn anim.
  				fadeOut: 600, // Milliseconds. The period of the fadeOut anim.
  				hold: 1000 // Milliseconds. The period of the pause before autohide beings.
  			},
  			loop: false,
  			repeat: function(event) { // The default jPlayer repeat event handler
  				if(event.jPlayer.options.loop) {
  					$(this).unbind(".jPlayerRepeat").bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function() {
  						$(this).jPlayer("play");
  					});
  				} else {
  					$(this).unbind(".jPlayerRepeat");
  				}
  			},
  			// globalVolume: false, // Not implemented: Set to make volume changes affect all jPlayer instances
  			// globalMute: false, // Not implemented: Set to make mute changes affect all jPlayer instances
  			idPrefix: "jp", // Prefix for the ids of html elements created by jPlayer. For flash, this must not include characters: . - + * / \
  			noConflict: "jQuery",
  			emulateHtml: false, // Emulates the HTML5 Media element on the jPlayer element.
  			errorAlerts: false,
  			warningAlerts: false
  		},
  		optionsAudio: {
  			size: {
  				width: "0px",
  				height: "0px",
  				cssClass: ""
  			},
  			sizeFull: {
  				width: "0px",
  				height: "0px",
  				cssClass: ""
  			}
  		},
  		optionsVideo: {
  			size: {
  				width: "480px",
  				height: "270px",
  				cssClass: "jp-video-270p"
  			},
  			sizeFull: {
  				width: "100%",
  				height: "100%",
  				cssClass: "jp-video-full"
  			}
  		},
  		instances: {}, // Static Object
  		status: { // Instanced in _init()
  			src: "",
  			media: {},
  			paused: true,
  			format: {},
  			formatType: "",
  			waitForPlay: true, // Same as waitForLoad except in case where preloading.
  			waitForLoad: true,
  			srcSet: false,
  			video: false, // True if playing a video
  			seekPercent: 0,
  			currentPercentRelative: 0,
  			currentPercentAbsolute: 0,
  			currentTime: 0,
  			duration: 0,
  			readyState: 0,
  			networkState: 0,
  			playbackRate: 1,
  			ended: 0
  
  /*		Persistant status properties created dynamically at _init():
  			width
  			height
  			cssClass
  */
  		},
  
  		internal: { // Instanced in _init()
  			ready: false
  			// instance: undefined,
  			// domNode: undefined,
  			// htmlDlyCmdId: undefined
  			// autohideId: undefined
  		},
  		solution: { // Static Object: Defines the solutions built in jPlayer.
  			html: true,
  			flash: true
  		},
  		// 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"')
  		format: { // Static Object
  			mp3: {
  				codec: 'audio/mpeg; codecs="mp3"',
  				flashCanPlay: true,
  				media: 'audio'
  			},
  			m4a: { // AAC / MP4
  				codec: 'audio/mp4; codecs="mp4a.40.2"',
  				flashCanPlay: true,
  				media: 'audio'
  			},
  			oga: { // OGG
  				codec: 'audio/ogg; codecs="vorbis"',
  				flashCanPlay: false,
  				media: 'audio'
  			},
  			wav: { // PCM
  				codec: 'audio/wav; codecs="1"',
  				flashCanPlay: false,
  				media: 'audio'
  			},
  			webma: { // WEBM
  				codec: 'audio/webm; codecs="vorbis"',
  				flashCanPlay: false,
  				media: 'audio'
  			},
  			fla: { // FLV / F4A
  				codec: 'audio/x-flv',
  				flashCanPlay: true,
  				media: 'audio'
  			},
  			m4v: { // H.264 / MP4
  				codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
  				flashCanPlay: true,
  				media: 'video'
  			},
  			ogv: { // OGG
  				codec: 'video/ogg; codecs="theora, vorbis"',
  				flashCanPlay: false,
  				media: 'video'
  			},
  			webmv: { // WEBM
  				codec: 'video/webm; codecs="vorbis, vp8"',
  				flashCanPlay: false,
  				media: 'video'
  			},
  			flv: { // FLV / F4V
  				codec: 'video/x-flv',
  				flashCanPlay: true,
  				media: 'video'
  			}
  		},
  		_init: function() {
  			var self = this;
  			
  			this.element.empty();
  			
  			this.status = $.extend({}, this.status); // Copy static to unique instance.
  			this.internal = $.extend({}, this.internal); // Copy static to unique instance.
  
  			this.internal.domNode = this.element.get(0);
  
  			this.formats = []; // Array based on supplied string option. Order defines priority.
  			this.solutions = []; // Array based on solution string option. Order defines priority.
  			this.require = {}; // Which media types are required: video, audio.
  			
  			this.htmlElement = {}; // DOM elements created by jPlayer
  			this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
  			this.html.audio = {};
  			this.html.video = {};
  			this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
  			
  			this.css = {};
  			this.css.cs = {}; // Holds the css selector strings
  			this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method)
  
  			this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+
  
  			this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds.
  
  			// Create the formats array, with prority based on the order of the supplied formats string
  			$.each(this.options.supplied.toLowerCase().split(","), function(index1, value1) {
  				var format = value1.replace(/^\s+|\s+$/g, ""); //trim
  				if(self.format[format]) { // Check format is valid.
  					var dupFound = false;
  					$.each(self.formats, function(index2, value2) { // Check for duplicates
  						if(format === value2) {
  							dupFound = true;
  							return false;
  						}
  					});
  					if(!dupFound) {
  						self.formats.push(format);
  					}
  				}
  			});
  
  			// Create the solutions array, with prority based on the order of the solution string
  			$.each(this.options.solution.toLowerCase().split(","), function(index1, value1) {
  				var solution = value1.replace(/^\s+|\s+$/g, ""); //trim
  				if(self.solution[solution]) { // Check solution is valid.
  					var dupFound = false;
  					$.each(self.solutions, function(index2, value2) { // Check for duplicates
  						if(solution === value2) {
  							dupFound = true;
  							return false;
  						}
  					});
  					if(!dupFound) {
  						self.solutions.push(solution);
  					}
  				}
  			});
  
  			this.internal.instance = "jp_" + this.count;
  			this.instances[this.internal.instance] = this.element;
  
  			// Check the jPlayer div has an id and create one if required. Important for Flash to know the unique id for comms.
  			if(!this.element.attr("id")) {
  				this.element.attr("id", this.options.idPrefix + "_jplayer_" + this.count);
  			}
  
  			this.internal.self = $.extend({}, {
  				id: this.element.attr("id"),
  				jq: this.element
  			});
  			this.internal.audio = $.extend({}, {
  				id: this.options.idPrefix + "_audio_" + this.count,
  				jq: undefined
  			});
  			this.internal.video = $.extend({}, {
  				id: this.options.idPrefix + "_video_" + this.count,
  				jq: undefined
  			});
  			this.internal.flash = $.extend({}, {
  				id: this.options.idPrefix + "_flash_" + this.count,
  				jq: undefined,
  				swf: this.options.swfPath + ((this.options.swfPath !== "" && this.options.swfPath.slice(-1) !== "/") ? "/" : "") + "Jplayer.swf"
  			});
  			this.internal.poster = $.extend({}, {
  				id: this.options.idPrefix + "_poster_" + this.count,
  				jq: undefined
  			});
  
  			// Register listeners defined in the constructor
  			$.each($.jPlayer.event, function(eventName,eventType) {
  				if(self.options[eventName] !== undefined) {
  					self.element.bind(eventType + ".jPlayer", self.options[eventName]); // With .jPlayer namespace.
  					self.options[eventName] = undefined; // Destroy the handler pointer copy on the options. Reason, events can be added/removed in other ways so this could be obsolete and misleading.
  				}
  			});
  
  			// Determine if we require solutions for audio, video or both media types.
  			this.require.audio = false;
  			this.require.video = false;
  			$.each(this.formats, function(priority, format) {
  				self.require[self.format[format].media] = true;
  			});
  
  			// Now required types are known, finish the options default settings.
  			if(this.require.video) {
  				this.options = $.extend(true, {},
  					this.optionsVideo,
  					this.options
  				);
  			} else {
  				this.options = $.extend(true, {},
  					this.optionsAudio,
  					this.options
  				);
  			}
  			this._setSize(); // update status and jPlayer element size
  
  			// Create the poster image.
  			this.htmlElement.poster = document.createElement('img');
  			this.htmlElement.poster.id = this.internal.poster.id;
  			this.htmlElement.poster.onload = function() { // Note that this did not work on Firefox 3.6: poster.addEventListener("onload", function() {}, false); Did not investigate x-browser.
  				if(!self.status.video || self.status.waitForPlay) {
  					self.internal.poster.jq.show();
  				}
  			};
  			this.element.append(this.htmlElement.poster);
  			this.internal.poster.jq = $("#" + this.internal.poster.id);
  			this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height});
  			this.internal.poster.jq.hide();
  			
  			// Generate the required media elements
  			this.html.audio.available = false;
  			if(this.require.audio) { // If a supplied format is audio
  				this.htmlElement.audio = document.createElement('audio');
  				this.htmlElement.audio.id = this.internal.audio.id;
  				this.html.audio.available = !!this.htmlElement.audio.canPlayType;
  			}
  			this.html.video.available = false;
  			if(this.require.video) { // If a supplied format is video
  				this.htmlElement.video = document.createElement('video');
  				this.htmlElement.video.id = this.internal.video.id;
  				this.html.video.available = !!this.htmlElement.video.canPlayType;
  			}
  
  			this.flash.available = this._checkForFlash(10);
  
  			this.html.canPlay = {};
  			this.flash.canPlay = {};
  			$.each(this.formats, function(priority, format) {
  				self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec);
  				self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available;
  			});
  			this.html.desired = false;
  			this.flash.desired = false;
  			$.each(this.solutions, function(solutionPriority, solution) {
  				if(solutionPriority === 0) {
  					self[solution].desired = true;
  				} else {
  					var audioCanPlay = false;
  					var videoCanPlay = false;
  					$.each(self.formats, function(formatPriority, format) {
  						if(self[self.solutions[0]].canPlay[format]) { // The other solution can play
  							if(self.format[format].media === 'video') {
  								videoCanPlay = true;
  							} else {
  								audioCanPlay = true;
  							}
  						}
  					});
  					self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay);
  				}
  			});
  			// This is what jPlayer will support, based on solution and supplied.
  			this.html.support = {};
  			this.flash.support = {};
  			$.each(this.formats, function(priority, format) {
  				self.html.support[format] = self.html.canPlay[format] && self.html.desired;
  				self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired;
  			});
  			// If jPlayer is supporting any format in a solution, then the solution is used.
  			this.html.used = false;
  			this.flash.used = false;
  			$.each(this.solutions, function(solutionPriority, solution) {
  				$.each(self.formats, function(formatPriority, format) {
  					if(self[solution].support[format]) {
  						self[solution].used = true;
  						return false;
  					}
  				});
  			});
  
  			// Init solution active state and the event gates to false.
  			this.html.active = false;
  			this.html.audio.gate = false;
  			this.html.video.gate = false;
  			this.flash.active = false;
  			this.flash.gate = false;
  
  			// Set up the css selectors for the control and feedback entities.
  			this._cssSelectorAncestor(this.options.cssSelectorAncestor);
  			
  			// If neither html nor flash are being used by this browser, then media playback is not possible. Trigger an error event.
  			if(!(this.html.used || this.flash.used)) {
  				this._error( {
  					type: $.jPlayer.error.NO_SOLUTION, 
  					context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}",
  					message: $.jPlayer.errorMsg.NO_SOLUTION,
  					hint: $.jPlayer.errorHint.NO_SOLUTION
  				});
  			}
  
  			// Add the flash solution if it is being used.
  			if(this.flash.used) {
  				var htmlObj,
  				flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted;
  
  				// Code influenced by SWFObject 2.2: http://code.google.com/p/swfobject/
  				// Non IE browsers have an initial Flash size of 1 by 1 otherwise the wmode affected the Flash ready event. 
  
  				if($.browser.msie && Number($.browser.version) <= 8) {
  					var objStr = '<object id="' + this.internal.flash.id + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="0" height="0"></object>';
  
  					var paramStr = [
  						'<param name="movie" value="' + this.internal.flash.swf + '" />',
  						'<param name="FlashVars" value="' + flashVars + '" />',
  						'<param name="allowScriptAccess" value="always" />',
  						'<param name="bgcolor" value="' + this.options.backgroundColor + '" />',
  						'<param name="wmode" value="' + this.options.wmode + '" />'
  					];
  
  					htmlObj = document.createElement(objStr);
  					for(var i=0; i < paramStr.length; i++) {
  						htmlObj.appendChild(document.createElement(paramStr[i]));
  					}
  				} else {
  					var createParam = function(el, n, v) {
  						var p = document.createElement("param");
  						p.setAttribute("name", n);	
  						p.setAttribute("value", v);
  						el.appendChild(p);
  					};
  
  					htmlObj = document.createElement("object");
  					htmlObj.setAttribute("id", this.internal.flash.id);
  					htmlObj.setAttribute("data", this.internal.flash.swf);
  					htmlObj.setAttribute("type", "application/x-shockwave-flash");
  					htmlObj.setAttribute("width", "1"); // Non-zero
  					htmlObj.setAttribute("height", "1"); // Non-zero
  					createParam(htmlObj, "flashvars", flashVars);
  					createParam(htmlObj, "allowscriptaccess", "always");
  					createParam(htmlObj, "bgcolor", this.options.backgroundColor);
  					createParam(htmlObj, "wmode", this.options.wmode);
  				}
  
  				this.element.append(htmlObj);
  				this.internal.flash.jq = $(htmlObj);
  			}
  			
  			// Add the HTML solution if being used.
  			if(this.html.used) {
  
  				// The HTML Audio handlers
  				if(this.html.audio.available) {
  					this._addHtmlEventListeners(this.htmlElement.audio, this.html.audio);
  					this.element.append(this.htmlElement.audio);
  					this.internal.audio.jq = $("#" + this.internal.audio.id);
  				}
  
  				// The HTML Video handlers
  				if(this.html.video.available) {
  					this._addHtmlEventListeners(this.htmlElement.video, this.html.video);
  					this.element.append(this.htmlElement.video);
  					this.internal.video.jq = $("#" + this.internal.video.id);
  					this.internal.video.jq.css({'width':'0px', 'height':'0px'}); // Using size 0x0 since a .hide() causes issues in iOS
  				}
  			}
  
  			// Create the bridge that emulates the HTML Media element on the jPlayer DIV
  			if( this.options.emulateHtml ) {
  				this._emulateHtmlBridge();
  			}
  
  			if(this.html.used && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms.
  				setTimeout( function() {
  					self.internal.ready = true;
  					self.version.flash = "n/a";
  					self._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
  					self._trigger($.jPlayer.event.ready);
  				}, 100);
  			}
  
  			// Initialize the interface components with the options.
  			this._updateInterface();
  			this._updateButtons(false);
  			this._updateAutohide();
  			this._updateVolume(this.options.volume);
  			this._updateMute(this.options.muted);
  			if(this.css.jq.videoPlay.length) {
  				this.css.jq.videoPlay.hide();
  			}
  
  			$.jPlayer.prototype.count++; // Change static variable via prototype.
  		},
  		destroy: function() {
  			// MJP: The background change remains. Would need to store the original to restore it correctly.
  
  			// Reset the interface, remove seeking effect and times.
  			this._resetStatus();
  			this._updateInterface();
  			this._seeked();
  			if(this.css.jq.currentTime.length) {
  				this.css.jq.currentTime.text("");
  			}
  			if(this.css.jq.duration.length) {
  				this.css.jq.duration.text("");
  			}
  
  			if(this.status.srcSet) { // Or you get a bogus error event
  				this.pause(); // Pauses the media and clears any delayed commands used in the HTML solution.
  			}
  			$.each(this.css.jq, function(fn, jq) { // Remove any bindings from the interface controls.
  				// Check selector is valid before trying to execute method.
  				if(jq.length) {
  					jq.unbind(".jPlayer");
  				}
  			});
  			if( this.options.emulateHtml ) {
  				this._destroyHtmlBridge();
  			}
  			this.element.removeData("jPlayer"); // Remove jPlayer data
  			this.element.unbind(".jPlayer"); // Remove all event handlers created by the jPlayer constructor
  			this.element.empty(); // Remove the inserted child elements
  			
  			this.instances[this.internal.instance] = undefined; // Clear the instance on the static instance object
  		},
  		enable: function() { // Plan to implement
  			// options.disabled = false
  		},
  		disable: function () { // Plan to implement
  			// options.disabled = true
  		},
  		_addHtmlEventListeners: function(mediaElement, entity) {
  			var self = this;
  			mediaElement.preload = this.options.preload;
  			mediaElement.muted = this.options.muted;
  			mediaElement.volume = this.options.volume;
  
  			// Create the event listeners
  			// Only want the active entity to affect jPlayer and bubble events.
  			// Using entity.gate so that object is referenced and gate property always current
  			
  			mediaElement.addEventListener("progress", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._getHtmlStatus(mediaElement);
  					self._updateInterface();
  					self._trigger($.jPlayer.event.progress);
  				}
  			}, false);
  			mediaElement.addEventListener("timeupdate", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._getHtmlStatus(mediaElement);
  					self._updateInterface();
  					self._trigger($.jPlayer.event.timeupdate);
  				}
  			}, false);
  			mediaElement.addEventListener("durationchange", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self.status.duration = this.duration;
  					self._getHtmlStatus(mediaElement);
  					self._updateInterface();
  					self._trigger($.jPlayer.event.durationchange);
  				}
  			}, false);
  			mediaElement.addEventListener("play", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._updateButtons(true);
  					self._trigger($.jPlayer.event.play);
  				}
  			}, false);
  			mediaElement.addEventListener("playing", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._updateButtons(true);
  					self._seeked();
  					self._trigger($.jPlayer.event.playing);
  				}
  			}, false);
  			mediaElement.addEventListener("pause", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._updateButtons(false);
  					self._trigger($.jPlayer.event.pause);
  				}
  			}, false);
  			mediaElement.addEventListener("waiting", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._seeking();
  					self._trigger($.jPlayer.event.waiting);
  				}
  			}, false);
  			mediaElement.addEventListener("seeking", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._seeking();
  					self._trigger($.jPlayer.event.seeking);
  				}
  			}, false);
  			mediaElement.addEventListener("seeked", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._seeked();
  					self._trigger($.jPlayer.event.seeked);
  				}
  			}, false);
  			mediaElement.addEventListener("volumechange", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					// Read the values back from the element as the Blackberry PlayBook shares the volume with the physical buttons master volume control.
  					// However, when tested 6th July 2011, those buttons do not generate an event. The physical play/pause button does though.
  					self.options.volume = mediaElement.volume;
  					self.options.muted = mediaElement.muted;
  					self._updateMute();
  					self._updateVolume();
  					self._trigger($.jPlayer.event.volumechange);
  				}
  			}, false);
  			mediaElement.addEventListener("suspend", function() { // Seems to be the only way of capturing that the iOS4 browser did not actually play the media from the page code. ie., It needs a user gesture.
  				if(entity.gate && !self.status.waitForLoad) {
  					self._seeked();
  					self._trigger($.jPlayer.event.suspend);
  				}
  			}, false);
  			mediaElement.addEventListener("ended", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					// Order of the next few commands are important. Change the time and then pause.
  					// Solves a bug in Firefox, where issuing pause 1st causes the media to play from the start. ie., The pause is ignored.
  					if(!$.jPlayer.browser.webkit) { // Chrome crashes if you do this in conjunction with a setMedia command in an ended event handler. ie., The playlist demo.
  						self.htmlElement.media.currentTime = 0; // Safari does not care about this command. ie., It works with or without this line. (Both Safari and Chrome are Webkit.)
  					}
  					self.htmlElement.media.pause(); // Pause otherwise a click on the progress bar will play from that point, when it shouldn't, since it stopped playback.
  					self._updateButtons(false);
  					self._getHtmlStatus(mediaElement, true); // With override true. Otherwise Chrome leaves progress at full.
  					self._updateInterface();
  					self._trigger($.jPlayer.event.ended);
  				}
  			}, false);
  			mediaElement.addEventListener("error", function() {
  				if(entity.gate && !self.status.waitForLoad) {
  					self._updateButtons(false);
  					self._seeked();
  					if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event.
  						clearTimeout(self.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution.
  						self.status.waitForLoad = true; // Allows the load operation to try again.
  						self.status.waitForPlay = true; // Reset since a play was captured.
  						if(self.status.video) {
  							self.internal.video.jq.css({'width':'0px', 'height':'0px'});
  						}
  						if(self._validString(self.status.media.poster)) {
  							self.internal.poster.jq.show();
  						}
  						if(self.css.jq.videoPlay.length) {
  							self.css.jq.videoPlay.show();
  						}
  						self._error( {
  							type: $.jPlayer.error.URL,
  							context: self.status.src, // this.src shows absolute urls. Want context to show the url given.
  							message: $.jPlayer.errorMsg.URL,
  							hint: $.jPlayer.errorHint.URL
  						});
  					}
  				}
  			}, false);
  			// Create all the other event listeners that bubble up to a jPlayer event from html, without being used by jPlayer.
  			$.each($.jPlayer.htmlEvent, function(i, eventType) {
  				mediaElement.addEventListener(this, function() {
  					if(entity.gate && !self.status.waitForLoad) {
  						self._trigger($.jPlayer.event[eventType]);
  					}
  				}, false);
  			});
  		},
  		_getHtmlStatus: function(media, override) {
  			var ct = 0, d = 0, cpa = 0, sp = 0, cpr = 0;
  
  			if(media.duration) { // Fixes the duration bug in iOS, where the durationchange event occurs when media.duration is not always correct.
  				this.status.duration = media.duration;
  			}
  			ct = media.currentTime;
  			cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0;
  			if((typeof media.seekable === "object") && (media.seekable.length > 0)) {
  				sp = (this.status.duration > 0) ? 100 * media.seekable.end(media.seekable.length-1) / this.status.duration : 100;
  				cpr = 100 * media.currentTime / media.seekable.end(media.seekable.length-1);
  			} else {
  				sp = 100;
  				cpr = cpa;
  			}
  			
  			if(override) {
  				ct = 0;
  				cpr = 0;
  				cpa = 0;
  			}
  
  			this.status.seekPercent = sp;
  			this.status.currentPercentRelative = cpr;
  			this.status.currentPercentAbsolute = cpa;
  			this.status.currentTime = ct;
  
  			this.status.readyState = media.readyState;
  			this.status.networkState = media.networkState;
  			this.status.playbackRate = media.playbackRate;
  			this.status.ended = media.ended;
  		},
  		_resetStatus: function() {
  			this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset.
  		},
  		_trigger: function(eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType
  			var event = $.Event(eventType);
  			event.jPlayer = {};
  			event.jPlayer.version = $.extend({}, this.version);
  			event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy
  			event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy
  			event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy
  			event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy
  			if(error) {
  				event.jPlayer.error = $.extend({}, error);
  			}
  			if(warning) {
  				event.jPlayer.warning = $.extend({}, warning);
  			}
  			this.element.trigger(event);
  		},
  		jPlayerFlashEvent: function(eventType, status) { // Called from Flash
  			if(eventType === $.jPlayer.event.ready) {
  				if(!this.internal.ready) {
  					this.internal.ready = true;
  					this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Once Flash generates the ready event, minimise to zero as it is not affected by wmode anymore.
  
  					this.version.flash = status.version;
  					if(this.version.needFlash !== this.version.flash) {
  						this._error( {
  							type: $.jPlayer.error.VERSION,
  							context: this.version.flash,
  							message: $.jPlayer.errorMsg.VERSION + this.version.flash,
  							hint: $.jPlayer.errorHint.VERSION
  						});
  					}
  					this._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
  					this._trigger(eventType);
  				} else {
  					// This condition occurs if the Flash is hidden and then shown again.
  					// Firefox also reloads the Flash if the CSS position changes. position:fixed is used for full screen.
  
  					// Only do this if the Flash is the solution being used at the moment. Affects Media players where both solution may be being used.
  					if(this.flash.gate) {
  
  						// Send the current status to the Flash now that it is ready (available) again.
  						if(this.status.srcSet) {
  
  							// Need to read original status before issuing the setMedia command.
  							var	currentTime = this.status.currentTime,
  								paused = this.status.paused; 
  
  							this.setMedia(this.status.media);
  							if(currentTime > 0) {
  								if(paused) {
  									this.pause(currentTime);
  								} else {
  									this.play(currentTime);
  								}
  							}
  						}
  						this._trigger($.jPlayer.event.flashreset);
  					}
  				}
  			}
  			if(this.flash.gate) {
  				switch(eventType) {
  					case $.jPlayer.event.progress:
  						this._getFlashStatus(status);
  						this._updateInterface();
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.timeupdate:
  						this._getFlashStatus(status);
  						this._updateInterface();
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.play:
  						this._seeked();
  						this._updateButtons(true);
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.pause:
  						this._updateButtons(false);
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.ended:
  						this._updateButtons(false);
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.error:
  						this.status.waitForLoad = true; // Allows the load operation to try again.
  						this.status.waitForPlay = true; // Reset since a play was captured.
  						if(this.status.video) {
  							this.internal.flash.jq.css({'width':'0px', 'height':'0px'});
  						}
  						if(this._validString(this.status.media.poster)) {
  							this.internal.poster.jq.show();
  						}
  						if(this.css.jq.videoPlay.length) {
  							this.css.jq.videoPlay.show();
  						}
  						if(this.status.video) { // Set up for another try. Execute before error event.
  							this._flash_setVideo(this.status.media);
  						} else {
  							this._flash_setAudio(this.status.media);
  						}
  						this._error( {
  							type: $.jPlayer.error.URL,
  							context:status.src,
  							message: $.jPlayer.errorMsg.URL,
  							hint: $.jPlayer.errorHint.URL
  						});
  						break;
  					case $.jPlayer.event.seeking:
  						this._seeking();
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.seeked:
  						this._seeked();
  						this._trigger(eventType);
  						break;
  					case $.jPlayer.event.ready:
  						// The ready event is handled outside the switch statement.
  						// Captured here otherwise 2 ready events would be generated if the ready event handler used setMedia.
  						break;
  					default:
  						this._trigger(eventType);
  				}
  			}
  			return false;
  		},
  		_getFlashStatus: function(status) {
  			this.status.seekPercent = status.seekPercent;
  			this.status.currentPercentRelative = status.currentPercentRelative;
  			this.status.currentPercentAbsolute = status.currentPercentAbsolute;
  			this.status.currentTime = status.currentTime;
  			this.status.duration = status.duration;
  
  			// The Flash does not generate this information in this release
  			this.status.readyState = 4; // status.readyState;
  			this.status.networkState = 0; // status.networkState;
  			this.status.playbackRate = 1; // status.playbackRate;
  			this.status.ended = false; // status.ended;
  		},
  		_updateButtons: function(playing) {
  			if(playing !== undefined) {
  				this.status.paused = !playing;
  				if(this.css.jq.play.length && this.css.jq.pause.length) {
  					if(playing) {
  						this.css.jq.play.hide();
  						this.css.jq.pause.show();
  					} else {
  						this.css.jq.play.show();
  						this.css.jq.pause.hide();
  					}
  				}
  			}
  			if(this.css.jq.restoreScreen.length && this.css.jq.fullScreen.length) {
  				if(this.options.fullScreen) {
  					this.css.jq.fullScreen.hide();
  					this.css.jq.restoreScreen.show();
  				} else {
  					this.css.jq.fullScreen.show();
  					this.css.jq.restoreScreen.hide();
  				}
  			}
  			if(this.css.jq.repeat.length && this.css.jq.repeatOff.length) {
  				if(this.options.loop) {
  					this.css.jq.repeat.hide();
  					this.css.jq.repeatOff.show();
  				} else {
  					this.css.jq.repeat.show();
  					this.css.jq.repeatOff.hide();
  				}
  			}
  		},
  		_updateInterface: function() {
  			if(this.css.jq.seekBar.length) {
  				this.css.jq.seekBar.width(this.status.seekPercent+"%");
  			}
  			if(this.css.jq.playBar.length) {
  				this.css.jq.playBar.width(this.status.currentPercentRelative+"%");
  			}
  			if(this.css.jq.currentTime.length) {
  				this.css.jq.currentTime.text($.jPlayer.convertTime(this.status.currentTime));
  			}
  			if(this.css.jq.duration.length) {
  				this.css.jq.duration.text($.jPlayer.convertTime(this.status.duration));
  			}
  		},
  		_seeking: function() {
  			if(this.css.jq.seekBar.length) {
  				this.css.jq.seekBar.addClass("jp-seeking-bg");
  			}
  		},
  		_seeked: function() {
  			if(this.css.jq.seekBar.length) {
  				this.css.jq.seekBar.removeClass("jp-seeking-bg");
  			}
  		},
  		setMedia: function(media) {
  		
  			/*	media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats.
  			 *	media.poster = String: Video poster URL.
  			 *	media.subtitles = String: * NOT IMPLEMENTED * URL of subtitles SRT file
  			 *	media.chapters = String: * NOT IMPLEMENTED * URL of chapters SRT file
  			 *	media.stream = Boolean: * NOT IMPLEMENTED * Designating actual media streams. ie., "false/undefined" for files. Plan to refresh the flash every so often.
  			 */
  			
  			var self = this;
  			
  			this._seeked();
  			clearTimeout(this.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution.
  
  			// Store the current html gates, since we need for clearMedia() conditions.
  			var audioGate = this.html.audio.gate;
  			var videoGate = this.html.video.gate;
  
  			var supported = false;
  			$.each(this.formats, function(formatPriority, format) {
  				var isVideo = self.format[format].media === 'video';
  				$.each(self.solutions, function(solutionPriority, solution) {
  					if(self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format.
  						var isHtml = solution === 'html';
  						
  						if(isVideo) {
  							if(isHtml) {
  								self.html.audio.gate = false;
  								self.html.video.gate = true;
  								self.flash.gate = false;
  							} else {
  								self.html.audio.gate = false;
  								self.html.video.gate = false;
  								self.flash.gate = true;
  							}
  						} else {
  							if(isHtml) {
  								self.html.audio.gate = true;
  								self.html.video.gate = false;
  								self.flash.gate = false;
  							} else {
  								self.html.audio.gate = false;
  								self.html.video.gate = false;
  								self.flash.gate = true;
  							}
  						}
  
  						// Clear media of the previous solution if:
  						//  - it was Flash
  						//  - changing from HTML to Flash
  						//  - the HTML solution media type (audio or video) remained the same.
  						// Note that, we must be careful with clearMedia() on iPhone, otherwise clearing the video when changing to audio corrupts the built in video player.
  						if(self.flash.active || (self.html.active && self.flash.gate) || (audioGate === self.html.audio.gate && videoGate === self.html.video.gate)) {
  							self.clearMedia();
  						} else if(audioGate !== self.html.audio.gate && videoGate !== self.html.video.gate) { // If switching between html elements
  							self._html_pause();
  							// Hide the video if it was being used.
  							if(self.status.video) {
  								self.internal.video.jq.css({'width':'0px', 'height':'0px'});
  							}
  							self._resetStatus(); // Since clearMedia usually does this. Execute after status.video useage.
  						}
  
  						if(isVideo) {
  							if(isHtml) {
  								self._html_setVideo(media);
  								self.html.active = true;
  								self.flash.active = false;
  							} else {
  								self._flash_setVideo(media);
  								self.html.active = false;
  								self.flash.active = true;
  							}
  							if(self.css.jq.videoPlay.length) {
  								self.css.jq.videoPlay.show();
  							}
  							self.status.video = true;
  						} else {
  							if(isHtml) {
  								self._html_setAudio(media);
  								self.html.active = true;
  								self.flash.active = false;
  							} else {
  								self._flash_setAudio(media);
  								self.html.active = false;
  								self.flash.active = true;
  							}
  							if(self.css.jq.videoPlay.length) {
  								self.css.jq.videoPlay.hide();
  							}
  							self.status.video = false;
  						}
  						
  						supported = true;
  						return false; // Exit $.each
  					}
  				});
  				if(supported) {
  					return false; // Exit $.each
  				}
  			});
  
  			if(supported) {
  				// Set poster after the possible clearMedia() command above. IE had issues since the IMG onload event occurred immediately when cached. ie., The clearMedia() hide the poster.
  				if(this._validString(media.poster)) {
  					if(this.htmlElement.poster.src !== media.poster) { // Since some browsers do not generate img onload event.
  						this.htmlElement.poster.src = media.poster;
  					} else {
  						this.internal.poster.jq.show();
  					}
  				} else {
  					this.internal.poster.jq.hide(); // Hide if not used, since clearMedia() does not always occur above. ie., HTML audio <-> video switching.
  				}
  				this.status.srcSet = true;
  				this.status.media = $.extend({}, media);
  				this._updateButtons(false);
  				this._updateInterface();
  			} else { // jPlayer cannot support any formats provided in this browser
  				// Pause here if old media could be playing. Otherwise, playing media being changed to bad media would leave the old media playing.
  				if(this.status.srcSet && !this.status.waitForPlay) {
  					this.pause();
  				}
  				// Reset all the control flags
  				this.html.audio.gate = false;
  				this.html.video.gate = false;
  				this.flash.gate = false;
  				this.html.active = false;
  				this.flash.active = false;
  				// Reset status and interface.
  				this._resetStatus();
  				this._updateInterface();
  				this._updateButtons(false);
  				// Hide the any old media
  				this.internal.poster.jq.hide();
  				if(this.html.used && this.require.video) {
  					this.internal.video.jq.css({'width':'0px', 'height':'0px'});
  				}
  				if(this.flash.used) {
  					this.internal.flash.jq.css({'width':'0px', 'height':'0px'});
  				}
  				// Send an error event
  				this._error( {
  					type: $.jPlayer.error.NO_SUPPORT,
  					context: "{supplied:'" + this.options.supplied + "'}",
  					message: $.jPlayer.errorMsg.NO_SUPPORT,
  					hint: $.jPlayer.errorHint.NO_SUPPORT
  				});
  			}
  		},
  		clearMedia: function() {
  			this._resetStatus();
  			this._updateButtons(false);
  
  			this.internal.poster.jq.hide();
  
  			clearTimeout(this.internal.htmlDlyCmdId);
  
  			if(this.html.active) {
  				this._html_clearMedia();
  			} else if(this.flash.active) {
  				this._flash_clearMedia();
  			}
  		},
  		load: function() {
  			if(this.status.srcSet) {
  				if(this.html.active) {
  					this._html_load();
  				} else if(this.flash.active) {
  					this._flash_load();
  				}
  			} else {
  				this._urlNotSetError("load");
  			}
  		},
  		play: function(time) {
  			time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
  			if(this.status.srcSet) {
  				if(this.html.active) {
  					this._html_play(time);
  				} else if(this.flash.active) {
  					this._flash_play(time);
  				}
  			} else {
  				this._urlNotSetError("play");
  			}
  		},
  		videoPlay: function(e) { // Handles clicks on the play button over the video poster
  			this.play();
  		},
  		pause: function(time) {
  			time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
  			if(this.status.srcSet) {
  				if(this.html.active) {
  					this._html_pause(time);
  				} else if(this.flash.active) {
  					this._flash_pause(time);
  				}
  			} else {
  				this._urlNotSetError("pause");
  			}
  		},
  		pauseOthers: function() {
  			var self = this;
  			$.each(this.instances, function(i, element) {
  				if(self.element !== element) { // Do not this instance.
  					if(element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event.
  						element.jPlayer("pause");
  					}
  				}
  			});
  		},
  		stop: function() {
  			if(this.status.srcSet) {
  				if(this.html.active) {
  					this._html_pause(0);
  				} else if(this.flash.active) {
  					this._flash_pause(0);
  				}
  			} else {
  				this._urlNotSetError("stop");
  			}
  		},
  		playHead: function(p) {
  			p = this._limitValue(p, 0, 100);
  			if(this.status.srcSet) {
  				if(this.html.active) {
  					this._html_playHead(p);
  				} else if(this.flash.active) {
  					this._flash_playHead(p);
  				}
  			} else {
  				this._urlNotSetError("playHead");
  			}
  		},
  		_muted: function(muted) {
  			this.options.muted = muted;
  			if(this.html.used) {
  				this._html_mute(muted);
  			}
  			if(this.flash.used) {
  				this._flash_mute(muted);
  			}
  
  			// The HTML solution generates this event from the media element itself.
  			if(this.flash.gate) {
  				this._updateMute(muted);
  				this._updateVolume(this.options.volume);
  				this._trigger($.jPlayer.event.volumechange);
  			}
  		},
  		mute: function(mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted).
  			mute = mute === undefined ? true : !!mute;
  			this._muted(mute);
  		},
  		unmute: function(unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted).
  			unmute = unmute === undefined ? true : !!unmute;
  			this._muted(!unmute);
  		},
  		_updateMute: function(mute) {
  			if(mute === undefined) {
  				mute = this.options.muted;
  			}
  			if(this.css.jq.mute.length && this.css.jq.unmute.length) {
  				if(mute) {
  					this.css.jq.mute.hide();
  					this.css.jq.unmute.show();
  				} else {
  					this.css.jq.mute.show();
  					this.css.jq.unmute.hide();
  				}
  			}
  		},
  		volume: function(v) {
  			v = this._limitValue(v, 0, 1);
  			this.options.volume = v;
  
  			if(this.html.used) {
  				this._html_volume(v);
  			}
  			if(this.flash.used) {
  				this._flash_volume(v);
  			}
  
  			// The HTML solution generates this event from the media element itself.
  			if(this.flash.gate) {
  				this._updateVolume(v);
  				this._trigger($.jPlayer.event.volumechange);
  			}
  		},
  		volumeBar: function(e) { // Handles clicks on the volumeBar
  			if(this.css.jq.volumeBar.length) {
  				var offset = this.css.jq.volumeBar.offset();
  				var x = e.pageX - offset.left;
  				var w = this.css.jq.volumeBar.width();
  				var v = x/w;
  				this.volume(v);
  			}
  			if(this.options.muted) {
  				this._muted(false);
  			}
  		},
  		volumeBarValue: function(e) { // Handles clicks on the volumeBarValue
  			this.volumeBar(e);
  		},
  		_updateVolume: function(v) {
  			if(v === undefined) {
  				v = this.options.volume;
  			}
  			v = this.options.muted ? 0 : v;
  
  			if(this.css.jq.volumeBarValue.length) {
  				this.css.jq.volumeBarValue.width((v*100)+"%");
  			}
  		},
  		volumeMax: function() { // Handles clicks on the volume max
  			this.volume(1);
  			if(this.options.muted) {
  				this._muted(false);
  			}
  		},
  		_cssSelectorAncestor: function(ancestor) {
  			var self = this;
  			this.options.cssSelectorAncestor = ancestor;
  			this._removeUiClass();
  			this.ancestorJq = ancestor ? $(ancestor) : []; // Would use $() instead of [], but it is only 1.4+
  			if(ancestor && this.ancestorJq.length !== 1) { // So empty strings do not generate the warning.
  				this._warning( {
  					type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
  					context: ancestor,
  					message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.",
  					hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
  				});
  			}
  			this._addUiClass();
  			$.each(this.options.cssSelector, function(fn, cssSel) {
  				self._cssSelector(fn, cssSel);
  			});
  		},
  		_cssSelector: function(fn, cssSel) {
  			var self = this;
  			if(typeof cssSel === 'string') {
  				if($.jPlayer.prototype.options.cssSelector[fn]) {
  					if(this.css.jq[fn] && this.css.jq[fn].length) {
  						this.css.jq[fn].unbind(".jPlayer");
  					}
  					this.options.cssSelector[fn] = cssSel;
  					this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel;
  
  					if(cssSel) { // Checks for empty string
  						this.css.jq[fn] = $(this.css.cs[fn]);
  					} else {
  						this.css.jq[fn] = []; // To comply with the css.jq[fn].length check before its use. As of jQuery 1.4 could have used $() for an empty set. 
  					}
  
  					if(this.css.jq[fn].length) {
  						var handler = function(e) {
  							self[fn](e);
  							$(this).blur();
  							return false;
  						};
  						this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace
  					}
  
  					if(cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one.
  						this._warning( {
  							type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
  							context: this.css.cs[fn],
  							message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.css.jq[fn].length + " found for " + fn + " method.",
  							hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
  						});
  					}
  				} else {
  					this._warning( {
  						type: $.jPlayer.warning.CSS_SELECTOR_METHOD,
  						context: fn,
  						message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD,
  						hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD
  					});
  				}
  			} else {
  				this._warning( {
  					type: $.jPlayer.warning.CSS_SELECTOR_STRING,
  					context: cssSel,
  					message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING,
  					hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING
  				});
  			}
  		},
  		seekBar: function(e) { // Handles clicks on the seekBar
  			if(this.css.jq.seekBar) {
  				var offset = this.css.jq.seekBar.offset();
  				var x = e.pageX - offset.left;
  				var w = this.css.jq.seekBar.width();
  				var p = 100*x/w;
  				this.playHead(p);
  			}
  		},
  		playBar: function(e) { // Handles clicks on the playBar
  			this.seekBar(e);
  		},
  		repeat: function() { // Handle clicks on the repeat button
  			this._loop(true);
  		},
  		repeatOff: function() { // Handle clicks on the repeatOff button
  			this._loop(false);
  		},
  		_loop: function(loop) {
  			if(this.options.loop !== loop) {
  				this.options.loop = loop;
  				this._updateButtons();
  				this._trigger($.jPlayer.event.repeat);
  			}
  		},
  
  		// Plan to review the cssSelector method to cope with missing associated functions accordingly.
  
  		currentTime: function(e) { // Handles clicks on the text
  			// Added to avoid errors using cssSelector system for the text
  		},
  		duration: function(e) { // Handles clicks on the text
  			// Added to avoid errors using cssSelector system for the text
  		},
  		gui: function(e) { // Handles clicks on the gui
  			// Added to avoid errors using cssSelector system for the gui
  		},
  
  		// Options code adapted from ui.widget.js (1.8.7).  Made changes so the key can use dot notation. To match previous getData solution in jPlayer 1.
  		option: function(key, value) {
  			var options = key;
  
  			 // Enables use: options().  Returns a copy of options object
  			if ( arguments.length === 0 ) {
  				return $.extend( true, {}, this.options );
  			}
  
  			if(typeof key === "string") {
  				var keys = key.split(".");
  
  				 // Enables use: options("someOption")  Returns a copy of the option. Supports dot notation.
  				if(value === undefined) {
  
  					var opt = $.extend(true, {}, this.options);
  					for(var i = 0; i < keys.length; i++) {
  						if(opt[keys[i]] !== undefined) {
  							opt = opt[keys[i]];
  						} else {
  							this._warning( {
  								type: $.jPlayer.warning.OPTION_KEY,
  								context: key,
  								message: $.jPlayer.warningMsg.OPTION_KEY,
  								hint: $.jPlayer.warningHint.OPTION_KEY
  							});
  							return undefined;
  						}
  					}
  					return opt;
  				}
  
  				 // Enables use: options("someOptionObject", someObject}).  Creates: {someOptionObject:someObject}
  				 // Enables use: options("someOption", someValue).  Creates: {someOption:someValue}
  				 // Enables use: options("someOptionObject.someOption", someValue).  Creates: {someOptionObject:{someOption:someValue}}
  
  				options = {};
  				var opts = options;
  
  				for(var j = 0; j < keys.length; j++) {
  					if(j < keys.length - 1) {
  						opts[keys[j]] = {};
  						opts = opts[keys[j]];
  					} else {
  						opts[keys[j]] = value;
  					}
  				}
  			}
  
  			 // Otherwise enables use: options(optionObject).  Uses original object (the key)
  
  			this._setOptions(options);
  
  			return this;
  		},
  		_setOptions: function(options) {
  			var self = this;
  			$.each(options, function(key, value) { // This supports the 2 level depth that the options of jPlayer has. Would review if we ever need more depth.
  				self._setOption(key, value);
  			});
  
  			return this;
  		},
  		_setOption: function(key, value) {
  			var self = this;
  
  			// The ability to set options is limited at this time.
  
  			switch(key) {
  				case "volume" :
  					this.volume(value);
  					break;
  				case "muted" :
  					this._muted(value);
  					break;
  				case "cssSelectorAncestor" :
  					this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor.
  					break;
  				case "cssSelector" :
  					$.each(value, function(fn, cssSel) {
  						self._cssSelector(fn, cssSel); // NB: The option is set inside this function, after further validity checks.
  					});
  					break;
  				case "fullScreen" :
  					if(this.options[key] !== value) { // if changed
  						this._removeUiClass();
  						this.options[key] = value;
  						this._refreshSize();
  					}
  					break;
  				case "size" :
  					if(!this.options.fullScreen && this.options[key].cssClass !== value.cssClass) {
  						this._removeUiClass();
  					}
  					this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
  					this._refreshSize();
  					break;
  				case "sizeFull" :
  					if(this.options.fullScreen && this.options[key].cssClass !== value.cssClass) {
  						this._removeUiClass();
  					}
  					this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
  					this._refreshSize();
  					break;
  				case "autohide" :
  					this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
  					this._updateAutohide();
  					break;
  				case "loop" :
  					this._loop(value);
  					break;
  				case "emulateHtml" :
  					if(this.options[key] !== value) { // To avoid multiple event handlers being created, if true already.
  						this.options[key] = value;
  						if(value) {
  							this._emulateHtmlBridge();
  						} else {
  							this._destroyHtmlBridge();
  						}
  					}
  					break;
  			}
  
  			return this;
  		},
  		// End of: (Options code adapted from ui.widget.js)
  
  		_refreshSize: function() {
  			this._setSize(); // update status and jPlayer element size
  			this._addUiClass(); // update the ui class
  			this._updateSize(); // update internal sizes
  			this._updateButtons();
  			this._updateAutohide();
  			this._trigger($.jPlayer.event.resize);
  		},
  		_setSize: function() {
  			// Determine the current size from the options
  			if(this.options.fullScreen) {
  				this.status.width = this.options.sizeFull.width;
  				this.status.height = this.options.sizeFull.height;
  				this.status.cssClass = this.options.sizeFull.cssClass;
  			} else {
  				this.status.width = this.options.size.width;
  				this.status.height = this.options.size.height;
  				this.status.cssClass = this.options.size.cssClass;
  			}
  
  			// Set the size of the jPlayer area.
  			this.element.css({'width': this.status.width, 'height': this.status.height});
  		},
  		_addUiClass: function() {
  			if(this.ancestorJq.length) {
  				this.ancestorJq.addClass(this.status.cssClass);
  			}
  		},
  		_removeUiClass: function() {
  			if(this.ancestorJq.length) {
  				this.ancestorJq.removeClass(this.status.cssClass);
  			}
  		},
  		_updateSize: function() {
  			// The poster uses show/hide so can simply resize it.
  			this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height});
  
  			// Video html or flash resized if necessary at this time.
  			if(!this.status.waitForPlay) {
  				if(this.html.active && this.status.video) { // Only if video media
  					this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
  				}
  				else if(this.flash.active) {
  					this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height});
  				}
  			}
  		},
  		_updateAutohide: function() {
  			var	self = this,
  				event = "mousemove.jPlayer",
  				namespace = ".jPlayerAutohide",
  				eventType = event + namespace,
  				handler = function() {
  					self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() {
  						clearTimeout(self.internal.autohideId);
  						self.internal.autohideId = setTimeout( function() {
  							self.css.jq.gui.fadeOut(self.options.autohide.fadeOut);
  						}, self.options.autohide.hold);
  					});
  				};
  
  			clearTimeout(this.internal.autohideId);
  			this.element.unbind(namespace);
  			if(this.css.jq.gui.length) {
  				this.css.jq.gui.unbind(namespace);
  				if(this.options.fullScreen && this.options.autohide.full || !this.options.fullScreen && this.options.autohide.restored) {
  					this.element.bind(eventType, handler);
  					this.css.jq.gui.bind(eventType, handler);
  					this.css.jq.gui.hide();
  				} else {
  					this.css.jq.gui.stop(true, true).show(); // Need the stop() otherwise a change screen mode during the GUI fade out hides the GUI in the other mode.
  				}
  			}
  		},
  		fullScreen: function() {
  			this._setOption("fullScreen", true);
  		},
  		restoreScreen: function() {
  			this._setOption("fullScreen", false);
  		},
  		_html_initMedia: function() {
  			if(this.status.srcSet  && !this.status.waitForPlay) {
  				this.htmlElement.media.pause();
  			}
  			if(this.options.preload !== 'none') {
  				this._html_load();
  			}
  			this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution.
  		},
  		_html_setAudio: function(media) {
  			var self = this;
  			// Always finds a format due to checks in setMedia()
  			$.each(this.formats, function(priority, format) {
  				if(self.html.support[format] && media[format]) {
  					self.status.src = media[format];
  					self.status.format[format] = true;
  					self.status.formatType = format;
  					return false;
  				}
  			});
  			this.htmlElement.media = this.htmlElement.audio;
  			this._html_initMedia();
  		},
  		_html_setVideo: function(media) {
  			var self = this;
  			// Always finds a format due to checks in setMedia()
  			$.each(this.formats, function(priority, format) {
  				if(self.html.support[format] && media[format]) {
  					self.status.src = media[format];
  					self.status.format[format] = true;
  					self.status.formatType = format;
  					return false;
  				}
  			});
  			this.htmlElement.media = this.htmlElement.video;
  			this._html_initMedia();
  		},
  		_html_clearMedia: function() {
  			if(this.htmlElement.media) {
  				if(this.htmlElement.media.id === this.internal.video.id) {
  					this.internal.video.jq.css({'width':'0px', 'height':'0px'});
  				}
  				this.htmlElement.media.pause();
  				this.htmlElement.media.src = "";
  				this.htmlElement.media.load(); // Stops an old, "in progress" download from continuing the download. Triggers the loadstart, error and emptied events, due to the empty src. Also an abort event if a download was in progress.
  			}
  		},
  		_html_load: function() {
  			if(this.status.waitForLoad) {
  				this.status.waitForLoad = false;
  				this.htmlElement.media.src = this.status.src;
  				this.htmlElement.media.load();
  			}
  			clearTimeout(this.internal.htmlDlyCmdId);
  		},
  		_html_play: function(time) {
  			var self = this;
  			this._html_load(); // Loads if required and clears any delayed commands.
  
  			this.htmlElement.media.play(); // Before currentTime attempt otherwise Firefox 4 Beta never loads.
  
  			if(!isNaN(time)) {
  				try {
  					this.htmlElement.media.currentTime = time;
  				} catch(err) {
  					this.internal.htmlDlyCmdId = setTimeout(function() {
  						self.play(time);
  					}, 100);
  					return; // Cancel execution and wait for the delayed command.
  				}
  			}
  			this._html_checkWaitForPlay();
  		},
  		_html_pause: function(time) {
  			var self = this;
  			
  			if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
  				this._html_load(); // Loads if required and clears any delayed commands.
  			} else {
  				clearTimeout(this.internal.htmlDlyCmdId);
  			}
  
  			// Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime.
  			this.htmlElement.media.pause();
  
  			if(!isNaN(time)) {
  				try {
  					this.htmlElement.media.currentTime = time;
  				} catch(err) {
  					this.internal.htmlDlyCmdId = setTimeout(function() {
  						self.pause(time);
  					}, 100);
  					return; // Cancel execution and wait for the delayed command.
  				}
  			}
  			if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
  				this._html_checkWaitForPlay();
  			}
  		},
  		_html_playHead: function(percent) {
  			var self = this;
  			this._html_load(); // Loads if required and clears any delayed commands.
  			try {
  				if((typeof this.htmlElement.media.seekable === "object") && (this.htmlElement.media.seekable.length > 0)) {
  					this.htmlElement.media.currentTime = percent * this.htmlElement.media.seekable.end(this.htmlElement.media.seekable.length-1) / 100;
  				} else if(this.htmlElement.media.duration > 0 && !isNaN(this.htmlElement.media.duration)) {
  					this.htmlElement.media.currentTime = percent * this.htmlElement.media.duration / 100;
  				} else {
  					throw "e";
  				}
  			} catch(err) {
  				this.internal.htmlDlyCmdId = setTimeout(function() {
  					self.playHead(percent);
  				}, 100);
  				return; // Cancel execution and wait for the delayed command.
  			}
  			if(!this.status.waitForLoad) {
  				this._html_checkWaitForPlay();
  			}
  		},
  		_html_checkWaitForPlay: function() {
  			if(this.status.waitForPlay) {
  				this.status.waitForPlay = false;
  				if(this.css.jq.videoPlay.length) {
  					this.css.jq.videoPlay.hide();
  				}
  				if(this.status.video) {
  					this.internal.poster.jq.hide();
  					this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
  				}
  			}
  		},
  		_html_volume: function(v) {
  			if(this.html.audio.available) {
  				this.htmlElement.audio.volume = v;
  			}
  			if(this.html.video.available) {
  				this.htmlElement.video.volume = v;
  			}
  		},
  		_html_mute: function(m) {
  			if(this.html.audio.available) {
  				this.htmlElement.audio.muted = m;
  			}
  			if(this.html.video.available) {
  				this.htmlElement.video.muted = m;
  			}
  		},
  		_flash_setAudio: function(media) {
  			var self = this;
  			try {
  				// Always finds a format due to checks in setMedia()
  				$.each(this.formats, function(priority, format) {
  					if(self.flash.support[format] && media[format]) {
  						switch (format) {
  							case "m4a" :
  							case "fla" :
  								self._getMovie().fl_setAudio_m4a(media[format]);
  								break;
  							case "mp3" :
  								self._getMovie().fl_setAudio_mp3(media[format]);
  								break;
  						}
  						self.status.src = media[format];
  						self.status.format[format] = true;
  						self.status.formatType = format;
  						return false;
  					}
  				});
  
  				if(this.options.preload === 'auto') {
  					this._flash_load();
  					this.status.waitForLoad = false;
  				}
  			} catch(err) { this._flashError(err); }
  		},
  		_flash_setVideo: function(media) {
  			var self = this;
  			try {
  				// Always finds a format due to checks in setMedia()
  				$.each(this.formats, function(priority, format) {
  					if(self.flash.support[format] && media[format]) {
  						switch (format) {
  							case "m4v" :
  							case "flv" :
  								self._getMovie().fl_setVideo_m4v(media[format]);
  								break;
  						}
  						self.status.src = media[format];
  						self.status.format[format] = true;
  						self.status.formatType = format;
  						return false;
  					}
  				});
  
  				if(this.options.preload === 'auto') {
  					this._flash_load();
  					this.status.waitForLoad = false;
  				}
  			} catch(err) { this._flashError(err); }
  		},
  		_flash_clearMedia: function() {
  			this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Must do via CSS as setting attr() to zero causes a jQuery error in IE.
  			try {
  				this._getMovie().fl_clearMedia();
  			} catch(err) { this._flashError(err); }
  		},
  		_flash_load: function() {
  			try {
  				this._getMovie().fl_load();
  			} catch(err) { this._flashError(err); }
  			this.status.waitForLoad = false;
  		},
  		_flash_play: function(time) {
  			try {
  				this._getMovie().fl_play(time);
  			} catch(err) { this._flashError(err); }
  			this.status.waitForLoad = false;
  			this._flash_checkWaitForPlay();
  		},
  		_flash_pause: function(time) {
  			try {
  				this._getMovie().fl_pause(time);
  			} catch(err) { this._flashError(err); }
  			if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
  				this.status.waitForLoad = false;
  				this._flash_checkWaitForPlay();
  			}
  		},
  		_flash_playHead: function(p) {
  			try {
  				this._getMovie().fl_play_head(p);
  			} catch(err) { this._flashError(err); }
  			if(!this.status.waitForLoad) {
  				this._flash_checkWaitForPlay();
  			}
  		},
  		_flash_checkWaitForPlay: function() {
  			if(this.status.waitForPlay) {
  				this.status.waitForPlay = false;
  				if(this.css.jq.videoPlay.length) {
  					this.css.jq.videoPlay.hide();
  				}
  				if(this.status.video) {
  					this.internal.poster.jq.hide();
  					this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height});
  				}
  			}
  		},
  		_flash_volume: function(v) {
  			try {
  				this._getMovie().fl_volume(v);
  			} catch(err) { this._flashError(err); }
  		},
  		_flash_mute: function(m) {
  			try {
  				this._getMovie().fl_mute(m);
  			} catch(err) { this._flashError(err); }
  		},
  		_getMovie: function() {
  			return document[this.internal.flash.id];
  		},
  		_checkForFlash: function (version) {
  			// Function checkForFlash adapted from FlashReplace by Robert Nyman
  			// http://code.google.com/p/flashreplace/
  			var flashIsInstalled = false;
  			var flash;
  			if(window.ActiveXObject){
  				try{
  					flash = new ActiveXObject(("ShockwaveFlash.ShockwaveFlash." + version));
  					flashIsInstalled = true;
  				}
  				catch(e){
  					// Throws an error if the version isn't available			
  				}
  			}
  			else if(navigator.plugins && navigator.mimeTypes.length > 0){
  				flash = navigator.plugins["Shockwave Flash"];
  				if(flash){
  					var flashVersion = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1");
  					if(flashVersion >= version){
  						flashIsInstalled = true;
  					}
  				}
  			}
  			return flashIsInstalled;
  		},
  		_validString: function(url) {
  			return (url && typeof url === "string"); // Empty strings return false
  		},
  		_limitValue: function(value, min, max) {
  			return (value < min) ? min : ((value > max) ? max : value);
  		},
  		_urlNotSetError: function(context) {
  			this._error( {
  				type: $.jPlayer.error.URL_NOT_SET,
  				context: context,
  				message: $.jPlayer.errorMsg.URL_NOT_SET,
  				hint: $.jPlayer.errorHint.URL_NOT_SET
  			});
  		},
  		_flashError: function(error) {
  			var errorType;
  			if(!this.internal.ready) {
  				errorType = "FLASH";
  			} else {
  				errorType = "FLASH_DISABLED";
  			}
  			this._error( {
  				type: $.jPlayer.error[errorType],
  				context: this.internal.flash.swf,
  				message: $.jPlayer.errorMsg[errorType] + error.message,
  				hint: $.jPlayer.errorHint[errorType]
  			});
  
  		},
  		_error: function(error) {
  			this._trigger($.jPlayer.event.error, error);
  			if(this.options.errorAlerts) {
  				this._alert("Error!" + (error.message ? "\n\n" + error.message : "") + (error.hint ? "\n\n" + error.hint : "") + "\n\nContext: " + error.context);
  			}
  		},
  		_warning: function(warning) {
  			this._trigger($.jPlayer.event.warning, undefined, warning);
  			if(this.options.warningAlerts) {
  				this._alert("Warning!" + (warning.message ? "\n\n" + warning.message : "") + (warning.hint ? "\n\n" + warning.hint : "") + "\n\nContext: " + warning.context);
  			}
  		},
  		_alert: function(message) {
  			alert("jPlayer " + this.version.script + " : id='" + this.internal.self.id +"' : " + message);
  		},
  		_emulateHtmlBridge: function() {
  			var self = this,
  			methods = $.jPlayer.emulateMethods;
  
  			// Emulate methods on jPlayer's DOM element.
  			$.each( $.jPlayer.emulateMethods.split(/\s+/g), function(i, name) {
  				self.internal.domNode[name] = function(arg) {
  					self[name](arg);
  				};
  
  			});
  
  			// Bubble jPlayer events to its DOM element.
  			$.each($.jPlayer.event, function(eventName,eventType) {
  				var nativeEvent = true;
  				$.each( $.jPlayer.reservedEvent.split(/\s+/g), function(i, name) {
  					if(name === eventName) {
  						nativeEvent = false;
  						return false;
  					}
  				});
  				if(nativeEvent) {
  					self.element.bind(eventType + ".jPlayer.jPlayerHtml", function() { // With .jPlayer & .jPlayerHtml namespaces.
  						self._emulateHtmlUpdate();
  						var domEvent = document.createEvent("Event");
  						domEvent.initEvent(eventName, false, true);
  						self.internal.domNode.dispatchEvent(domEvent);
  					});
  				}
  				// The error event would require a special case
  			});
  
  			// IE9 has a readyState property on all elements. The document should have it, but all (except media) elements inherit it in IE9. This conflicts with Popcorn, which polls the readyState.
  		},
  		_emulateHtmlUpdate: function() {
  			var self = this;
  
  			$.each( $.jPlayer.emulateStatus.split(/\s+/g), function(i, name) {
  				self.internal.domNode[name] = self.status[name];
  			});
  			$.each( $.jPlayer.emulateOptions.split(/\s+/g), function(i, name) {
  				self.internal.domNode[name] = self.options[name];
  			});
  		},
  		_destroyHtmlBridge: function() {
  			var self = this;
  
  			// Bridge event handlers are also removed by destroy() through .jPlayer namespace.
  			this.element.unbind(".jPlayerHtml"); // Remove all event handlers created by the jPlayer bridge. So you can change the emulateHtml option.
  
  			// Remove the methods and properties
  			var emulated = $.jPlayer.emulateMethods + " " + $.jPlayer.emulateStatus + " " + $.jPlayer.emulateOptions;
  			$.each( emulated.split(/\s+/g), function(i, name) {
  				delete self.internal.domNode[name];
  			});
  		}
  	};
  
  	$.jPlayer.error = {
  		FLASH: "e_flash",
  		FLASH_DISABLED: "e_flash_disabled",
  		NO_SOLUTION: "e_no_solution",
  		NO_SUPPORT: "e_no_support",
  		URL: "e_url",
  		URL_NOT_SET: "e_url_not_set",
  		VERSION: "e_version"
  	};
  
  	$.jPlayer.errorMsg = {
  		FLASH: "jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ", // Used in: _flashError()
  		FLASH_DISABLED: "jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ", // Used in: _flashError()
  		NO_SOLUTION: "No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.", // Used in: _init()
  		NO_SUPPORT: "It is not possible to play any media format provided in setMedia() on this browser using your current options.", // Used in: setMedia()
  		URL: "Media URL could not be loaded.", // Used in: jPlayerFlashEvent() and _addHtmlEventListeners()
  		URL_NOT_SET: "Attempt to issue media playback commands, while no media url is set.", // Used in: load(), play(), pause(), stop() and playHead()
  		VERSION: "jPlayer " + $.jPlayer.prototype.version.script + " needs Jplayer.swf version " + $.jPlayer.prototype.version.needFlash + " but found " // Used in: jPlayerReady()
  	};
  
  	$.jPlayer.errorHint = {
  		FLASH: "Check your swfPath option and that Jplayer.swf is there.",
  		FLASH_DISABLED: "Check that you have not display:none; the jPlayer entity or any ancestor.",
  		NO_SOLUTION: "Review the jPlayer options: support and supplied.",
  		NO_SUPPORT: "Video or audio formats defined in the supplied option are missing.",
  		URL: "Check media URL is valid.",
  		URL_NOT_SET: "Use setMedia() to set the media URL.",
  		VERSION: "Update jPlayer files."
  	};
  
  	$.jPlayer.warning = {
  		CSS_SELECTOR_COUNT: "e_css_selector_count",
  		CSS_SELECTOR_METHOD: "e_css_selector_method",
  		CSS_SELECTOR_STRING: "e_css_selector_string",
  		OPTION_KEY: "e_option_key"
  	};
  
  	$.jPlayer.warningMsg = {
  		CSS_SELECTOR_COUNT: "The number of css selectors found did not equal one: ",
  		CSS_SELECTOR_METHOD: "The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",
  		CSS_SELECTOR_STRING: "The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",
  		OPTION_KEY: "The option requested in jPlayer('option') is undefined."
  	};
  
  	$.jPlayer.warningHint = {
  		CSS_SELECTOR_COUNT: "Check your css selector and the ancestor.",
  		CSS_SELECTOR_METHOD: "Check your method name.",
  		CSS_SELECTOR_STRING: "Check your css selector is a string.",
  		OPTION_KEY: "Check your option name."
  	};
  })(jQuery);
  