﻿
// The interface for media plugins that handle playing media in abstract manner.
var Plugin = Class.create({
    // Member variables
    pluginObject: null,
    askedForPlugin: false,

    // Overridden methods
    initialize: function(playerName) { },
    setMedia: function(media) { },
    playMedia: function(media) { },
    play: function() { },
    pause: function() { },
    stop: function() { },
    getLoaded: function() { },
    getTime: function() { },
    setTime: function(time) { },
    getVolume: function() { },
    setVolume: function(vol) { },
    getMute: function() { },
    setMute: function(isMute) { },
    getDuration: function() { },


    // Events
    onPlaybackEnded: function() { },
    onPlaybackStarted: function() { },
    onPlaybackPaused: function() { },
    onErrorOccured: function() { },

    // Helpers
    _needsPlugin: function(url) {
        if (!this.askedForPlugin) {
            this.askedForPlugin = true;
            if (confirm(NeedPluginMsg)) {
                if (window.innerWidth < 500 || window.innerHeight < 500)
                    window.resizeTo(Math.max(970, window.innerWidth), Math.max(750, window.innerHeight));
                location.href = url;
            }
        }
    }
});
Plugin.Debugging = false;

var QuickTimePlugin = Class.create(Plugin, {
	pluginName: 'QuickTime',
	_hasChangedVolume: false,
	_supportsWmp: false,
	initialize: function(playerName) {
		this._embed(playerName);
		this._supportsWmp = !Browser.IE && Browser.supportsPlugin('Flip4Mac');

		// Most browsers need to wait until onload before they can register events on quicktime.
		Event.observe(window, 'load', function() {
			this._registerEvent('qt_play', function() { this.onPlaybackStarted(); } .bind(this));
			this._registerEvent('qt_pause', function() { this.onPlaybackPaused(); } .bind(this));
			this._registerEvent('qt_ended', function() { this.onPlaybackEnded(); } .bind(this));
			// This makes sure that AutoPlay won't default back to true on next track.
			this.pluginObject.SetResetPropertiesOnReload(false);
		} .bind(this));
	},

	play: function() { this.pluginObject.Play(); },
	pause: function() { try { this.pluginObject.Stop(); } catch (e) { } },
	stop: function() { try { this.pluginObject.Stop(); this.pluginObject.Rewind(); } catch (e) { } },
	setMedia: function(media) {
		if (this._checkSupport(media) == false) return;
		
		this.pluginObject.SetAutoPlay(false);
		this._setURL(media);
		// We use this to notify that the player is paused and can start playing the newly set media.
		this.onPlaybackPaused();
	},
	playMedia: function(media) {
		if (this._checkSupport(media) == false) return;
		
		this.pluginObject.SetAutoPlay(true);
		this._setURL(media);
	},
	_setURL: function(url) {
		// The vol gets reset everytime we change the url.
		var vol = this.pluginObject.GetVolume();
		this.pluginObject.SetURL(url);
		if (!this._hasChangedVolume)
			this.pluginObject.SetVolume(vol);
	},
	getLoaded: function() {
		var duration = this.pluginObject.GetDuration();
		return duration == 0 ? 0 : this.pluginObject.GetMaxTimeLoaded() / duration;
	},
	getTime: function() {
		var duration = this.pluginObject.GetDuration();
		return duration == 0 ? 0 : this.pluginObject.GetTime() / duration;
	},
	setTime: function(time) {
	    // If we don't stop and play on some streams, QuickTime may get confused and keep playing at old position.
	    var wasPlaying = this.pluginObject.GetRate() != 0;
	    var forward = parseInt(this.pluginObject.GetDuration() * time);
	    if (forward > this.pluginObject.GetMaxTimeLoaded()) // Quicktime does not seem to want to jump into as song if it has not downloaded the part being jumped into.
	        forward = this.pluginObject.GetMaxTimeLoaded() - 10;

	    this.pluginObject.Stop();
	    this.pluginObject.SetTime(forward);
	    if (wasPlaying)
	        this.pluginObject.Play();
	},
	getVolume: function() { return this.pluginObject.GetVolume() / 255; },
	setVolume: function(vol) { this.pluginObject.SetVolume(vol * 255); this._hasChangedVolume = true; },
	getDuration: function() {
		try {
			if (this.pluginObject.GetDuration() > 2140000000) return 0;
			return this.pluginObject.GetDuration() / this.pluginObject.GetTimeScale();
		}
		catch (err) { return 0; }
	},
	getMute: function() { return this.pluginObject.GetMute(); },
	setMute: function(muted) { this.pluginObject.SetMute(muted); },

	/* Private methods */
	_embed: function(elName) {
		if (Browser.IE && !$('qt_event_source'))
			document.write("<OBJECT id='qt_event_source' classid='clsid:CB927D12-4FF7-4a9e-A169-56E4B8A75598'></OBJECT>");

		document.write("<object classid='clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B' style='BEHAVIOR: url(#qt_event_source)' id='" + elName + "' " + this._getSizeString() + " codebase='http://www.apple.com/qtactivex/qtplugin.cab#version=7,3,0,0'>");
		document.write("<param name='src' value='blank.gif'/>");
		document.write("<param name='enablejavascript' value='True'/>");
		document.write("<param name='postdomevents' value='True'/>");
		document.write("<param name='autoplay' value='false'/>");
		document.write("<embed type='video/quicktime' " + this._getSizeString() + " autoplay='false' postdomevents='True' enablejavascript='True' id='" + elName + "_embed' name='" + elName + "' pluginspage='http://www.apple.com/quicktime/download/' src='blank.gif'/>");
		document.write("</object>");

		// On IE, the following returns the object tag, but on other browser it returns the embed tag.
		this.pluginObject = $(document[elName]);
		// On Safari, events from the player are not catchable at the embed tag, so we'll use the object tag + bubbling.
		this.eventSource = $(elName);
	},
	_checkSupport: function(media) {
		if (!this._supportsWmp && media.indexOf('.wma') > -1) {
			this._needsPlugin("http://www.microsoft.com/downloads/details.aspx?FamilyId=915D874D-D747-4180-A400-5F06B1B5E559&displaylang=en");
			return false;
		}
		return true;
	},
	_getSizeString: function() {
		return Plugin.Debugging ? "height='16' width='200'" : "height='1' width='1'";
	},
	_registerEvent: function(eventName, handler) {
		this.eventSource.observe(eventName, handler);
	}
});
var WmpPlugin = Class.create(Plugin, {
    pluginName: 'Wmp',
    _errorHandlerReference: -1,
    initialize: function(playerName) {
        this._embed(playerName);

        // Opera sometimes loses changes to window if it hasn't finished loading
        Event.observe(window, 'load', function() {
            this._registerEvent('PlayStateChange', this.onPlayStateChange.bind(this));
        } .bind(this));
    },


    setMedia: function(media) { this.pluginObject.settings.autoStart = false; this.pluginObject.URL = media; },
    playMedia: function(media) { this.pluginObject.settings.autoStart = true; this.pluginObject.URL = media; },
    play: function() { this.pluginObject.controls.play(); },
    pause: function() { this.pluginObject.controls.pause(); },
    stop: function() { this.pluginObject.controls.stop(); },

    getLoaded: function() {
        return this.pluginObject.network.downloadProgress / 100;
    },
    getTime: function() {
        var duration = this.pluginObject.currentMedia.duration;
        return duration == 0 ? 0 : this.pluginObject.controls.currentPosition / duration;
    },
    setTime: function(time) {
        this.pluginObject.controls.currentPosition = time * this.pluginObject.currentMedia.duration;
    },
    getVolume: function() { return this.pluginObject.settings.volume / 100; },
    setVolume: function(vol) { this.pluginObject.settings.volume = Math.round(vol * 100); },
    getDuration: function() {
        return this.pluginObject.currentMedia ? this.pluginObject.currentMedia.duration : 0;
    },
    getMute: function() { return this.pluginObject.settings.mute; },
    setMute: function(muted) { this.pluginObject.settings.mute = muted; },

    /* Private methods */
    _embed: function(elName) {
        if (!Browser.IE && !Browser.supportsMime('application/x-ms-wmp')) {
            this._missingPlugin();
            return;
        }

        if (Browser.IE)
            document.write("<object classid='clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6' id='" + elName + "' " + this._getSizeString() + ">");
        else
            document.write("<object type='application/x-ms-wmp' id='" + elName + "' " + this._getSizeString() + ">");
        document.write("<param name='autoStart' value='false' />");
        document.write('</object>');
        this.pluginObject = $(elName);
    },
    _missingPlugin: function() {
        this.hasAsked = false;
        this.setMedia = this.playMedia = this.play = this.pause = this.stop = this.getLoaded =
		this.getTime = this.setTime = this.getVolume = this.setVolume = this.getDuration = this.getMute =
		this.setMute = this._needsPlugin.curry("http://port25.technet.com/pages/windows-media-player-firefox-plugin-download.aspx").bind(this);
    },
    _getSizeString: function() {
        return Plugin.Debugging ? "width='232' height='49'" : "width='1' height='1'";
    },
    _registerEvent: function(eventName, handler) {
        if (this.pluginObject && this.pluginObject.attachEvent)
            this.pluginObject.attachEvent(eventName, handler);
        else
        // Other browsers need global functions as event handlers.
            window["OnDS" + eventName + "Evt"] = handler;
    },
    _errorHandler: function() {
        if (this.pluginObject != null) {
            if (this.pluginObject.error != null) {
                if (this.pluginObject.error.errorCount > 0) {
                    for (var i = 0; i < this.pluginObject.error.errorCount; i++) {
                        var error = '';
                        error += 'Error code: ' + this.pluginObject.error.item(i).errorCode;
                        error += '\nDescription: ' + this.pluginObject.error.item(i).errorDescription;
                        //error += '\nErrorContext: ' + this.pluginObject.error.item(i).errorContext;
                        try {
                            error += '\nTrackID: ' + $('trackid').innerText;
                        }
                        catch (err) { error += '\nTrackID: Not found'; }
                        //error += '\nRemedy: ' + this.pluginObject.error.item(i).remedy;
                        //error += '\nCustomUrl: ' + this.pluginObject.error.item(i).customUrl;
                        new Ajax.Request("/Ajax/LogError.ashx", { parameters: "msg=" + escape(error) + "&eurl=" + escape(this.pluginObject.URL), onSuccess: null || function() { } });
                    }
                    this.pluginObject.error.clearErrorQueue();
                    this.onErrorOccured();
                }
            }
        }
    },
    onPlayStateChange: function(state) {
        if (state == 2 || state == 10) // we consider "ready", as in "ready to play" to be the same as paused.
            this.onPlaybackPaused();
        else if (state == 1)
            this.onPlaybackEnded();
        else if (state == 3)
            this.onPlaybackStarted();

        this._errorHandler();
    }
});
var MPlayerPlugin = Class.create(Plugin, {
	pluginName: 'MPlayer',
	initialize: function(playerName) {
		this._embed(playerName);
	},

	play: function() { this.pluginObject.Play(); },
	pause: function() { try { this.pluginObject.Stop(); } catch (e) { } },
	stop: function() { try { this.pluginObject.Stop(); this.pluginObject.Rewind(); } catch (e) { } },
	setMedia: function(media) {
		this.pluginObject.filename = media;
		
		// We use this to notify that the player is paused and can start playing the newly set media.
		this.onPlaybackPaused();
	},
	playMedia: function(media) {
		this.pluginObject.filename = media;
		this.pluginObject.Play();
	},
	getLoaded: function() {
		var duration = this.pluginObject.GetDuration();
		return duration == 0 ? 0 : this.pluginObject.GetMaxTimeLoaded() / duration;
	},
	getTime: function() {
		var duration = this.pluginObject.GetDuration();
		return duration == 0 ? 0 : this.pluginObject.GetTime() / duration;
	},
	setTime: function(time) {
		// If we don't stop and play on some streams, QuickTime may get confused and keep playing at old position.
		var wasPlaying = this.pluginObject.GetRate() != 0;
		this.pluginObject.Stop();
		this.pluginObject.SetTime(this.pluginObject.GetDuration() * time);
		if (wasPlaying)
			this.pluginObject.Play();
	},
	getVolume: function() { return this.pluginObject.GetVolume() / 255; },
	setVolume: function(vol) { this.pluginObject.SetVolume(vol * 255); this._hasChangedVolume = true; },
	getDuration: function() {
		try {
			if (this.pluginObject.GetDuration() > 2140000000) return 0;
			return this.pluginObject.GetDuration() / this.pluginObject.GetTimeScale();
		}
		catch (err) { return 0; }
	},
	getMute: function() { return this.pluginObject.GetMute(); },
	setMute: function(muted) { this.pluginObject.SetMute(muted); },

	/* Private methods */
	_embed: function(elName) {
		document.write("<embed nocache=0 type='application/x-mplayer2' src='http://wms.tonlist.is/tonlistv2/30SEC64KWMA/864/864b00fd-4f04-4e65-b18f-fdd4e18252ee.wma' " + this._getSizeString() + " id='" + elName + "_embed' name='" + elName + "' />");

		// On IE, the following returns the object tag, but on other browser it returns the embed tag.
		this.pluginObject = $(document[elName]);
	},
	_getSizeString: function() {
		return Plugin.Debugging ? "height='60' width='200' showcontrols='true'" : "height='1' width='1' showcontrols='false'";
	}
});

var SmartPlayer = Class.create(Plugin, {
    lastPlayedProgress: null,
    lastLoadedProgress: null,
    mediaQueue: [],
    isPlaying: false,
    timer: null,

    initialize: function(usePlugin, playerName) {
        playerName = playerName || 'myPlayer';
        var plugin = Browser.getUrlParameter("useplugin") || usePlugin;

        if (plugin == null) {
            if (Browser.Windows) // && (Browser.IE || Browser.supportsMime('application/x-ms-wmp')))
                plugin = WmpPlugin;
            else
                plugin = QuickTimePlugin;
        }
        else if (plugin.constructor == String)
            plugin = window[plugin.indexOf('Plugin') > -1 ? plugin : plugin + "Plugin"];

        this.pluginObject = new plugin(playerName);
        this.pluginObject.onPlaybackEnded = this.onPlaybackEnded.bind(this);
        this.pluginObject.onPlaybackStarted = this.onPlaybackStarted.bind(this);
        this.pluginObject.onPlaybackPaused = this.onPlaybackPaused.bind(this);
        this.pluginObject.onErrorOccured = function() { this.onErrorOccuredEvt(); } .bind(this);
        this.pluginName = this.pluginObject.pluginName;
        this.pluginObject.setVolume(0.5);
    },

    play: function() { this.pluginObject.play(); },
    pause: function() { this.pluginObject.pause(); },
    stop: function() { this.pluginObject.stop(); },
    getTime: function() { return this.pluginObject.getTime(); },
    setTime: function(time) { this.pluginObject.setTime(time); this.onTimer(); },
    getVolume: function() { return this.pluginObject.getVolume(); },
    setVolume: function(vol) { this.pluginObject.setVolume(vol); },
    getMute: function() { return this.pluginObject.getMute(); },
    setMute: function(muted) { this.pluginObject.setMute(muted); },
    getDuration: function() { return this.pluginObject.getDuration(); },
    getIsPlaying: function() { return this.isPlaying; },

    setMedia: function(media) {
        this.mediaQueue = [media];
        this.pluginObject.setMedia(media);
    },

    playMedia: function($super, media) {
        this.mediaQueue = [media];
        this.pluginObject.playMedia(media);
    },

    queueMedia: function(media) {
        if (this.mediaQueue.length == 0)
            this.playMedia(media);
        else
            this.mediaQueue.push(media);
    },

    togglePlayPause: function() {
        if (this.isPlaying)
            this.pause();
        else
            this.play();
    },

    onPlaybackEnded: function() {
        this.mediaQueue.shift();
        if (this.mediaQueue.length == 0) {
            var newTrack = this.onQueryNextMedia();
            if (newTrack)
                this.mediaQueue.push(newTrack);
        }

        if (this.mediaQueue.length > 0) {
            // WMP sometimes freaks out if it is told to play something on the playbackEnded event.
            setTimeout(function() { this.pluginObject.playMedia(this.mediaQueue[0]) } .bind(this), 10);
        }
        else {
            this.isPlaying = false;
            this.onPlayPauseChanged(false);
        }
    },
    onPlaybackStarted: function() {
        this.isPlaying = true;
        this.onPlayPauseChanged(true);

        if (!this.timer) {
            this.timer = new PeriodicalExecuter(this.onTimer.bind(this), 1);
        }
    },
    onPlaybackPaused: function() {
        this.isPlaying = false;
        this.onPlayPauseChanged(false);
    },
    onTimer: function() {
        var progress = this.pluginObject.getLoaded();
        if (progress != this.lastLoadedProgress)
            this.onLoadProgressChanged(this.lastLoadedProgress = progress);

        progress = this.pluginObject.getTime();
        if (progress != this.lastPlayedProgress)
            this.onPlayProgressChanged(this.lastPlayedProgress = progress);
    },

    onPlayPauseChanged: function(isPlaying) { },
    onPlayProgressChanged: function(percentage) { },
    onLoadProgressChanged: function(loaded) { },
    onErrorOccuredEvt: function() { },
    onQueryNextMedia: function() { }
});

var Browser = {
	IE: navigator.userAgent.indexOf('MSIE') != -1,

	Windows: navigator.platform.indexOf('Win') != -1,
	Mac: navigator.platform.indexOf('Mac') != -1,
	Linux: navigator.platform.indexOf('Linux') != -1,

	/* Only works for non-IE browsers */
	supportsMime: function(mime) {
		if (navigator.plugins && navigator.plugins.length) {
			for (var i = 0; i < navigator.plugins.length; i++) {
				var plugin = navigator.plugins[i];
				for (var j = 0; j < plugin.length; j++) {
					if (plugin[j].type == mime)
						return true;
				}
			}
		}
		return false;
	},
	supportsPlugin: function(name) {
		if (navigator.plugins && navigator.plugins.length) {
			for (var i = 0; i < navigator.plugins.length; i++) {
				if (navigator.plugins[i].name.indexOf(name) > -1)
					return true;
			}
		}
		return false;
	},

	getUrlParameter: function(name) {
		var regex = new RegExp("[\\?&]" + name + "=([^&#]*)", "i");
		var result = regex.exec(window.location.href);
		return result ? result[1] : null;
	},

	debug: function(text) {
		((window.console && console.log) ||
		(window.opera && opera.postError) ||
		window.alert).call(this, text);
	}
};