// = Apple Gracefully Degrading Media Playback =
//
// This script provides functionality for both creating video players
// that will use modern technology if possible, but fall back on QuickTime
// and possibly Flash to ensure the widest audience availability.
//
// Part of the functionality included herein is also video controls similar 
// to those in Show Leopard's QuickTime X Player.
//

/*jsl:ignoreall*/

var Media = {
    MIN_QUICKTIME_VERSION: '7.4',
    MIN_FLASH_VERSION: '9.0.115',

    create: function(container, src, options) {
        var element, innerface, controls, controller;
        
        // Do some browser-specific setup
        if (Media.Detection.Firefox()) {
            Element.addClassName(container, 'mozilla');
        }
        if (Media.Detection.IE()) {
            Media._createEventSource();
        }
        
        if (Media._isHTML5VideoAvailable()) {
            // Create <video> player
            return build(Media.Spec.Video);
        }
        
        if (Media._isQuickTimeAvailable(Media.MIN_QUICKTIME_VERSION)) {
            // Create QuickTime player
            return build(Media.Spec.QuickTime);
        }

        if (Media._isFlashAvailable(Media.MIN_FLASH_VERSION)) {
            // Create Flash player
            return build(Media.Spec.Flash);
        }

        // At this point we've determined there will be no video, 
        // so show a download prompt if we need to, then return false;
        if (Media._shouldShowDownloadPrompt()) {
            Media.createDownloadPrompt(container, src, options);
        }
        
        function build(Spec) {
            // First, create a new controller...
            controller = Media.Controller(container);

            // ...then a video player (with a corresponding video interface)...
            element = Spec.create(container, src, options);
            innerface = Media.VideoInterface(element, controller);

            // Now, customize the interface with events and/or polling, and custom methods.
            var eventsToRegister = Spec.eventsToRegister;
            for (var event in eventsToRegister) {
                var name = eventsToRegister[event].name || eventsToRegister[event],
                	fn = eventsToRegister[event].callback;
                
                innerface.registerEvent(event, name, fn);
            }
            
            if (Spec.pollForChanges)
                innerface.pollForChanges(Spec.pollForChanges);
                
            if (Spec.interfaceMethods)
                innerface.override(Spec.interfaceMethods);

            innerface.setup();
            
            // Finally, attach the video interface to the controller
            controller.setVideo(innerface);
            
            return controller;
        }
        
        return false;
    },    
	
    createDownloadPrompt: function (container, src, options)
    {
        var downloadNotice = document.createElement('a');
        Element.addClassName(downloadNotice, 'quicktime-download');
		if (typeof options.width !== 'undefined' && typeof options.height !== 'undefined') {
			downloadNotice.addClassName('size' + options.width + 'x' + options.height);
		}

        downloadNotice.setAttribute('href', 'http://www.apple.com/quicktime/download/');
        downloadNotice.innerHTML = options.downloadText || 'Get the latest QuickTime.';

        container.appendChild(downloadNotice);

        if ('fire' in Element) {
            Element.fire(document.body, 
                         "QuickTime:noCompatibleQTAvailable", 
                         {controller: this, minVersion: Media.MIN_QUICKTIME_VERSION});
        }
        
        return downloadNotice;
    },
    
    _isHTML5VideoAvailable: function ()
    {
        return Media.Detection.HTML5();
    },
    
    _isQuickTimeAvailable: function ()
    {
        return Media.Detection.QuickTime(Media.MIN_QUICKTIME_VERSION);
    },
    
    _isFlashAvailable: function ()
    {
        return Media.Detection.Flash(Media.MIN_FLASH_VERSION);
    },
    
    _shouldShowDownloadPrompt: function ()
    {
        return !Media.Detection.Mobile();
    },
    
    _createEventSource: function()
    {
        var source_id = "qt_event_source",
            behavior,
            head;

        // Don't recreate it if we already have one by this id
        if (document.getElementById(source_id)) {
            return;
        }

        behavior = document.createElement('object');
        behavior.id = source_id;
        behavior.setAttribute('clsid', 'CB927D12-4FF7-4a9e-A169-56E4B8A75598');

        head = document.getElementsByTagName('head')[0];
        head.appendChild(behavior);
    }
};

Media.Detection = {
    HTML5: function () {
        if (!('HTMLMediaElement' in window))
            return false;
        
        var video = document.createElement('video');
        return (video.canPlayType && video.canPlayType('video/mp4')!=='');
    },
    QuickTime: function(version) {
        return AC.Detector.isValidQTAvailable(version);
    },
    Flash: function(version) {
        return AC.Detector.isFlashAvailable(version);
    },
    Mobile: function () {
        return AC.Detector.isMobile();
    },
    IE: function () {
        return AC.Detector.isIEStrict();
    },
    Firefox: function () {
        return AC.Detector.isFirefox();
    }
};

// == Media.Spec ==
//
// Media.Spec is a list of the types of media that we can support, with
// specs on how to create and interact with each.
// 
// The create function is in charge of generating DOM elements, and appending 
// them to the container.
Media.Spec = {
    
    Video: {
        create: function (container, src, options) {
            var video = document.createElement('video'),
                qtSource = document.createElement('source');
            
            if (video.canPlayType('video/mp4')) {
                var id = options.id || (container.id ? container.id+'_video' : '');
                video.setAttribute('id', id);
                Element.addClassName(video, (video.playerType = 'video'));
            
                qtSource.setAttribute('src', src);
                qtSource.setAttribute('type', 'video/mp4');
                video.appendChild(qtSource);

                this._configure(video, options);

                Event.observe(window, 'unload', function () {
                     try {
                         video.stop();
                     } catch(e) {}

                     video.style.display = 'none';
                     video = null;
                 });
            
                container.appendChild(video);
            } else {
                // We need to create our fallback in case the browser supports <video>, but not our codec.
                // So... do that now
                video = this._createFallback(container, src, options);
            }
            
            return video;
        },
        
        eventsToRegister: {
            load: 'load',
            timeupdate: 'timeupdate',
            durationchange: 'durationchange',
            progress: 'progress',
            playing: 'playing',
            play: 'play',
            pause: 'pause',
            ended: 'ended'
        },
        
        interfaceMethods: {

            duration: function () {
                return this.duration;
            },
            time: function () {
                return this.currentTime;
            },
            setTime: function(value) {
                this.currentTime = value;
            },
            volume: function () {
                return this.volume;
            },
            setVolume: function(value) {
                this.volume = value;
            },
            muted: function () {
                return this.muted;
            },
            setMuted: function(value) {
                this.muted = value;
            },
            rate: function () {
                return this.playbackRate;
            },
            setRate: function (value) {
                this.playbackRate = value;
            },
            defaultRate: function () {
            	return this.defaultPlaybackRate;
            },
            src: function () {
            	return this.src;
            },
            setSrc: function (src) {
            	this.src = src;
            },
            status: function () {
                return this.status;
            },
            percentLoaded: function () {
                return this.buffered.end(0) / this.duration;
            },
            pause: function () {
                this.pause();
            },
            play: function () {
                this.play();
            },
            paused: function () {
                return this.paused;
            },
            ended: function () {
                return this.ended;
            },
            timeScale: function () {
            	return 2997;
            },
			movieType: function () {
				return 'Video';
			}
        },
        
        _configure: function (video, options)
        {
            if (!options) {
                return;
            }

            var property,
                attributeName;

            for (property in options) {
                if (options.hasOwnProperty(property)) {

                    attributeName = property.toLowerCase();

                    switch(attributeName) {
                        case 'type':
                        case 'src':
                        case 'data':
                        case 'classid':
                        case 'name':
                        case 'id':
                        case 'postdomevents':
                        case 'saveembedtags':
                        case 'factory':
                        case 'aggressiveCleanup':
                        case 'innerId':
                        case 'cache':
                        case 'aggressivecleanup':
                        case 'showlogo':
                            //do nothing as these shouldn't be overridden or set
                        break;
                        case('class'):
                            Element.addClassName(video, options[property]);
                        break;
                        case('controller'):
                            if (options[property]) {
                                video.setAttribute("controls", "controls");
                            }
                        break;
                        case('autoplay'):
                        case('autostart'):
                            if (options[property]) {
                                video.setAttribute("autoplay", "autoplay");
                            }
                        break;
                        default:
                            video.setAttribute(attributeName, options[property]);
                        break;
                    }
                }
            }
        },
        
        _createFallback: function (container, src, options)
        {
            if (Media._isQuickTimeAvailable()) {
                return Media.Spec.QuickTime.create(container, src, options);
            }
            
            if (Media._isFlashAvailable()) {
                return Media.Spec.Flash.create(container, src, options);
            }
            
            if (Media._shouldShowDownloadPrompt()) {
                return Media.createDownloadPrompt(container, src, options);
            }
        }
    },
    
    QuickTime: {
        create: function(container, src, options) {
            var outerObject = this._createObject(src, options),
                innerObject = null,
				id = options.id || (container.id ? container.id+'_video' : '');
				
            outerObject.setAttribute('id', id);
            
            if (!Media.Detection.IE()) {
                // Note:
                // Creating an inner object in IE results in a "# items remaining" 
                // status which makes the page appear as if it never finishes loading. 
                // So, we won't create one for IE.

                innerObject = this._createEmbed(src, options);
                outerObject.appendChild(innerObject);
            } else {
                outerObject.style.behavior = "url(#qt_event_source)";
                
                if (options.aggressiveCleanup !== false){

                    //knowing it's IE at this point, make sure we clear the movie when the page closes
                    //we also set our reference to null for good measure
                    Event.observe(window, 'unload', function () {
                        try {
                            outerObject.Stop();
                        } catch(e) {}

                        outerObject.style.display = 'none';
                        outerObject = null;
                    });
                }
            }

            this._configure(innerObject, outerObject, options);

            // Needs to be last so IE sees all the parameters appended to
            // the object prior to loading the activex control
            outerObject.setAttribute('classid', 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B');
            
            // We're adding that in order to be able to specialize the CSS for movie-loading for the Quicktime plugin only. (Added to enable Flash support) 
            Element.addClassName(outerObject, (outerObject.playerType = 'quicktime'));
            
            container.appendChild(outerObject);
            
            return innerObject || outerObject;
        },

		pollForChanges: [
			'load',
        	'timeupdate',
        	'durationchange',
       		'progress',
        	'playing',
        	'play',
        	'pause',
       		'ended'
		],
        
        interfaceMethods: {
            setup: function () {
            },
            duration: function () {
                var duration = 0;
                try {
                    duration = this.GetDuration() / this.GetTimeScale();
                } catch(e) {}
                return duration || 0;
            },
            time: function () {
                var time = 0;
                try {
                    time = this.GetTime() / this.GetTimeScale();
                } catch(e) {}
                return time || 0;
            },
            setTime: function(value) {
                this.SetTime(value * this.GetTimeScale());
            },
            volume: function () {
                return this.GetVolume() / 255;
            },
            setVolume: function(value) {
                this.SetVolume(value * 255);
            },
            muted: function () {
                return this.GetMute();
            },
            setMuted: function(value) {
                this.SetMute(value);
            },
            rate: function () {
                var rate;
                try {
                    rate = this.GetRate();
                } catch(e) {}
                
                return rate || 1;
            },
            setRate: function (value) {
				this.SetRate(value);
            },
            status: function () {
                this.GetPluginStatus();
            },
            percentLoaded: function () {
				var percent = 0;
				try {
					percent = this.GetMaxBytesLoaded()/this.GetMovieSize();
				} catch(e) {}
                return percent;
            },
            pause: function () {
				try {
                	this.Stop();
				} catch(e) {}
            },
            play: function () {
				try {
	                this.Play();
				} catch(e) {}
            },
            paused: function () {
                return this.GetRate()===0;
            },
            ended: function () {
                return this.ended;
            },
			src: function () {
				var src;
				try {
					src = this.GetURL();
				} catch(e) {}
			
				return src || '';
			},
			setSrc: function (src) {
				this.SetURL(src);
			},
            timeScale: function () {
            	return this.GetTimeScale();
            },
			movieType: function () {
				return 'QuickTime';
			}
        },
        
        _configure: function(innerObject, outerObject, options)
        {
            if (!options) {
                return;
            }

            var property = null,
                attributeName = null;

            for (property in options) {
                if(options.hasOwnProperty(property)) {

                    attributeName = property.toLowerCase();

                    switch(attributeName) {
                        case('type'):
                        case('src'):
                        case('data'):
                        case('classid'):
                        case('name'):
                        case('id'):
                        case('postdomevents'):
                        case('saveembedtags'):
                        case('factory'):
                        case('aggressiveCleanup'):
                            //do nothing as these shouldn't be overridden or set
                        break;
                        case('class'):
                            Element.addClassName(outerObject, options[property]);
                        break;
                        case('innerId'):
                            if(innerObject) {
                                innerObject.setAttribute('id', options[property]);
                            }
                        break;
                        case('autoplay'):
                            this._addParameter(outerObject, 'autostart', options[property]);
                            this._addParameter(innerObject, 'autostart', options[property]);
                        break;
                        case('width'):
                        case('height'):
                            outerObject.setAttribute(attributeName, options[property]);
                            if(innerObject) {
                                innerObject.setAttribute(attributeName, options[property]);
                            }
                        break;
                        default:
                            this._addParameter(outerObject, attributeName, options[property]);
                            this._addParameter(innerObject, attributeName, options[property]);
                        break;
                    }
                }
            }
        },
        
        /**
        * Adds an object param tag to the specified parent
        * Note that the attributes are added in this seemingly odd order
        * so they show up in the logical order in the dom
        */
        _addParameter: function(parent, name, value)
        {
            if (!parent) {
                return;
            }

            var param = document.createElement('param');
            param.setAttribute('value', value);
            param.setAttribute('name', name);
            parent.appendChild(param);

            param = null;
        },

        /**
        * Adds an object embed tag to the specified parent
        * only used for displaying controller over the movie
        */
        _createEmbed: function(url, options) {
            var embed = document.createElement('embed');
            embed.setAttribute('src', url);
            embed.setAttribute('type', 'video/quicktime');
            if (!Media.Detection.Firefox())
                embed.setAttribute('wmode', 'transparent');
            embed.setAttribute('postdomevents', true);
            embed.setAttribute('controller', false);
            embed.setAttribute('scale', 'tofit');
            
            if (options) {
                if (!isNaN(parseInt(options.width, 10)))
                    embed.setAttribute('width', options.width);
                if (!isNaN(parseInt(options.height, 10)))
                    embed.setAttribute('height', options.height);                   
            }
            
            return embed;
        },

        /**
        * Creates the IE friendly outer object
        * NOTE Safari and Opera both seem to be able to use this one as well
        */
        _createObject: function(url, options)
        {
            var object = document.createElement('object'),
                activexVersion = '7,3,0,0';

            if (Media.Detection.Mobile() && options.posterFrame) {
                this._addParameter(object, 'src', options.posterFrame);
                this._addParameter(object, 'href', url);
                this._addParameter(object, 'target', 'myself');
            } else {
                this._addParameter(object, 'src', url);
                if (!Media.Detection.Firefox())
                    this._addParameter(object, 'wmode', 'transparent');
            }

            object.setAttribute('id', name);
            this._addParameter(object, 'saveembedtags', true);
            this._addParameter(object, 'postdomevents', true);

            if (null !== options && (typeof options.codebase !== 'undefined') && '' !== options.codebase) {
                activexVersion = options.codeBase;
            }

            object.setAttribute('codebase', 'http://www.apple.com/qtactivex/qtplugin.cab#version=' + activexVersion);

            return object;
        }

    },
    
    Flash: {
        create: function(container, src, options) {
            return true;            
        }
    }
};

// == Media.VideoInterface ==
//
// Media.VideoInterface does the work of talking with the media element.
// It takes cues from Media.Spec to operate on the video element itself.
Media.VideoInterface = function(videoElement, delegateObject) {

    var video = videoElement,
        delegate = delegateObject,
        pollTimeout;

    return {
        object: function () {
            return video;
        },
        setObject: function (newVideo) {
            video = newVideo;
        },
        
        setDelegate: function (newDelegate) {
            delegate = newDelegate;
        },

        setup: function() {},
        
        /**
         * Override default vales (and optionally add new ones), subscribing the 
         * standard media interface to a customized one.
         */
        override: function (functions) {
            var member;
            
            function wrap(name, fn) {
                return function() {
                    return fn.apply(video, arguments);
                };
            }
            
            for (member in functions) {
                this[member] = wrap(member, functions[member]);
            }
            return this;
        },
        
        registerEvent: function (functionName, eventName, auxFunction) {
            if (!auxFunction && typeof(eventName)=='function') {
                auxFunction = eventName;
                eventName = null;
            }
        
            var e = eventName || functionName;
            
            Event.observe(video, e, function (event) {
                if (auxFunction) {
                    auxFunction.apply(this);
                }
                this.messageDelegate(functionName);
            }.bind(this));
        },
    
        pollForChanges: function (functions) {
            if (pollTimeout)
                window.clearInterval(pollTimeout);

            if (functions) {
                pollTimeout = window.setInterval(function(){
                    // functions.forEach(this.messageDelegate);
					for(var i = 0, func; func = functions[i]; i++) {
						this.messageDelegate(func);
					}
                }.bind(this), 480);
            }
        },
    
        messageDelegate: function(eventName) {
            if (!delegate)
                return;
                
            eventName = eventName.charAt(0).toUpperCase()+eventName.substring(1);
            var callback = 'videoReceived'+eventName+'Event';
        
            if (callback in delegate) {
                delegate[callback](this);
            }
        }
    };
};

Media.Controller = function (containerElement) {
    var container = containerElement,
        delegate,
        video,
        controlPanel,
        timeout,
        duration,
        seeking = false,
        wasPlayingBeforeSeek,
        hasBegunPlaying = false,
        hasEndedPlaying = false,
		playing = false,
		playAcknowledged = true,
		pauseAcknowledged = true;

    function containerMouseMove(e) {
        if (!controlPanel)
            return;
            
        controlPanel.show();
        window.clearTimeout(timeout);
        
        var videoObject = video.object(),
            mouseElement = e.target || e.srcElement;
        
        if (mouseElement == videoObject) {
            timeout = window.setTimeout(function(){
                if (controlPanel && typeof controlPanel != 'undefined') controlPanel.hide();
            }, 2500);
        }
    }
    
    function containerMouseOut(e) {
        if (!controlPanel)
            return;
        
        controlPanel.hide();
        window.clearTimeout(timeout);
    }
    
    return {
        _send: function (msg, params) {
            if (delegate && msg in delegate) {
                params = [this].concat(params);
                return delegate[msg].apply(delegate, params);
            }
        },
        
        _fireEvent: function (event, data) {
            Media.Controller.fireEvent(event, data);
        },
        
        reset: function () {
            seeking = false;
            hasBegunPlaying = false;
            hasEndedPlaying = false;
    		playing = false;
        },
        
        setDelegate: function (newDelegate) {
            delegate = newDelegate;
        },
        
        setVideo: function (vid) {
            hasBegunPlaying = false;
            hasEndedPlaying = false;
            
            video = vid;
            duration = video.duration() || 0;

            if (controlPanel && controlPanel.videoObjectHasChanged) {
                controlPanel.videoObjectHasChanged(video);
            }

            return this;
        },

		video: function() {
			return video;
		},
        
        setControlPanel: function (cp) {
            controlPanel = cp;
            
            if (controlPanel) {
                controlPanel.delegate = this;
                Event.observe(containerElement, 'mousemove', containerMouseMove);
                Event.observe(containerElement, 'mouseout', containerMouseOut);
                
            } else {
                Event.stopObserving(containerElement, 'mousemove', containerMouseMove);
                Event.stopObserving(containerElement, 'mouseout', containerMouseOut);   
            }
            
            return this;
        },

        beginSeeking: function () {
            if (seeking)
                return;

            seeking = true;
            wasPlayingBeforeSeek = !video.paused() && this.rate()==1;
            
            this.pause();
            
            var time = video.time();
            this._send('didStartJogging', time);
            this._fireEvent("QuickTime:didStartJogging", {controller: this, time: time});
        },
        
        endSeeking: function () {
            if (wasPlayingBeforeSeek) {
                this.play();
            }
            seeking = false;

            var time = video.time();
            this._send('didStopJogging', time);
            this._fireEvent("QuickTime:didStopJogging", {controller: this, time: time});
        },
        
        time: function () {
            return video.time() || 0;
        },
        
        setTime: function(newTime) {
            video.setTime(newTime);
            this.videoReceivedTimeupdateEvent();
        },

        duration: function () {
            if ( !duration ) {
                duration = video.duration();
            }
           	return duration;
        },

        volume: function() {
            return video.volume();
        },

        setVolume: function(level) {
			video.setMuted(false);
            video.setVolume(level);
        },

		setMuted: function(mute) {
			video.setMuted(mute);
		},
		
        toggleMute: function () {
			var isMuted = video.muted();

			if ( isMuted ) {
				this.setMuted(false);
			} else {
				this.setMuted(true);
			}
			
			return !isMuted;
        },
    
        playPause: function () {
            var isPaused = video.paused(),
				rate = this.rate();
            
            if (isPaused && rate === 1) {
                this.play();
            } else if (rate !== 1) {
				this.setRate(1);
			} else {
                this.pause();
            }
            
            return video.paused();
        },
		
		playing: function() {
			return playing;
		},

        play: function () {
            video.play();
			playing = true;
			playAcknowledged = false;
        },
    
        pause: function () {
            video.pause();
			playing = false;
			pauseAcknowledged = false;
        },
	
        stop: function () {
            video.pause();
			playing = false;
			pauseAcknowledged = false;
			hasEndedPlaying = true;
        },
        
        setRate: function (newRate) {
            video.setRate(newRate);
        },

        rate: function () {
            return video.rate();
        },

		src: function () {
			return video.src();
		},
		
		setSrc: function (src) {
			this.setSrc(src);
		},
		
		timeScale: function () {
			return video.timeScale();
		},
		
		movieType: function() {
			return video.movieType();
		},

        videoReceivedPlayingEvent: function (evt) {
            if (!hasBegunPlaying && (this.movieType() == 'Video' || this.time() > 0)) {
                if (controlPanel && typeof controlPanel.enableBasicControls !== 'undefined') {
    				controlPanel.enableBasicControls();
                }

    			playing = true;

                hasBegunPlaying = true;
                this._send('didBecomePlayable');
                this._fireEvent("QuickTime:canplaythrough", {controller: this});

                this._send('didBegin');
                this._fireEvent("QuickTime:begin", {controller: this});
				
				timeout = window.setTimeout(function(){
					if (controlPanel && typeof controlPanel != 'undefined') controlPanel.hide();
				}, 500);
				
            }
        },
        
        videoReceivedLoadEvent: function (evt) {
            if (controlPanel) {
                controlPanel.updatePercentLoaded(video.percentLoaded());
            }
            
        },
        
        videoReceivedEndedEvent: function (evt) {
            var time = video.time();
                duration = video.duration();
            
            if (hasEndedPlaying)
                return;

            if (hasBegunPlaying && (time >= duration || (video.movieType() == "Flash" && time >= (duration - 0.5)))) {
                hasEndedPlaying = true;
                this.videoReceivedTimeupdateEvent(this);

                this._send('onMovieFinished');
                this._send('didEnd');
                this._fireEvent("QuickTime:end", {controller: this});
            }
        },
        
        videoReceivedPlayEvent: function (evt) {
            if (playAcknowledged)
                return;
                
            playAcknowledged = true;
            
			this._send('didStart');
            this._fireEvent("QuickTime:start", {controller: this});
        },

        videoReceivedPauseEvent: function (evt) {
            if (pauseAcknowledged)
                return;
                
            pauseAcknowledged = true;
            
			this._send('didStop');
            this._fireEvent("QuickTime:stop", {controller: this});
        },

        videoReceivedTimeupdateEvent: function (evt) {
            if (controlPanel)
                controlPanel.updateTime(this.time());
            
            if (this.rate() < 0 && this.time() === 0 && playing) {
                this.pause();
            }
            
            if (this._lastTime != this.time()) {
                this._fireEvent("QuickTime:didPlayProgress", {
                    controller: this, 
                    currentTime: this.time(), 
                    duration: this.duration()
                });
            }
            
            this._lastTime = this.time();
        },
    
        videoReceivedProgressEvent: function (evt) {
            if (controlPanel)
                controlPanel.updatePercentLoaded(video.percentLoaded());
        },

        videoReceivedDurationchangeEvent: function (evt) {
            if (controlPanel)
                controlPanel.updateRemainingTime(this.duration() - this.time());
        }
    };
};
Media.Controller.fireEvent = function (event, data) {
    var body = $(document.body);
    if ('fire' in body) {
        body.fire(event, data);
    }
};

Media.ControlsWidget = function (containerElement, delegateObject, options) {
    this.container = containerElement;
    this.delegate = delegateObject;
	this.options = options;
    
    this._createTemplate();
    this._setupControls();
};
Media.ControlsWidget.TEMPLATE = '\
<div id="controls">\
	<div class="ACMediaControllerSpeedDisplay" id="speed-display-container">\
		<div class="directionDisplay"></div>\
		<div class="speedDisplay" id="speed-display"></div>\
	</div>\
    <div class="mediaControllerPanel" id="controls-panel">\
		<div class="slim-left-cap"></div>\
        <div class="ACMediaController movie-loading" id="controller">\
            <div class="volumeMute" id="volume-mute"></div>\
            <div class="volumePanel">\
                <div class="volumeTrack" id="volume-track">\
                    <div id="control-volume-progress" class="volumeTrackProgress"></div>\
                    <div class="volumePlayHead" id="volume-handle"></div>\
                </div>\
            </div>\
			<div class="volumeFull" id="volume-full"></div>\
            <div class="fastBackward" id="control-fastbackward"></div>\
            <div id="control-play-pause"></div>\
            <div class="fastForward" id="control-fastforward"></div>\
			<div class="accessibilitiesMenuControl" id="control-accessibilities-menu"></div>\
			<div class="sizesMenuControl" id="control-sizes-menu"></div>\
			<div class="downloadMenuControl" id="control-download-menu"></div>\
			<div class="shareMenuControl" id="control-share-menu"></div>\
			<div class="track-container">\
      	      <div class="timeDisplay" id="control-timeDisplay"><span id="min-played">00</span>:<span id="sec-played">00</span></div>\
	            <div class="sliderPanel">\
	                <div id="control-track" class="track">\
	                    <div id="control-loaded-progress" class="loadedProgress"></div>\
	                    <div id="control-track-progress" class="trackProgress"></div>\
	                    <div id="control-playhead" class="playHead"></div>\
	                </div>\
					<div id="track-end-cap" class="track-right-cap"></div>\
	            </div>\
	            <div class="durationDisplay" id="control-durationDisplay">-<span id="min-remain">00</span>:<span id="sec-remain">00</span></div>\
			</div>\
        </div>\
		<div class="slim-right-cap"></div>\
    </div>\
</div>';
Media.ControlsWidget.show = function(controls) {
    if (controls.fadeElement && !controls._showing) {
        if (controls._effect) {
            try {
				controls._effect.cancel();
			} catch(e) {}
            delete controls._effect;
        }

        controls._showing = true;
        controls._hiding = false;

		if (controls.fadeElement) {
			if (controls.supportsCSSAnimations() === true) {
				controls._effect = function() {
					Element.removeClassName(controls.fadeElement, 'fade');
				};
				controls._effect();
			} else {
		        controls._effect = new Effect.Opacity(controls.fadeElement, {
		            to: 1, 
		            duration: 0.5,
		            afterFinish: function(){controls._showing=false;}
		        });
			}
		}
    }
};
Media.ControlsWidget.hide = function(controls) {
    if (controls.fadeElement && !controls._hiding) {
        if (controls._effect) {
            try {
				controls._effect.cancel();
			} catch(e) {}
            delete controls._effect;
        }
        
        controls._hiding = true;
        controls._showing = false;

		if (controls.fadeElement) {
			if (controls.supportsCSSAnimations() === true) {
				controls._effect = function() {
					Element.addClassName(controls.fadeElement, 'fade');
				};
				controls._effect();
			} else {
		        controls._effect = new Effect.Opacity(controls.fadeElement, {
		            to: 0,
					duration: 0.5,
		            afterFinish: function(){controls._hiding=false;}
		        });
			}
		}
    }
};
Media.ControlsWidget.prototype = {
    delegate: null,
    element: null,
    
    _createTemplate: function () {
        function templateToNode(str) {
            var temporary = document.createElement('div'),
                node;
            temporary.innerHTML = str;
            node = temporary.firstChild;
            return node;
        }

        this.container.appendChild(templateToNode(Media.ControlsWidget.TEMPLATE));

        this.element = document.getElementById('controls');
    },
    
	supportsCSSAnimations: function() {
		try {
			var temp = document.createElement('div').style;
			temp.setProperty('-webkit-transition', 'inherit', null);
			temp.setProperty('-moz-transition', 'inherit', null);
			temp.setProperty('transition', 'inherit', null);
			
			return (temp.getPropertyValue('-webkit-transition') == 'inherit' || temp.getPropertyValue('-moz-transition') == 'inherit' || temp.getPropertyValue('transition') == 'inherit');
		} catch(e) {
			return false;
		}
	},

    _setupControls: function () {
		this.fadeElement = get('controls-panel');
		
		if(Media.Detection.Firefox() || (typeof this.options != 'undefined' && this.options.slimController === true)) {
			Element.addClassName(this.container, 'slim');
		}
		
		this.trackEndCap = get('track-end-cap');
	
        function addActiveStateSwitching(element) {
            element.baseClassName = element.baseClassName || element.className;
            
            function onmousedown(event) {
                Element.addClassName(this, this.baseClassName+'-active');
            }
            function onmouseup(event) {
                Element.removeClassName(this, this.baseClassName+'-active');            
            }
            
            Event.observe(element, 'mousedown', onmousedown.bind(element));
            Event.observe(element, 'mouseup', onmouseup.bind(element));
            Event.observe(document.documentElement, 'mouseup', onmouseup.bind(element));
        }
        
        function get(id) { return document.getElementById(id); }
        
        // Play/pause button
		this.toggleControl = get('control-play-pause');
		
        this.playControl = document.createElement('div');
		Element.addClassName(this.playControl, 'play');
        addActiveStateSwitching(this.playControl);
		this.playControl.id = 'play-control';
        Event.observe(this.playControl, 'click', this.play.bind(this));
		this.playControl.innerHTML = 'Play';
		this.playControl.style.display = 'none';

		this.pauseControl = document.createElement('div');
		Element.addClassName(this.pauseControl, 'pause');
		addActiveStateSwitching(this.pauseControl);
		this.pauseControl.id = 'pause-control';
		Event.observe(this.pauseControl, 'click', this.pause.bind(this));
		this.pauseControl.innerHTML = 'Pause';
		this.pauseControl.style.display = 'none';
		
		Element.show(this._send('playing') ? this.pauseControl : this.playControl);        
        var playpause = get('control-play-pause');
        playpause.appendChild(this.playControl);
        playpause.appendChild(this.pauseControl);
        
        // Fast Backward
        this.fastBackwardControl = get('control-fastbackward');
        Event.observe(this.fastBackwardControl, 'click', this.fastBackward.bind(this));
        addActiveStateSwitching(this.fastBackwardControl);
        
        // Fast Forward
        this.fastForwardControl = get('control-fastforward');
        Event.observe(this.fastForwardControl, 'click', this.fastForward.bind(this));
        addActiveStateSwitching(this.fastForwardControl);
        
        // Volume Mute
		this.volumeMuteControl = get('volume-mute');
		Event.observe(this.volumeMuteControl, 'click', this.muteVolume.bind(this));
		addActiveStateSwitching(this.volumeMuteControl);
		
		// Volume Full
		this.volumeFullControl = get('volume-full');
		Event.observe(this.volumeFullControl, 'click', this.fullVolume.bind(this));
		addActiveStateSwitching(this.volumeFullControl);
		
		this.speedDisplayContainer = get('speed-display-container');
		this.speedDisplay = get('speed-display');
		this.volumeThumb = get('volume-handle');
		this.volumeTrack = get('volume-track');
		this.volumeProgress = get('control-volume-progress');
		this.playhead = get('control-playhead');
		this.track = get('control-track');
		this.trackProgress = get('control-track-progress');
		this.controlLoadedProgress = get('control-loaded-progress');
		this.minutesPlayed = get('min-played');
	    this.secondsPlayed = get('sec-played');
	    this.minutesRemaining = get('min-remain');
	    this.secondsRemaining = get('sec-remain');
	
        // Volume Slider
		if (!this.volumeScrubber && this.element !== null) {
		    this.volumeThumb.baseClassName = 'volumePlayHead';
    		addActiveStateSwitching(this.volumeThumb);
    		
	        this.volumeScrubber = new Control.Slider(this.volumeThumb, this.volumeTrack, {
	            alignX: -5,
	            onSlide: function (value) {
	                this._seeking = true;
	                this._send('setVolume', value);
	                this.volumeProgress.style.width = this.volumeThumb.style.left;
	            }.bind(this),
	            onChange: function (value) {
	                this._seeking = false;
	                this.volumeProgress.style.width = this.volumeThumb.style.left;
	            }.bind(this)
	        });
	        this.volumeScrubber.setValue(1);
	        addActiveStateSwitching(this.volumeThumb);
		}
        
        
        // Playback Slider
		if (!this.scrubber && this.element !== null) {
		    this.playhead.baseClassName = 'playHead';
		    addActiveStateSwitching(this.playhead);
    		
	        this.scrubber = new Control.Slider(this.playhead, this.track, {
	            alignX: -9,
	            onSlide: function (value) {
	                if (!this._seeking) {
	                    this._seeking = true;
	                    this._send('beginSeeking');
	                    this.resetRate();
	                }
	                this._send('setTime',value*this._send('duration'));
	                this.trackProgress.style.width = this.playhead.style.left;
	            }.bind(this),
	            onChange: function (value) {
	                if (this._seeking) {
	                    this._seeking = false;
	                    this._send('endSeeking');
	                }
	                this.trackProgress.style.width = this.playhead.style.left;
	            }.bind(this)
	        });
		}

    },
    
	_enableControl: function(control) {
        Element.addClassName(control, control.baseClassName+'-enabled');
	},
	_disableControl: function(control) {
        Element.removeClassName(control, control.baseClassName+'-enabled');	    
	},
	
	reset: function () {
		this.show();
		
        Element.show(this.playControl);
	    Element.hide(this.pauseControl);
        
		this._disableControl(this.volumeMuteControl);
		this._disableControl(this.volumeFullControl);
        this._disableControl(this.volumeThumb);
        this._disableControl(this.playControl);
		this._disableControl(this.pauseControl);
        this._disableControl(this.playhead);
        this._disableControl(this.fastBackwardControl);
        this._disableControl(this.fastForwardControl);
        
        Element.removeClassName(this.fastBackwardControl, 'fastBackward-active');
		Element.removeClassName(this.fastForwardControl, 'fastForward-active');
	},
	
	enableBasicControls: function() {
	    Element.hide(this.playControl);
	    Element.show(this.pauseControl);
        
		this._enableControl(this.volumeMuteControl);
		this._enableControl(this.volumeFullControl);
        this._enableControl(this.volumeThumb);
        this._enableControl(this.playControl);
		this._enableControl(this.pauseControl);
        this._enableControl(this.playhead);
        this._enableControl(this.fastBackwardControl);
        this._enableControl(this.fastForwardControl);
	},
	
    _send: function (msg, params) {
        if (this.delegate && msg in this.delegate) {
            params = [].concat(params);
            return this.delegate[msg].apply(this.delegate, params);
        }
    },
    
    show: function () {
        Media.ControlsWidget.show(this);
    },
    
    hide: function () {
        if (this._seeking)
            return;
        Media.ControlsWidget.hide(this);

		Element.removeClassName(this.speedDisplayContainer, 'fastBackward');
		Element.removeClassName(this.speedDisplayContainer, 'fastForward');
    },
	
	resetRate: function() {
		if (this._send('rate') !== 1) {
			Element.removeClassName(this.speedDisplayContainer, 'fastBackward');
			Element.removeClassName(this.speedDisplayContainer, 'fastForward');
			Element.removeClassName(this.fastBackwardControl, 'fastBackward-active');
			Element.removeClassName(this.fastForwardControl, 'fastForward-active');
		
			this._send('setRate', 1);
		}		
	},
	
	play: function() {
		this.resetRate();
		
		if (this._send('playing') === false) {
			this._send('play');
		}
		
		
		Element.hide(this.playControl);
	    Element.show(this.pauseControl);
	},
	
	pause: function() {
		this.resetRate();
		
		if (this._send('playing') === true) {
			this._send('pause');
		}
		
		Element.hide(this.pauseControl);
	    Element.show(this.playControl);
	},
	
    togglePlaying: function () {
        var isPlaying = this._send('playing');

        if (isPlaying) {
			this.pause();
		} else {
			this.resetRate();
			this.play();
        }
    },
    
    fastBackward: function () {
        var currentRate = this._send('rate');

		if (this._send('playing') === false) {
			this._send('play');
		} else {
			Element.hide(this.pauseControl);
    	    Element.show(this.playControl);
		}
				
        switch(currentRate) {
            case -2:
                this._send('setRate', -4);
				this.speedDisplay.innerHTML = '4x';
                break;
            case -4:
                this._send('setRate', -8);
				this.speedDisplay.innerHTML = '8x';
                break;
            default:
                this._send('setRate', -2);
				this.speedDisplay.innerHTML = '2x';
                break;
        }
		
		Element.removeClassName(this.speedDisplayContainer, 'fastForward');
		Element.addClassName(this.speedDisplayContainer, 'fastBackward');
		Element.removeClassName(this.fastForwardControl, 'fastForward-active');
		Element.addClassName(this.fastBackwardControl, 'fastBackward-active');
    },

    fastForward: function () {
        var currentRate = this._send('rate');

		if (this._send('playing') === false) {
			this._send('play');
		} else {
			Element.hide(this.pauseControl);
    	    Element.show(this.playControl);
		}
		
        switch(currentRate) {
            case 2:
                this._send('setRate', 4);
				this.speedDisplay.innerHTML = '4x';
                break;
            case 4:
                this._send('setRate', 8);
				this.speedDisplay.innerHTML = '8x';
                break;
            default:
                this._send('setRate', 2);
				this.speedDisplay.innerHTML = '2x';
                break;
        }
		
		Element.removeClassName(this.speedDisplayContainer, 'fastBackward');
		Element.addClassName(this.speedDisplayContainer, 'fastForward');
		Element.removeClassName(this.fastBackwardControl, 'fastBackward-active');
		Element.addClassName(this.fastForwardControl, 'fastForward-active');
    },
    
	muteVolume: function() {
		this._send('setMuted', true);
		this.volumeScrubber.setValue(0);
	},
	
	fullVolume: function() {
		this._send('setMuted', false);
		this._send('setVolume', 1);
		this.volumeScrubber.setValue(1);
	},
	
    updatePercentLoaded: function (newPercent) {
        if(typeof this.controlLoadedProgress !== 'undefined' && newPercent) this.controlLoadedProgress.style.width = newPercent*100 + '%';
		if (newPercent === 1) {
			Element.addClassName(this.trackEndCap, 'track-right-cap-loaded');
			Element.removeClassName(this.trackEndCap, 'track-right-cap');
		}
    },
    
    updateTime: function (newTime) {
        var duration = this._send('duration');
		
		if(newTime === 0 || newTime === duration) {
			Element.removeClassName(this.fastBackwardControl, 'active');
			Element.removeClassName(this.fastForwardControl, 'active');
		}

        this.scrubber.setValue((newTime / duration) || 0);
        this.updateElapsedTime(newTime);
        this.updateRemainingTime(duration-newTime);
    },
    
    _setTimeForReadout: function(time, minutes, seconds) {
        var min = parseInt(time / 60, 10),
            sec = parseInt(time % 60, 10);
        if (min < 10) {
            min = '0'+min;
        }
        if (sec < 10) {
            sec = '0'+sec;
        }
		
        minutes.innerHTML = min;
        seconds.innerHTML = sec;
    },
    
    updateElapsedTime: function (newTime) {
        var minutes = this.minutesPlayed,
            seconds = this.secondsPlayed;
            
        this._setTimeForReadout(newTime, minutes, seconds);
    },
    
    updateRemainingTime: function (newTime) {
        var minutes = this.minutesRemaining,
            seconds = this.secondsRemaining;
            
        this._setTimeForReadout(newTime, minutes, seconds);
    }
};