/**
 * Creates a fully featured video player with controls and monitoring, connected to a specific HTML node set
 * @class Represents a video player with complete UI/controls. Behaviour is adopted to the FXTV.no requirements.
 * @constructor
 * @param {int} index A number matching the HTML nodes' ID endings. For example, if the Media Player object has 'video123' as ID, index should be 123
 * @param {boolean} miniOnly Optional parameter. When set to true, the player behaviour will be modified for use as thumbnail/secondary player only
 * @param {object} callbacks An optional object whose properties are pointers to callback functions. Zero or more of these callbacks can be attached: onMouseOver, onMouseOut, onVideoEnd, onHide
 * @author Tor Erik Alr�k
 * @author Gustaf Forsslund@Oktavilla (modifications for tv4.se)
 * @version 1.8.1
 */

function VideoPlayer(index, miniOnly, delayedAttach, callbacks) {
    // callbacks = {
    // 		onMouseOver, - when user mouses over the video area
    // 		onMouseOut,
    // 		onVideoEnd, - when video stops by itself (program finished or live stream ended)
    // 		onHide, - when the hide method is called
    // 		onItemChange, - when video position is moving
    // 		onError, - when user clicks for error details
    // 		onQualityClick, - when user clicks for quality details settings
    // 		onReplay, - when users presses play again after having stopped the stream (live only)
    // 		playerContainer, - html target element for reloading the player when onReplay event fires
    // 		onMuteChange, - when user presses the mute button
    // 		onPlayerClick - when user clicks in the video area
    //  }
    // CONSTRUCTOR
    if (index == null) index = 0;
    var ie = navigator.userAgent.indexOf("MSIE") >= 0 && navigator.userAgent.indexOf("Opera") < 0 && navigator.userAgent.indexOf("Mac") < 0;
    this.ie = ie;
    var bufferingStarted;
    // first time buffering
    var buffering = false;
    // buffering in general
    var videoStarted;
    var sliderRemapped;
    var slider;
    var progressBar;
    var lastDur;
    var lastPos = 0;
    var sliderEvtsEnabled;
    var dragging;
    var live;
    var items;
    var started = false;
    var hFallbackTimer;
    var delayedProps;
    var waitForAd = false;
    var endAdPos = '';
    var endAdCode = '';
    var adCategory = '';
    var programTitle = '';
    var currentItem;
    var cancelled = false;
    var wmpErrorMsg = '';
    var wmpErrorCode = '';
    var prevVolume = '90';
    var mutedFromButton = false;
    var hideAd;
    var showingStartAd = false;
    var showingEndAd = false;

    var startPos = 0;
    var endPos = null;
    var crop = false;
    var lastStartPos = 0;
    var lastCrop = false;

    var me = this;

    var vp = null;
    this.vp = vp;

    var qualityStatusShowing = false;
    var prevStatusMsg;

    var fallbackDelay1 = 5000;
    // max loading time for start ad
    var fallbackDelay2 = 20000;
    // max time for showing start ad before video starts buffering
    var fallbackDelay3 = 20000;
    // max time for showing start ad during buffering
    var fallbackDelay4 = 20000;
    // max time for showing end ad

    var adSession = Math.round(Math.random() * 10000);

    var bufferFlashEmbed1 = '<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://active.macromedia.com/flash/cabs/swflash.cab#version="7,0,0,0" id="bufferAnimation"><PARAM name="movie" value="';
    var bufferFlashEmbed2 = '"><PARAM name="quality" value="high"><PARAM name="wmode" value="transparent"></OBJECT><div id="bufferAd">';
    var bufferFlashEmbed3 = '</div>';

    var titleTimerId;
    var currentProgId;

    var skipForwardLong = 60;
    // seconds
    var skipBackLong = -30;
    // seconds

    var skipForwardShort = 15;
    // seconds
    var skipBackShort = -7.5;
    // seconds

    var log = new Object();
    try {
        log.debug = logDebug;
        log.error = logError;
    } catch(e) {
        log.error = function(source, e) {
            if (location.href.indexOf('debug=true') > 0) alert(source + ': ' + e.message);
        }
        log.debug = function(source, message) {
            if (location.href.indexOf('debug=true') > 0 && $('debugList')) {
                $('debugList').innerHTML = $('debugList').innerHTML + '<li>' + source + ': ' + message + '</li>';
            }
        }
    }

    // Method required in constructor
    this.attach = function() {
        try {
            if (ie) {
                var obj = $('video' + index);
                // Eolas fix - fortunately one code line only
                obj.outerHTML = obj.outerHTML;

                vp = new CorePlayer('video' + index, log);
                // vp buttons
                Event.observe('playButton' + index, 'click', function(e) {
                    if (!(Element.visible('videoAdEnd' + index) || Element.visible('videoAdStart' + index))) {
                        if (videoStarted && callbacks && callbacks.onReplay && !me.isPlaying()) {
                            callbacks.onReplay(index, callbacks.playerContainer, currentProgId);
                        } else {
                            vp.togglePlay();
                        }
                    }
					Event.stop(e);
                    return false
                }, false);
                Event.observe('volDownButton' + index, 'click', function(e) {
                    vp.volumeDown(10);
                    Element.addClassName($('muteButton' + index), 'soundButton');
                    Element.removeClassName($('muteButton' + index), 'muteButton');
                    Event.stop(e);
                    return false
                }, false);
                Event.observe('volUpButton' + index, 'click', function(e) {
                    vp.volumeUp(10);
                    Element.addClassName($('muteButton' + index), 'soundButton');
                    Element.removeClassName($('muteButton' + index), 'muteButton');
                    Event.stop(e);
                    return false
                }, false);
                Event.observe('muteButton' + index, 'click', function(e) {
                    muteChange();
                    Event.stop(e);
                    return false
                }, false);
                /*
                Event.observe('prevButton' + index, 'click', function() {
                    skipBack();
                    Event.stop(e);
                    return false
                }, false);
                Event.observe('nextButton' + index, 'click', function() {
                    skipForward();
                    Event.stop(e);
                    return false
                }, false);
                Event.observe('toolsButton' + index, 'click', function() {
                    toggleQualityStatus();
                    Event.stop
                    return false
                }, false);
                */
                Event.observe('fullscreenButton' + index, 'click', function(e) {
                    vp.gotoFullscreen();
                    Event.stop(e);
                    return false
                }, false);
                Event.observe('sliderButton' + index, 'mousedown', function() {
                    startDrag();
                }, true);
                Event.observe('sliderButton' + index, 'mouseup', function() {
                    endDrag();
                }, true);

                // vp events
                vp.onStatusChange = statusChange;
                vp.onBufferingStart = bufferingStart;
                vp.onPosChange = posChange;
                vp.onVideoStart = videoStart;
                vp.onVideoEnd = videoEnd;
                vp.onPlayStateChange = playStateChange;
                vp.onCeased = me.detach;
                if (callbacks && callbacks.onPlayerClick)
                    vp.onPlayerClick = callbacks.onPlayerClick
                else if (!miniOnly) {
                    vp.onPlayerClick = playerClicked;
                }
                Event.observe(window, 'unload', function() {
                    vp.stop(true);
                }, false);
            } else {
                Event.observe('playButton' + index, 'click', function(e) {
                    Event.stop(e);
                }, false);
                Event.observe('volDownButton' + index, 'click', function(e) {
                    Event.stop(e);
                }, false);
                Event.observe('volUpButton' + index, 'click', function(e) {
                    Event.stop(e);
                }, false);
                Event.observe('muteButton' + index, 'click', function(e) {
                    Event.stop(e);
                }, false);
                Event.observe('fullscreenButton' + index, 'click', function(e) {
                    Event.stop(e);
                }, false);
            }

            if (callbacks && callbacks.onMouseOver) {
                Event.observe('bg' + index, 'mouseover', function() {
                    callbacks.onMouseOver(index);
                    return false
                }, false);
                Event.observe('video' + index, 'mouseover', function() {
                    callbacks.onMouseOver(index);
                    return false
                }, false);
            }

            if (callbacks && callbacks.onMouseOut) {
                Event.observe('bg' + index, 'mouseout', function() {
                    callbacks.onMouseOut(index);
                    return false
                }, false);
                Event.observe('video' + index, 'mouseout', function() {
                    callbacks.onMouseOut(index);
                    return false
                }, false);
            }
        } catch (e) {
            log.error('VideoPlayer.attach', e);
        }
    }

    // Constructor continues
    if (!delayedAttach) {
        me.attach();
    }

    // PUBLIC METHODS

    /**
     * Starts video with ads before/after, if exists
     * @param {object} props details of the media being started, and for ads. The startVideo parameters are equivalent to the members of props, but there are three additions
     * @param {string} adCategory props.adCategory is the category used as parameter when getting ads from the ad system
     * @param {string} startAdPos props.startAdPos is the identifier for the ad to be showed before video starts
     * @param {string} endAdPos props.endAdPos is the identifier for the ad to be showed after video ends
     */

    this.start = function(props) {
        try {
            // metaUrl,progId,hostProgId,live,timeBegin,startPos,endPos,adCategory,startAdPos,endAdPos,bannerAdPos,bannerAdElement,programTitle,items,bufferFlash,bufferContent,crop
            me.hideStartAd();
            me.hideEndAd(false);
            if (props.live == null) props.live = false;
            changeControls({'live': props.live});
            this.reset(true);

            if (!cancelled) {
                started = false;
                live = props.live;
                currentProgId = props.progId;
                programTitle = props.programTitle;
                items = props.items;
                if (props.adCategory && props.adCategory.length > 0 && ie) { // has ads
                    adCategory = props.adCategory;
                    if (props.endAdPos && props.endAdPos.length > 0) { // has end ad
                        endAdPos = props.endAdPos;
                    }
                    if (props.startAdPos && props.startAdPos.length > 0) { // has start ad
                        var bufferWaitCleared = false;
                        delayedProps = {'metaUrl': props.metaUrl, 'progId': props.progId, 'hostProgId': props.hostProgId, 'live': props.live, 'timeBegin': props.timeBegin, 'startPos': props.startPos, 'endPos': props.endPos, 'crop': props.crop};
                        log.debug('VideoPlayer.start', 'Retrieving start ad');
                        getAd(props.adCategory,props.startAdPos, 'videoAdStart' + index, function(adCode) {
                                if (!started) {
                                    try {
                                        window.clearTimeout(hFallbackTimer);
                                        bufferWaitCleared = true;
                                    } catch(e) {}
                                    if (getFlashVersion() >= 8) {
                                        if (adCode.toLowerCase().indexOf('enablewaitforflash') > 0 || waitForAd) {
											showStartAd(bufferFlashEmbed1 + props.bufferFlash + bufferFlashEmbed2 + adCode + bufferFlashEmbed3);
                                            waitForAd = true;
                                            hFallbackTimer = window.setTimeout(me.startVideo, fallbackDelay2);
                                        } else {
                                            showStartAd(bufferFlashEmbed1 + props.bufferFlash + bufferFlashEmbed2 + bufferFlashEmbed3);
                                            me.startVideo();
                                        }
                                    } else {
                                        me.startVideo();
                                    }
                                }
                            }
                        );
                        if (!bufferWaitCleared) hFallbackTimer = window.setTimeout(me.startVideo, fallbackDelay1);
                    } else { // start video immediately
                        log.debug('VideoPlayer.start', 'No start ad, starting immediately.');
                        if (props.bufferFlash && props.bufferFlash.length > 0 && ie) {
                            $('videoAdStart' + index).innerHTML = bufferFlashEmbed1 + props.bufferFlash + bufferFlashEmbed2 + bufferFlashEmbed3;
                            showStartAd();
                        } else if (props.bufferContent && props.bufferContent.length > 0 && ie) {
                            $('videoAdStart' + index).innerHTML = props.bufferContent;
                            showStartAd();
                        }
                        me.startVideo(props.metaUrl, props.progId, props.hostProgId, props.live, props.timeBegin, props.startPos, props.programTitle, props.paused, props.endPos, props.crop);
                    }
                } else {
                    log.debug('VideoPlayer.start', 'No ads, starting immediately.');
                    if (props.bufferFlash && props.bufferFlash.length > 0 && ie) {
                        $('videoAdStart' + index).innerHTML = bufferFlashEmbed1 + props.bufferFlash + bufferFlashEmbed2 + bufferFlashEmbed3;
                        showStartAd();
                    } else if (props.bufferContent && props.bufferContent.length > 0 && ie) {
                        $('videoAdStart' + index).innerHTML = props.bufferContent;
                        showStartAd();
                    }
                    this.startVideo(props.metaUrl, props.progId, props.hostProgId, props.live, props.timeBegin, props.startPos, props.programTitle, props.paused, props.endPos, props.crop);
                }
            }
        } catch(e) {
            log.error('VideoPlayer.start', e);
        }
    }

    /**
     * Stops playback of video or ads, and resets all variables related to the program/video being played
     */

    this.reset = function() {
        try {
            if (ie && vp) {
                vp.stop();
                if ($('video' + index)) $('video' + index).style.visibility = 'hidden';
            }
            bufferingStarted = false;
            buffering = false;
            videoStarted = false;
            sliderRemapped = false;
            lastDur = 0;
            lastPos = 0;
            sliderEvtsEnabled = true;
            dragging = false;
            live = false;
            started = false;
            waitForAd = false;
            endAdPos = '';
            endAdCode = '';
            adCategory = '';
            adSession = Math.round(Math.random() * 10000);
            currentProgId = 0;
            programTitle = '';
            delayedProps = null;
            items = null;
            currentItem = null;
            cancelled = false;
            wmpErrorMsg = '';
            wmpErrorCode = '';
            qualityStatusShowing = false;
            prevStatusMsg = null;
            mutedFromButton = false;
            hideAd = null;
            startPos = 0;
            endPos = null;
            crop = false;
            lastStartPos = 0;
            lastCrop = false;

            if (slider) {
                slider.setValue(0);
            }
            if (progressBar) {
                progressBar.setValue(0);
            }
            if ($('playerStatus' + index))
                $('playerStatus' + index).innerHTML = '';
            if ($('timeElapsed' + index))
                $('timeElapsed' + index).innerHTML = '';
            if ($('duration' + index))
                $('duration' + index).innerHTML = '';

            if ($('muteButton' + index)) {
                Element.addClassName($('muteButton' + index), 'soundButton');
                Element.removeClassName($('muteButton' + index), 'muteButton');
            }

            try {
                window.clearTimeout(hFallbackTimer);
            } catch(e) {
            }
            try {
                window.clearTimeout(titleTimerId);
            } catch(e) {
            }
        } catch(e) {
            log.error('VideoPlayer.reset', e)
        }
    }

    this.detach = function() {
        try {
            log.debug('VideoPlayer', 'Player ' + index + ' going to sleep.');
            if (vp && vp.reset) vp.reset();
            me.reset();
            vp = null;
        } catch(e) {
            log.error('VideoPlayer.detach', e)
        }
    }

    /**
     * Starts video playback immediately, without ads
     * @param {string} metaUrl The url pointing to the media file, to be used by Windows Media Player. If omitted, parameters from start method are used instead.
     * @param {int} progId the video program's id, used for identifying current stream, or to get correct stream in live programs
     * @param {int} hostProgId optional, as props.progId, but used for sub programs having a real host program
     * @param {boolean} live indicates that stream is live if included, and set to true
     * @param {int} timeBegin not used
     * @param {int} startPos when starting playback, jumps playing position to the number of seconds from the start. startPos is optional.
     * @param {string} programTitle the title of the program, to be showed in the status area.
     * @param {boolean} paused
     * @param {int} endPos playback stops when endPos is reached. endPos is optional.
     * @param {boolean} crop Should the video player crop to only show the playable area (between startPos and endPos). crop is optional.
     */
    this.startVideo = function(metaUrl, progId, hostProgId, pLive, timeBegin, startPos, pProgramTitle, paused, endPos, crop) {
        try {
            window.clearTimeout(hFallbackTimer);
        } catch(e) {}
        if (metaUrl && metaUrl.length > 16) {
            //this.reset();
            live = pLive;
            programTitle = pProgramTitle;
            currentProgId = progId;
            if (live == null) live = false;
            if (ie) {
                if (waitForAd) {
                    delayedProps = {'metaUrl': metaUrl, 'progId': progId, 'hostProgId': hostProgId, 'live': live, 'timeBegin': timeBegin, 'startPos': startPos, 'endPos': endPos, 'crop': crop};
                    hFallbackTimer = window.setTimeout(me.startVideo, fallbackDelay2);
                } else {
                    vp.start(metaUrl, progId, hostProgId, live, timeBegin, startPos, paused, endPos, crop);
                    if (miniOnly) {
                        prevVolume = me.getVolume();
                        me.setVolume(0);
                    }
                    hFallbackTimer = window.setTimeout(me.hideStartAd, fallbackDelay3);
                    started = true;
                    waitForAd = false;
                    delayedProps = null;
                }
            } else {
                log.debug('Starting non-scriptable WMP plugin');
                startNonIE(metaUrl);
            }
        } else { // delayed start, using delayedProps
            if (delayedProps && !started) {
                if (ie) {
                    vp.start(delayedProps.metaUrl, delayedProps.progId, delayedProps.hostProgId, delayedProps.live, delayedProps.timeBegin, delayedProps.startPos, delayedProps.paused, delayedProps.endPos, delayedProps.crop);
                    if (miniOnly) {
                        prevVolume = me.getVolume();
                        me.setVolume(0);
                    }
                } else {
                    startNonIE(delayedProps.metaUrl);
                }
                hFallbackTimer = window.setTimeout(me.hideStartAd, fallbackDelay3);
                started = true;
                waitForAd = false;
                delayedProps = null;
            }
        }
    }

    /**
     * Shows the player in the large design.
     */

    this.showMaxi = function() {
        try {
            Element.removeClassName($('videoPanel' + index), 'mini');
            Element.removeClassName($('videoPanel' + index), 'maxi');
        } catch (e) {
        }
        Element.addClassName($('videoPanel' + index), 'maxi');
        $('videoPanel' + index).style.display = 'block';
    }

    /**
     * Shows the player in the small design.
     */
    this.showMini = function() {
        if (ie) {
            try {
                Element.removeClassName($('videoPanel' + index), 'mini');
                Element.removeClassName($('videoPanel' + index), 'maxi');
            } catch (e) {
            }
            Element.addClassName($('videoPanel' + index), 'mini');
            $('videoPanel' + index).style.display = 'block';
        } else {
            stopNonIE();
        }
    }

    /**
     * Reveals a hidden video player
     * @param {boolean} videoOnly if the optional parameter is set to true, only the video area will be revealed, assuming the controls and panels is not hidden.
     * @see #hide
     */
    this.show = function(videoOnly) {
        if (videoOnly) {
            $('video' + index).style.display = 'block';
        } else {
            $('videoPanel' + index).style.display = 'block';
        }
    }

    /**
     * Hides the video player
     * @param {boolean} videoOnly if the optional parameter is set to true, only the video area will be hidden, leaving the controls and panels visible.
     * @see #show
     */
    this.hide = function(videoOnly) {
        if (ie) {
            try {
                if (videoOnly) {
                    $('video' + index).style.display = 'none';
                } else {
                    me.hideEndAd(false);
                    me.hideStartAd();
                    try {
                        vp.stop(true);
                    } catch(e) {
                    }
                    ;
                    $('videoPanel' + index).style.display = 'none';
                    try {
                        window.clearTimeout(hFallbackTimer);
                    } catch(e) {
                    }
                    try {
                        window.clearTimeout(titleTimerId);
                    } catch(e) {
                    }
                    if (callbacks && callbacks.onHide) {
                        callbacks.onHide();
                    }
                }
            } catch(e) {
                log.error('VideoPlayer.hide', e);
            }
        } else {
            stopNonIE();
            $('videoPanel' + index).style.display = 'none';
            try {
                window.clearTimeout(hFallbackTimer);
            } catch(e) {
            }
            try {
                window.clearTimeout(titleTimerId);
            } catch(e) {
            }
            if (callbacks && callbacks.onHide) {
                callbacks.onHide();
            }
        }
    }

    /**
     * Gets the playing state of the video player.
     * @return state, set to true when video is playing, otherwise false
     * @type boolean
     */
    this.isPlaying = function() {
        try {
            if (vp)
                return vp.isPlaying();
            else if (!ie)
                return ($('video' + index) && $('video' + index).className.indexOf('nonIE') >= 0);
            else
                return false;
        } catch(e) {
            return false;
        }
    }

    /**
     * Gets the playing state of the video player.
     * @return state, set to true when video is playing, otherwise false
     * @type boolean
     */
    this.isLive = function() {
        return live;
    }


    /**
     * Gets the mute state of the video player.
     * @return mute setting, set to true when video is muted, otherwise false
     * @type boolean
     */
    this.isMuted = function(fromButton) {
        try {
            if (fromButton) {
                return mutedFromButton;
            } else
                return vp.isMuted();
        } catch(e) {
            return false;
        }
    }

    /**
     * Sets the sound volume of the video player.
     * @param {int} level a number between 0 and 100
     */
    this.setVolume = function(level) {
        vp.setVolume(level);
    }

    /**
     * Gets the sound volume of the video player.
     * @return a number between 0 and 100
     * @type int
     */
    this.getVolume = function() {
        return vp.getVolume();
    }

    /**
     * Mutes the video player.
     */
    this.mute = function() {
        Element.addClassName($('muteButton' + index), 'muteButton');
        Element.removeClassName($('muteButton' + index), 'soundButton');
        return vp.mute();
    }

    /**
     * Unmutes the video player.
     */
    this.unmute = function() {
        vp.unmute();
        Element.addClassName($('muteButton' + index), 'soundButton');
        Element.removeClassName($('muteButton' + index), 'muteButton');
    }

    /**
     * Go to fullscreen mode
     */
    this.gotoFullscreen = function() {
        vp.gotoFullscreen();
    }



    /**
     * Moves playback to the specified position
     * @param {int} pos number of seconds from the beginning of the program
     *  */
    this.gotoPos = function(pos) {
        if (!live) {
            vp.gotoPos(pos);
        }
    }

    /**
     * Gets the playback position (time)
     * @return pos number of seconds from the beginning of the program
     * @type int
     **/
    this.getPos = function(pos) {
        return vp.getPos();
    }

    /**
     * Gets the ID of the currently opened program
     * @return progId
     * @type int
     */
    this.getProgId = function() {
        return currentProgId;
    }


    /**
     * Gets the ID of the currently playing item
     * @return itemId
     * @type int
     */
    this.getCurrentItemId = function() {
        if (currentItem && currentItem.id != 0) {
            return currentItem.id;
        }
    }

    /**
     *  Turns on the waiting timer for ads
     */
    this.enableAdWait = function() {
        waitForAd = true;
    }

    /*

        this.setStartAd = function(htmlCode) {
            startAdCode = htmlCode;
        }

        this.setEndAd = function(htmlCode) {
            endAdCode = htmlCode;
        }
    */

    /**
     * Hides the ad shown before the video starts, if any
     */
    this.hideStartAd = function() {
        //showingStartAd = false;
        $('video' + index).style.visibility = 'visible';
        //log.debug('VideoPlayer.hideStartAd','started');
        $('videoAdStart' + index).style.display = 'none';

        if ($('videoAdStart' + index).innerHTML.length > 0)
            window.setTimeout(function() {
                $('videoAdStart' + index).innerHTML = '';
            }, 10); // avoid extra load from flash object when playing video(s)
        //log.debug('VideoPlayer.hideStartAd',$('videoAdStart' + index).innerHTML);
        try {
            window.clearTimeout(hFallbackTimer);
        } catch(e) {
        }

    }

    /**
     * Hides the ad shown after the video ends
     */
    this.hideEndAd = function(hideAll) {
        //showingEndAd = false;
        //log.debug('VideoPlayer.hideEndAd','started');
        $('videoAdEnd' + index).innerHTML = '';
        // avoid extra load from flash object when playing video(s)
        $('videoAdEnd' + index).style.display = 'none';
        //log.debug('VideoPlayer.hideEndAd',$('videoAdEnd' + index).innerHTML);
    }

    /**
     * @ignore
     */
    this.hideToMini = function() {
        me.hideEndAd();
        try {
            window.clearTimeout(hFallbackTimer);
        } catch(e) {
        }
        me.showMini();
    }

    /**
     * To be called when ad is finished
     */
    this.adEnded = function() {
        if (showingStartAd) {
            showingStartAd = false;
            $("videoAdStart" + index).style.display = "none";
            me.startVideo();
        } else if (showingEndAd) {
            showingEndAd = false;
            if (callbacks && callbacks.onVideoEnd) {
                var itemId;
                if (currentItem != null) itemId = currentItem.id;
                callbacks.onVideoEnd(index, currentProgId, itemId);
            }
        }
    }

    /**
     * Updates an ad on the page with the a new one (using the submitted id)
     */
    this.updatePageAd = function(adId) {
        var element = $$("#additional-content .ad")[0];
        if (element != null) {
            if (!this.adHeight) {
                if (element.offsetHeight) {
                    this.adHeight = element.offsetHeight;
                } else {
                    this.adHeight = element.getStyle("height");
                }
            }
            if (!this.adFormat) {
                var scripts = element.getElementsByTagName("script");
                for (var i = 0; i < scripts.length; i++) {
                    if (scripts[i].innerHTML.indexOf("CM8ShowAd") >= 0) {
                        this.adFormat = scripts[i].innerHTML.replace(/(?:.|\s)*CM8ShowAd\("(.*?)"\)(?:.|\s)*/, "$1");
                    }
                }
            }
            var iframeSrc = "/tv4se/jsp/adFrame.jsp";
            iframeSrc += "?adCategory=" + adCategory;
            if (adFormat != null) {
                iframeSrc += "&adFormat=" + adFormat;
            }
            if (adId != null) {
                iframeSrc += "&adId=" + adId;
            }
            var iframeCode = "<iframe src=\"" + iframeSrc + "\" id=\"synched-ad\" frameborder=\"0\" scrolling=\"no\" style=\"height: " + (this.adHeight - 4) + "px\"></iframe>";
            element.innerHTML = iframeCode;
        }
    }

    // PRIVATE METHODS, INCLUDING EVENT HANDLERS

    function startNonIE(url) {
        /*if ($('videoE' + index)) {
              Element.remove($('videoE' + index));
          }*/

        if ($('video' + index)) {
            var el = $('video' + index);
            Element.remove(el);
        }

        Element.addClassName('videoPanel' + index, 'nonIE')
        /*if (navigator.userAgent.toLowerCase().indexOf("mac")>0) {
              url = url + '&overrideBW=1000';
          }*/
        var vpNonIE;
        if (navigator.userAgent.toLowerCase().indexOf("mac") > 0 && navigator.userAgent.toLowerCase().indexOf("gecko") > 0) {
            log.debug('VideoPlayer', 'Creating plugin code for Firefox on MacOS');
            vpNonIE = document.createElement('embed');
            vpNonIE.id = 'video' + index;
            vpNonIE.setAttribute('class', 'videoObject nonIE');
            vpNonIE.setAttribute('type', 'application/x-mplayer2');
            vpNonIE.setAttribute('scale', 'tofit');
            vpNonIE.setAttribute('src', url);
            vpNonIE.setAttribute('filename', url);
            vpNonIE.setAttribute('autoplay', 'true');
            vpNonIE.setAttribute('controller', 'true');
            vpNonIE.setAttribute('pluginspage', 'http://www.microsoft.com/windows/windowsmedia/player/wmcomponents.mspx');
        } else {
            log.debug('VideoPlayer', 'Creating standards-compliant plugin code');
            var params;
            vpNonIE = document.createElement('object');
            vpNonIE.id = 'video' + index;
            vpNonIE.setAttribute('class', 'videoObject nonIE');
            vpNonIE.setAttribute('type', 'application/x-mplayer2');
            params = '<param name="stretchToFit" value="1"><param name="displaySize" value="4"><param name="autoSize" value="0"><param name="allowChangeDisplaySize" value="1">';
            if (miniOnly) {
                params += '<param name="showControls" value="0"><param name="showStatusBar" value="0"><param name="mute" value="1">';
            } else {
                params += '<param name="showControls" value="1"><param name="showStatusBar" value="1"><param name="volume" value="90">';
            }

            params += '<param name="autoStart" value="1"><param name="enableContextMenu" value="1">';
            params += '<param name="windowlessVideo" value="0"><param name="src" value="' + url + '">';
            params += '<param name="SCALE" value="tofit"><param name="AUTOPLAY" value="true">';

            if ((!live) || miniOnly) {
                params += '<param name="showTracker" value="1">';
            } else {
                params += '<param name="showTracker" value=0">';
            }
            params += $MR('sumo.web.video.nonIE.missingPlugin');

            vpNonIE.innerHTML = params;
        }

        $('videoPanel' + index).appendChild(vpNonIE);
        started = true;
    }

    function stopNonIE() {
        if ($('video' + index)) {
            Element.remove($('video' + index));
        }
        var newObj = document.createElement('div');
        newObj.setAttribute('class', 'videoObject');
        newObj.id = 'video' + index;
        $('videoPanel' + index).insertBefore(newObj, $('videoControls' + index));
    }


    function bufferingStart() {
        bufferingStarted = true;
        if (live) showStatus(programTitle);
        else hideStatus();
    }

    function playStateChange(newState, oldState) {
        if (newState < 3 || newState == 8 || newState == 10) {
            Element.addClassName($('playButton' + index), 'playButton');
            Element.removeClassName($('playButton' + index), 'pauseButton');
            /*
            Element.removeClassName($('playButton' + index), 'stopButton');
            */
        } else {
            if (false && live) {
                /*
                Element.addClassName($('playButton' + index), 'stopButton');
                */
                Element.removeClassName($('playButton' + index), 'pauseButton');
                Element.removeClassName($('playButton' + index), 'playButton');
            } else {
                Element.addClassName($('playButton' + index), 'pauseButton');
                /*
                Element.removeClassName($('playButton' + index), 'stopButton');
                */
                Element.removeClassName($('playButton' + index), 'playButton');
            }
        }
        if (newState == 6) {
            buffering = true;
            changeControls({'buffering': true});
        } else {
            buffering = false;
            changeControls({'buffering': false});
        }
    }

    function playerClicked(button) {
        vp.gotoFullscreen();
    }

    function videoStart() {
        me.hideStartAd();
        videoStarted = true;
        if (miniOnly) {
            me.setVolume(prevVolume);
            muteChange();
        }
    }

    function videoEnd() {
        if (endAdPos.length > 0) {
            log.debug('VideoPlayer.videoEnd', 'Retrieving end ad');
            getAd(adCategory, endAdPos, 'videoAdEnd' + index, showEndAd);
        } else if (callbacks && callbacks.onVideoEnd) {
            var itemId;
            if (currentItem != null) itemId = currentItem.id;
            callbacks.onVideoEnd(index, currentProgId, itemId);
        }
    }

    function changeControls(options) {
        if (options.live != null) {
            if (options.live) {
                log.debug('VideoPlayer.changeControls', 'Setting controls to live mode');
                Element.addClassName('videoControls' + index, 'live');
            } else {
                log.debug('VideoPlayer.changeControls', 'Setting controls to on demand mode');
                Element.removeClassName('videoControls' + index, 'live');
            }
        }
        if (options.buffering != null) {
            if (options.buffering) {
                log.debug('VideoPlayer.changeControls', 'Setting controls to buffering mode');
                Element.addClassName('videoControls' + index, 'buffering');
            } else {
                Element.removeClassName('videoControls' + index, 'buffering');
            }
        }
        if (options.statusMsg != null) {
            if (options.statusMsg) {
                Element.addClassName('videoControls' + index, 'statusMsg');
            } else {
                Element.removeClassName('videoControls' + index, 'statusMsg');
            }
        }
    }

    function showStatus(message) {
        $('playerStatus' + index).innerHTML = message;
        $('playerStatus' + index).title = message;
        changeControls({'statusMsg': true});
    }

    function hideStatus() {
        if (Element.hasClassName('videoControls' + index, 'statusMsg')) {
            changeControls({'statusMsg': false});
        }
    }

    function toggleQualityStatus() {
        if (qualityStatusShowing) {
            if (prevStatusMsg) {
                showStatus(prevStatusMsg);
            } else {
                changeControls({'statusMsg': false});
            }
            prevStatusMsg = null;
            window.clearTimeout(titleTimerId);
            qualityStatusShowing = false;
            Event.stopObserving('playerStatus' + index, 'click', qualityClick, false);
        } else {
            var bitrate = vp.getBitrate();
            if (bitrate != null) {
                qualityStatusShowing = true;
                if ($('videoControls' + index).className.indexOf('statusMsg') >= 0) prevStatusMsg = $('playerStatus' + index).innerHTML;
                if (callbacks && callbacks.onQualityClick)
                    showStatus($MR('sumo.web.video.status.quality1') + Math.round(bitrate / 10000) * 10 + $MR('sumo.web.video.status.quality2'));
                else
                    showStatus($MR('sumo.web.video.status.quality1') + Math.round(bitrate / 10000) * 10 + $MR('sumo.web.video.status.quality2.noDetails'));
                Event.observe($('playerStatus' + index), 'click', qualityClick, false);
                $('playerStatus' + index).style.cursor = 'pointer';
                titleTimerId = window.setTimeout(toggleQualityStatus, 10000);
            }
        }
    }

    function qualityClick() {
        if (callbacks && callbacks.onQualityClick) {
            callbacks.onQualityClick();
        }
        Event.stopObserving('playerStatus' + index, 'click', qualityClick, false);
        $('playerStatus' + index).style.cursor = 'default';
    }


    function muteChange() {
        if (vp.toggleMute()) {
            Element.addClassName($('muteButton' + index), 'muteButton');
            Element.removeClassName($('muteButton' + index), 'soundButton');
            mutedFromButton = true;
            if (callbacks && callbacks.onMuteChange) {
                callbacks.onMuteChange(index, true);
            }
        } else {
            Element.addClassName($('muteButton' + index), 'soundButton');
            Element.removeClassName($('muteButton' + index), 'muteButton');
            if (callbacks && callbacks.onMuteChange) {
                callbacks.onMuteChange(index, false);
            }
            mutedFromButton = false;
        }

    }

    function statusChange(type, message, code) {
        try {
            window.clearTimeout(titleTimerId);
        } catch(e) {
        }
        switch (type) {
            case vp.MSG_WMP_STATUS:
                if (!(live && !bufferingStarted && !videoStarted)) {
                    //$('playerStatus' + index).innerText = message;
                    //$('playerStatus' + index).title = message;
                    //if (message.indexOf('bit')>0 || message.indexOf('piller')>0) {
                    //
                    //}
                }
                break;
            case vp.MSG_WMP_BUFFERING:
            //$('playerTime' + index).innerText = 'Bufrer';
            //$('playerStatus' + index).innerText = 'Henter video (' + message + ' % fullf�rt)';
            //$('playerStatus' + index).title = 'Henter video (' + message + ' % fullf�rt)';
                break;
            case vp.MSG_WMP_ERROR:
                me.hideStartAd();
                var errmsg = $MR('sumo.web.video.status.wmperror1');
                wmpErrorMsg = message;
                var u = 0xFFFFFFFF + code + 1;
                wmpErrorCode = u.toString(16).toUpperCase();

                if (callbacks && callbacks.onError) {
                    errmsg += $MR('sumo.web.video.status.wmperror2');
                    Event.observe($('playerStatus' + index), 'click', errorClick, false);
                    $('playerStatus' + index).style.cursor = 'pointer';
                }
                showStatus(errmsg);
                break;
            case vp.MSG_OPENING:
                showStatus($MR('sumo.general.video.status.startingvideo') + programTitle);
                break;
            case vp.MSG_CHECKS_IF_STARTED:
                showStatus($MR('sumo.general.video.status.checkstarted'));
                break;
            case vp.MSG_TRIES_AGAIN:
                showStatus($MR('sumo.general.video.status.notstarted'));
                break;
        }
        //titleTimerId = window.setTimeout(function() {$('playerStatus' + index).innerText = programTitle; $('playerStatus' + index).title=programTitle},6000);
    }

    function errorClick() {
        if (callbacks && callbacks.onError) {
            callbacks.onError(wmpErrorMsg, wmpErrorCode, currentProgId);
        }
        Event.stopObserving('playerStatus' + index, 'click', errorClick, false);
        $('playerStatus' + index).style.cursor = 'default';
    }

    function getTimeString(t) {
        var hh = Math.floor(t / 3600);
        (hh == 0)?hh = '':hh = hh + ':';
        var rest = t % 3600;
        var mm = Math.floor(rest / 60);
        (mm < 10)?mm = '0' + mm + ':':mm = mm + ':';
        var ss = Math.floor(rest % 60);
        if (ss < 10) ss = '0' + ss;
        return hh + mm + ss;
    }

    function posChange(pos, dur, bufferingProgress) {
        if (this.crop) {
            dur -= this.startPos;
            pos -= this.startPos;
            if (this.endPos > 0) {
                dur = this.endPos - this.startPos;
            }
        }
        var posStr = getTimeString(pos);
        var durStr = getTimeString(dur);
        try {
            if (buffering && bufferingProgress < 100) {
                $('timeElapsed' + index).innerHTML = $MR('sumo.general.video.status.buffering');
                $('duration' + index).innerHTML = bufferingProgress + '%';
            } else {
                if (!(dragging)) {
                    if (posStr && posStr.length > 0) {
                        if (posStr.length > 7)
                            $('timeElapsed' + index).innerHTML = posStr.substring(1);
                        else
                            $('timeElapsed' + index).innerHTML = posStr;
                    } else {
                        $('timeElapsed' + index).innerHTML = '00:00';
                    }
                }
                if (!live) {
                    if (durStr.length > 7)
                        durStr = durStr.substring(1);
                    $('duration' + index).innerHTML = durStr;
                    var pd = pos / dur;

                    if (items) {
                        var newItem = items.checkForItemChange(pos, currentItem);
                        if (newItem) {
                            currentItem = newItem;
                            var prevItem = items.getPrevItem(pos);
                            var nextItem = items.getNextItem(pos);
                            /*
                            if (prevItem) {
                                $('prevButton' + index).title = $MR('sumo.general.video.control.skipto') + ' �' + prevItem.title + '�';
                            } else {
                                $('prevButton' + index).title = $MR('sumo.general.video.control.skipback');
                            }
                            if (nextItem) {
                                $('nextButton' + index).title = $MR('sumo.general.video.control.skipto') + ' �' + nextItem.title + '�';
                            } else {
                                $('nextButton' + index).title = $MR('sumo.general.video.control.skipforward');
                            }
                            */
                            if (callbacks.onItemChange) {
                                callbacks.onItemChange(currentProgId, newItem.id);
                            }
                        }
                    }
                    if (slider && slider.setValue && !dragging) {
                        sliderEvtsEnabled = false;
                        slider.setValue(pd, 0);
                        sliderEvtsEnabled = true;
                    }
                    if (progressBar) {
                        progressBar.setValue(pd);
                    }

                    if (dur != lastDur) {
                        if (slider) {
                            slider.dispose();
                        }
                        lastStartPos = this.startPos;
                        lastCrop = this.crop;
                        progressBar = new ProgressBar('progressBar' + index, 'slider' + index, 'sliderButton' + index);
                        slider = new Control.Slider('sliderButton' + index, 'slider' + index, {
                            onChange: function(v) {
                                if (sliderEvtsEnabled) {
                                    if (vp) {
                                        var pos = v * lastDur;
                                        if (lastCrop) {
                                            pos += lastStartPos;
                                        }
                                        vp.gotoPos(pos);
                                    }
                                    dragging = false;
                                    log.debug('slider.onChange', v);
                                }
                            },
                            onSlide: function(v) {
                                $('timeElapsed' + index).innerHTML = getTimeString(v * lastDur);
                            }
                        });
                        dragging = false;
                        lastDur = dur;
                    }
                }
            }
        } catch(e) {
            log.error('posChange', e);
        }
    }

    function skipBack() {
        var pos = vp.getPos();
        if (items && !live) {
            var prevItem = items.getPrevItem(pos);
            if (prevItem) {
                vp.gotoPos(prevItem.timeBegin);
            } else {
                if (vp.getDuration() < 360)    vp.shiftPos(skipBackShort)
                else vp.shiftPos(skipBackLong);
            }
        } else {
            if (vp.getDuration() < 360)    vp.shiftPos(skipBackShort)
            else vp.shiftPos(skipBackLong);
        }
    }

    function skipForward() {
        var pos = vp.getPos();
        if (items && !live) {
            var nextItem = items.getNextItem(pos);
            if (nextItem) {
                vp.gotoPos(nextItem.timeBegin);
            } else {
                if (vp.getDuration() < 360)    vp.shiftPos(skipForwardShort)
                else vp.shiftPos(skipForwardLong);
            }
        } else {
            if (vp.getDuration() < 360)    vp.shiftPos(skipForwardShort)
            else vp.shiftPos(skipForwardLong);
        }
    }

    function startDrag() {
        dragging = true;
        log.debug('slider', 'start dragging/' + dragging);
    }

    function endDrag() {
        dragging = false;
        //log.debug('slider','end dragging/' + dragging);
    }

    function ProgressBar(pb, sl, slb) {
        try {
            var progressBar = $(pb);
            var tw = parseInt($(sl).currentStyle.width);
            var bw = parseInt($(slb).currentStyle.width);
        } catch(e) {
            log.error('VideoPlayer.ProgressBar constructor', e);
        }

        this.setValue = function(v) {
            progressBar.style.width = Math.round(((tw - bw) * v) + (bw / 2)) + 'px';
        }

    }

    function getAd(sitePage, position, targetElmId, onComplete) {
        adPath = "/ajaxad/adam/noscript?cat=" + sitePage + "&format=" + position + "&page=" + new Date().getMilliseconds();

        // polluting the global scope in order to get callback from ad
        if (!window.tv2video) {
            window.tv2video = new Array();
        }
        window.tv2video[index] = me;

        log.debug('VideoPlayer.getAd', 'Retrieving ad ' + position + ' to element ' + targetElmId + ' for ' + sitePage);
        var adAjax = new Ajax.Updater(
                targetElmId,
                adPath,
        {
            evalScripts: true,
            'method': 'get',
            'onComplete': function(request) {
                if (onComplete) {
                    onComplete(request.responseText);
                }
            },
            'onError': function(request) {
                log.error('VideoPlayer.getAd', request.status + ': ' + request.statusText);
            },
            'onException': function(request) {
                log.error('VideoPlayer.getAd', request.status + ': ' + request.statusText);
            },
            'evalScripts': true,
            'onSuccess': function() {
                log.debug('VideoPlayer.getAd', 'Ad retrieved.');
            },
            'onFailure': function(request) {
                log.error('VideoPlayer.getAd', request.status + ': ' + request.statusText);
            }
        });
        return adAjax;
    }

    function showStartAd(adCode) {
        try {
            if (adCode && adCode.length > 0 && $('videoAdStart' + index).innerHTML.length > 0) {
                showingStartAd = true;
                $('videoAdStart' + index).style.display = 'block';
            }
        } catch (e) {
            log.error('VideoPlayer.showStartAd', e);
        }
    }

    function showEndAd(adCode) {
        try {
            if (adCode && (adCode.toLowerCase().indexOf('enablewaitforflash') > 0 || waitForAd) && getFlashVersion() >= 8) {
                showingEndAd = true;
                $('videoAdEnd' + index).style.display = 'block';
                hFallbackTimer = window.setTimeout(me.adEnded, fallbackDelay4);
            } else if (callbacks && callbacks.onVideoEnd) {
                var itemId;
                if (currentItem != null) itemId = currentItem.id;
                callbacks.onVideoEnd(index, currentProgId, itemId);
            }
        } catch (e) {
            log.error('VideoPlayer.showEndAd', e);
        }
    }

    function getPlayerContainer() {
        if (callbacks && callbacks.playerContainer) {
            return callbacks.playerContainer;
        } else {
            return $('videoPanel' + index).parentNode.id;
        }
    }
}

function ItemList(progId, offset) {
    this.addItem = addItem;
    this.getItem = getItem;
    this.getNextItem = getNextItem;
    this.getPrevItem = getPrevItem;
    this.checkForItemChange = checkForItemChange;
    this.getItemCount = getItemCount;

    var items = new Array();
    var emptyItem = new Item(0, 0, 100000);

    function Item(id, timeBegin, timeEnd, title) {
        this.id = id;
        this.timeBegin = timeBegin - offset;
        this.timeEnd = timeEnd - offset;
        this.duration = timeEnd - timeBegin;
        this.title = title;
    }

    function getItemCount() {
        return items.length;
    }

    function addItem(id, timeBegin, timeEnd, title) {
        items[items.length] = new Item(id, timeBegin, timeEnd, title, offset);
    }

    function getItem(id) {
        for (var i = 0; i < items.length; i++) {
            if (id == items[i].id) {
                return items[i];
            }
        }
        return false;
    }

    function getNextItem(time) {
        for (var i = 0; i < items.length; i++) {
            if (items[i].timeBegin > time) {
                logDebug('VideoPlayer', 'Found next item starting at ' + items[i].timeBegin + ', following ' + time);
                return items[i];
            }
        }
        return false;
    }

    function getPrevItem(time) {
        for (var i = items.length - 1; i >= 0; i--) {
            if (items[i].timeEnd < time) {
                logDebug('VideoPlayer', 'Found previous item ending at ' + items[i].timeEnd + ', preceding ' + time);
                return items[i];
            }
        }
        return false;
    }

    function checkForItemChange(time, currentItem) {
        var itemToReturn;
        for (var i = 0; i < items.length; i++) {
            if ((time > items[i].timeBegin) && (time < items[i].timeEnd)) {
                itemToReturn = items[i];
                break;
            }
        }

        if (!itemToReturn) {
            itemToReturn = emptyItem;
        }

        if (currentItem && currentItem.id == itemToReturn.id) {
            return false;
        } else {
            logDebug('VideoPlayer', 'Found item: ' + itemToReturn.id);
            return itemToReturn;
        }
    }

}

function DrmManager() {
    var drmObj;

    function getDrmObj() {
        try {
            if (util.isIE()) {
                if (!drmObj) {
                    drmObj = new ActiveXObject('DRM.GetLicense');
                }
            }
            return drmObj;
        } catch(e) {
            logError('DrmManager.getDrmObj', e);
        }
    }

    this.getSystemInfo = function() {
        try {
            return getDrmObj().GetSystemInfo();
        } catch(e) {
            logError('DrmManager.getDrmObj', e);
        }
    }

    this.getDRMVersion = function() {
        try {
            return getDrmObj().GetDRMVersion();
        } catch(e) {
            logError('DrmManager.getDrmObj', e);
        }
    }

    this.getDRMSecurityVersion = function() {
        try {
            return getDrmObj().GetDRMSecurityVersion();
        } catch(e) {
            logError('DrmManager.getDrmObj', e);
        }
    }

    this.storeLicense = function(lic) {
        try {
            getDrmObj().StoreLicense(lic);
            logDebug('Storing license for DRM protected video.');
        } catch(e) {
            logError('DrmManager.getDrmObj', e);
        }
    }
}


function MultiPlayer(players) {
    this.subPlay = subPlay;
    this.swapVideos = swapVideos;
    this.switchSound = switchSound;
    this.setDefaultProgram = setDefaultProgram;
    this.resetSubPlayers = resetSubPlayers;
    this.mainToMini = mainToMini;


    var defaultPrograms = new Array();
    var lastMutedIndex;

    function setDefaultProgram(index, progId) {
        defaultPrograms[index] = progId;
    }

    function subPlay(index, target, progId, autostart, pos) {
        debug('Play in player' + index + '/' + progId);
        try {
            if (progId) {
                if (!(index && target)) {
                    var sub = findSubPlayer(progId, true);
                    if (sub) {
                        index = sub.index;
                        target = sub.target;
                    }
                }

                if (players[index] && players[index].detach) {
                    players[index].detach();
                }
                var params = 'progId=' + progId + '&playerIndex=' + index + '&scope=request';
                if (autostart != null && autostart == false) {
                    params += '&autostart=false';
                }
                if (pos) {
                    params += '&startPos=' + pos;
                }
                var url = 'ajax/playSub.do';
                if (progId)
                    userNav.open(url, target, params);
            } else {

            }
        } catch(e) {
            logError('MultiPlayer.subPlay', e);
        }
    }


    function resetSubPlayers() {
        for (var i = 1; i < players.length; i++) { // start p� 1, ikke 0
            if (players[i] && players[i].detach) {
                players[i].detach();
            }
        }
    }

    function swapVideos(index, target, subProgId, subPos) {
        try {
            var mainProgId = mainPlayer.getProgId();
            var mainPlaying = mainPlayer.isPlaying() || !util.isIE();
            var mainLive = mainPlayer.isLive();
            var mainPos = mainPlayer.getPos();
            if (mainLive) mainPos = null;
            debug('Switching ' + mainProgId + ' in main player with ' + subProgId + ' in player ' + index);
            userNav.play(subProgId, null, subPos, false);

            if (mainProgId && mainLive) {
                subPlay(index, target, mainProgId, mainPlaying, mainPos);
            } else if (defaultPrograms[index]) {
                subPlay(index, target, defaultPrograms[index]);
            } else {
                players[index].hide();
                if (!refreshNewsSubPlayers())
                    $(target).innerHTML = '';
            }

        } catch(e) {
            logError('MultiPlayer.swapVideos', e);
        }
    }

    function mainToMini() {
        if (mainPlayer.isPlaying() && mainPlayer.isLive()) {
            var sub = findSubPlayer(mainPlayer.getProgId());
            if (sub) {
                subPlay(sub.index, sub.target, mainPlayer.getProgId());
            }
        }
    }

    function findSubPlayer(progId, force) {
        // bygg ut slik at element-id-ene det refereres til hentes inn via constructor
        //if ((players[1] && players[1].isPlaying() && players[1].getProgId()==progId) || (players[2] && players[2].isPlaying() && players[2].getProgId()==progId)) {
        //	return null;
        //}
        if (!($('nettavisen') || (players[2] && players[2].isPlaying && players[2].isPlaying()))) {
            tiles.stopTextFeed('nettavisen');
            return {index: 2, target: 'videoContainer2'};
        } else if (!(players[1] && players[1].isPlaying && players[1].isPlaying())) {
            return {index: 1, target: 'videoContainer1'};
        } else if (force) {
            tiles.stopTextFeed('nettavisen');
            return {index: 2, target: 'videoContainer2'};
        } else {
            return null;
        }
    }

    function refreshNewsSubPlayers() {
        var refreshAllowed = true;
        for (var i = 1; i < players[i]; i++) {
            if (players[i].isPlaying && players[i].isPlaying()) {
                refreshAllowed = false;
            }
        }
        if (refreshAllowed && $('leftTab')) {
            var url = 'ajax/leftTab.do?pngStyle=dark';
            userNav.open(url, 'leftTab');
        }
        return refreshAllowed;
    }

    function switchSound(index, muted) {
        try {
            debug('Muted: ' + muted);
            if (muted) {
                if (lastMutedIndex != null && index != 0 && lastMutedIndex != index) {
                    if (!players[lastMutedIndex].isMuted(true)) {
                        players[lastMutedIndex].unmute();
                        debug('multiPlayer is unmuting player ' + lastMutedIndex);
                    }
                }
            } else {
                for (var i = 0; i < players.length; i++) {
                    if (players[i] && players[i].mute) {
                        if (!players[i].isMuted(true) && i != index) {
                            players[i].mute();
                            lastMutedIndex = i;
                            debug('multiPlayer is muting player ' + i);
                        }
                    }
                }
                players[index].unmute();
                debug('multiPlayer is unmuting player ' + index);
            }
            debug('lastMutedIndex: ' + lastMutedIndex);
        } catch(e) {
            logError('MultiPlayer.switchSound', e);
        }
    }

}

function DummyPlayer(index, callbacks) {
    if (callbacks && callbacks.onClick)
        Event.observe('videoPanel' + index, 'click', function() {
            callbacks.onClick();
            return false
        }, false);
    else
        Event.observe('videoPanel' + index, 'click', function() {
            return false
        }, false);
}


function getFlashVersion() {
    var isIE = navigator.userAgent.indexOf("MSIE") >= 0 && navigator.userAgent.indexOf("Opera") < 0 && navigator.userAgent.indexOf("Mac") < 0;

    function getAxVersion()
    {
        var version;
        var axo;
        var e;

        try {
            axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
            version = major(axo.GetVariable("$version"));
        } catch (e) {
        }
        if (!version) {
            try {
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                version = 6;
                axo.AllowScriptAccess = "always";
                version = major(axo.GetVariable("$version"));
            } catch (e) {
            }
        }
        if (!version) {
            try {
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
                version = major(axo.GetVariable("$version"));
            } catch (e) {
            }
        }
        if (!version) {
            try {
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
                version = 3;
            } catch (e) {
            }
        }
        if (!version) {
            try {
                axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                version = 2;
            } catch (e) {
                version = -1;
            }
        }
        return version;
    }

    function major(versionStr) {
        try {
            tempArray = versionStr.split(" ");
            return tempArray[1].split(",")[0];

        } catch(e) {
            return -1;
        }
    }

    var version = -1;

    if (navigator.plugins != null && navigator.plugins.length > 0) {
        if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
            var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
            var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
            version = flashDescription.split(" ")[2].split(".")[0];
            //var tempArrayMajor = descArray[2].split(".");
            //version = tempArrayMajor[0];
        }
    }
    else if (isIE) {
        version = getAxVersion();
    }
    return version;
}


/**
 * Creates a new video player connected to a specific WMP ActiveX-object in the HTML code
 * @class Represents a video player with core functionality
 * @constructor
 * @param {string} wmpElementId the HTML ID of the WMP object
 * @author Tor Erik Alr�k
 * @version 2.2
 */


function CorePlayer(wmpElementId, log) {

    // PRIVATE VARIABLES
    var p;
    // player element
    this.p = p;
    var metaUrl = '';
    var progId;
    var hostProgId;
    var live = false;
    var timeBegin;
    var startPos;
    var posUpdateInterval = 500;
    //ms
    var me = this;
    // in WMP events, 'this' will not work

    var hUpdaterTimer;
    // Handles for setTimeouts/setIntervals
    var hLiveAttTimer;
    var hLiveRetryTimer;

    var reportWmpErrors = true;

    var bufferState;
    var oldPlayState;
    var oldOpenState;
    var lastProgress;
    var noProgressCounter;
    var lastErrorCode;
    var startAsPaused = false;
    var waitingForUser = 0;

    // for direktesendt video
    var retryCount = 0;
    var liveEvaluateCount = 0;

    // CONSTRUCTOR OPERATIONS
    try {
        p = $(wmpElementId);
        p.attachEvent('OpenStateChange', wmpOpenStChg);
        p.attachEvent('PlayStateChange', wmpPlayStChg);
        p.attachEvent('StatusChange', wmpStatusChange);
        p.attachEvent('Error', wmpError);
        p.attachEvent('Buffering', wmpBuffering);
        p.attachEvent('Click', wmpClick);
    } catch (e) {
        log.error('Player.constructor', e);
        log.error('Player.constructor', 'Could not access HTML element for WMP');
    }

    // PRIVATE METHODS
    // WMP event handlers
    function wmpOpenStChg(newState) {
        //log.debug('Player.wmpOpenStChg','openState:  ' + newState);
        if (newState == 14 || newState == 16 || newState == 18) {
            waitingForUser++;
        }
        if (newState == 15 || newState == 17 || newState == 19) {
            waitingForUser--;
        }
        if (me.openStateChange) {
            me.openStateChange(newState, oldOpenState);
        }
        oldOpenState = newState;
    }

    function wmpClick(button, shiftState, fX, fY) {
        //log.debug('Player.wmpPlayStChg','playState: ' + newState);
        if (me.onPlayerClick && button == 1) {
            me.onPlayerClick(button, shiftState, fX, fY);
        }
    }

    function wmpPlayStChg(newState) {
        //log.debug('Player.wmpPlayStChg','playState: ' + newState);
        if (me.onPlayStateChange) {
            me.onPlayStateChange(newState, oldPlayState);
        }
        if (newState == 3 && bufferState == 0 && startPos && startPos > 0) {
            me.gotoPos(startPos);
        }
        //log.debug('Player.wmpPlayStChg','playState: ' + newState + ' !me.finished: ' + !me.finished + ' me.started: ' + me.started);
        if (newState == 8 && !me.finished && me.started) { // Media ended
            log.debug('Player.wmpPlayStChg', 'fire event onVideoEnd');
            me.finished = true;
            if (me.onVideoEnd) {
                me.onVideoEnd();
            }
        }
        oldPlayState = newState;
    }

    function wmpStatusChange() {
        //log.debug('Player.wmpStatusChange','Status:  ' + p.status);
        if (me.onStatusChange && p.playState != 6) {
            me.onStatusChange(me.MSG_WMP_STATUS, p.status, p.playState);
        }
    }

    function wmpError() {
        var errorStr = '';
        var lastErrorCode = 0;
        for (var i = 0; i < p.error.errorCount; i++) {
            var errItem = p.error.item(i);
            errorStr += errItem.errorDescription + ' / ';
            lastErrorCode = errItem.errorCode;
            log.error('Player.p (' + p.id + ')', errItem.errorCode + ': ' + errItem.errorDescription);
        }
        errorStr = errorStr.substring(0, errorStr.length - 4);
        p.error.clearErrorQueue();
        if (reportWmpErrors && me.onStatusChange) {
            me.onStatusChange(me.MSG_WMP_ERROR, errorStr, lastErrorCode);
        }
    }

    function wmpBuffering() {

    }

    // Live startup
    function tryLive() {
        log.debug('Player.tryLive', 'Attempt ' + retryCount);
        try {
            var max = 6;
            //reportWmpErrors = false;
            if (retryCount < max) {
                if (retryCount == 0) {
                    p.URL = metaUrl;
                    p.controls.play();
                } else {
                    p.controls.play();
                }
                retryCount++;
                evaluateLiveAttempt();
            } else if (retryCount == max) {
                reportWmpErrors = true;
                retryCount++;
                evaluateLiveAttempt();
            }
        } catch(e) {
            log.debug('CorePlayer.tryLive', 'Player element probably missing.');
            if (me.onCeased) {
                me.onCeased();
            }
        }
    }

    function evaluateLiveAttempt() { //
        // Call when media url is opened.
        // This function checks if the stream really starts,
        // or if the broadcast is not started yet
        try {
            var hasError = false;
            var readyState;
            var errorCode = 0;

            liveEvaluateCount++;
            if (me.onStatusChange) {
                me.onStatusChange(me.MSG_CHECKS_IF_STARTED);
            }
            readyState = p.openState;
            hasError = (p.error.errorCount > 0);
            if (hasError) errorCode = p.error.item(0).errorCode + '';
        } catch (e) {
            log.debug('CorePlayer.evaluateLiveAttempt1', 'Player element probably missing.');
            return;
        }
        if (liveEvaluateCount > 20 && waitingForUser <= 0) { // nothing seems to be happening, giving up for a while
            try {
                liveEvaluateCount = 0;
                retryLive();
                log.debug('Player.evaluateLive', 'Nothing seems to be happening, giving up for a while');
                if (me.onStatusChange) {
                    me.onStatusChange(me.MSG_TRIES_AGAIN);
                }
            } catch (e) {
                log.error('CorePlayer.evaluateLiveAttempt2', e);
            }
        }
        else { // not given up yet
            //log.debug('Player.evaluateLive','openState: ' + readyState);
            if (readyState == 13) {// Media open
                try {
                    // a stream is open
                    // check for correct progId
                    log.debug('Player.evaluateLive', 'Video opening');
                    var streamProgId = parseFloat(p.currentMedia.getItemInfo("author"));
                    if (!streamProgId) streamProgId = 0;
                    if (!hostProgId) hostProgId = progId;
                    if (streamProgId > 0 && !(progId == streamProgId || hostProgId == streamProgId)) { // progId mismatch
                        log.debug('Player.evaluateLive', 'progId mismatch: ' + progId + '/' + hostProgId + '/' + streamProgId);
                        if (me.onStatusChange) {
                            me.onStatusChange(me.MSG_WRONG_PROGRAM);
                        }
                        // stop the stream and try again in a while
                        me.stop();
                        retryLive();
                    } else { // correct progId or no streamProgId
                        log.debug('Player.evaluateLive', 'Correct progId or no streamProgId: ' + progId + '/' + hostProgId + '/' + streamProgId);
                        hUpdaterTimer = window.setInterval(wmpStatusUpdater, posUpdateInterval);
                        if (me.onBufferingStart) me.onBufferingStart();
                        reportWmpErrors = true;
                    }
                } catch (e) {
                    log.error('CorePlayer.evaluateLiveAttempt3', e);
                }
            } else {
                try {
                    if (hasError && (errorCode == '-2147014835' || errorCode == '-1072885299') && p.controls.currentPosition == 0) {
                        // probably not started, try again
                        log.debug('Player.evaluateLive', 'Error in player, show probably not started');
                        p.error.clearErrorQueue();
                        retryLive();
                        if (me.onStatusChange) {
                            me.onStatusChange(me.MSG_TRIES_AGAIN);
                        }
                    }
                    else { // something is happening - just wait a bit more
                        hLiveAttTimer = window.setTimeout(evaluateLiveAttempt, 1000);
                    }
                } catch (e) {
                    log.error('CorePlayer.evaluateLiveAttempt4', e);
                }
            }
        }
    }

    function retryLive() {
        var delay = 5000;
        hLiveRetryTimer = window.setTimeout(tryLive, delay);
    }

    function resetTimers() {
        if (hUpdaterTimer) {
            window.clearInterval(hUpdaterTimer);
        }
        if (hLiveAttTimer) {
            window.clearInterval(hLiveAttTimer);
        }
        if (hLiveRetryTimer) {
            window.clearInterval(hLiveRetryTimer);
        }
    }

    // General playback methods
    //var xyz=0;

    function wmpStatusUpdater() {
        //var xz = new Date();
        //debug(xyz + ' Start: ' +xz.getSeconds() + ':' + xz.getMilliseconds());
        if (!p) {
            if (me.onCeased)
                me.onCeased();
            return;
        }
        if (p.openState == 13 && p.playState != 10) { // media open & no error
            if (me.onPosChange) {
                me.onPosChange(p.controls.currentPosition, p.currentMedia.duration, p.network.bufferingProgress);
                // double-phew
            }
            if (p.playState == 6 && me.onStatusChange && p.network.bufferingProgress < 100) {
                me.onStatusChange(me.MSG_WMP_BUFFERING, p.network.bufferingProgress);
            }
            if (isBufferingJustFinished()) {
                log.debug('Player.statusUpdater', 'First buffering finished');
                me.started = true;
                if (startAsPaused) {
                    p.controls.pause();
                }
                if (me.onVideoStart) { // first time buffering when starting up
                    me.onVideoStart();
                }
            }
            if (me.endPos > 0 && p.controls.currentPosition >= me.endPos) {
                me.onVideoEnd();
            }
            // kvalitetsm�ling
        }
        //var yz = new Date();
        //debug(xyz + ' End: ' +yz.getSeconds() + ':' + yz.getMilliseconds());
        //xyz++;
    }

    function isBufferingJustFinished() {
        if (p.controls.currentPosition == 0) {
            if (bufferState == 0 && p.network.sourceProtocol != 'http' && p.network.bufferingProgress == 100) {
                bufferState = 1;
                return true;
            } else {
                return false;
            }
        } else {
            if (bufferState == 0) {
                bufferState = 1;
                return true;
            } else {
                return false;
            }
        }
    }

    function checkVideoFinished() {
        // 'media ended' play state change is not done correctly for live streams being stopped.
        // Instead, finish can be detected when buffering occurs with no progress (but the same state might occur for other reasons, too)

        /*
          window.status = 'playState: ' + p.playState + ' lastErrorCode: ' + lastErrorCode + ' finished: ' + this.finished + ' started: ' + this.started;
          if (live && (p.playState == 6 || p.playState == 10) && lastErrorCode == 0 && !this.finished && this.started) {
              var progress = p.network.bufferingProgress;
              if (p.playState == 10 || (progress < 100 && progress == lastProgress)) {
                  noProgressCounter++;
                  log.debug('Player.isVideoFinished','no buffering progress, counting: ' + noProgressCounter);
              }
              if ((noProgressCounter * posUpdateInterval) / 1000 >  10) { // seconds
                  // assume media is ended
                  log.debug('Player.isVideoFinished','Video finished, probably');
                  this.finished = true;
                  if (this.onVideoEnd) {
                      this.onVideoEnd();
                  }
              }


          }*/
    }

    // PUBLIC CONSTANTS
    /**
     * Indicates a "status message" type
     * @type int
     */
    this.MSG_WMP_STATUS = 0;
    /**
     * Indicates a buffering progress message
     * @type int
     */
    this.MSG_WMP_BUFFERING = 1;
    /**
     * Indicates a error message from Windows Media Player
     * @type int
     */
    this.MSG_WMP_ERROR = 2;
    /**
     * Status message: The media/stream is being opened
     * @type int
     */
    this.MSG_OPENING = 101;
    /**
     * Status message: Got no connection when trying to open media
     * @type int
     */
    this.MSG_NO_CONNECTION = 102;
    /**
     * Status message: Opened wrong stream (based on progId)
     * @type int
     */
    this.MSG_WRONG_PROGRAM = 103;
    /**
     * Status message: Checks if live video stream has started
     * @type int
     */
    this.MSG_CHECKS_IF_STARTED = 104;
    /**
     * Status message: Trying to open the live stream again
     * @type int
     */
    this.MSG_TRIES_AGAIN = 105;

    // PUBLIC PROPERTIES

    /**
     * ID of the currently playing program
     * @type int
     */
    this.progId = progId;

    /**
     * Set to true if video playback is really started (buffering completed)
     * @type boolean
     */
    this.started = false;
    // i betydningen bufring fullf�rt, videobilde vises

    /**
     * Set to true if video playback is finished (end of video reached)
     * @type boolean
     */
    this.finished = false;

    // PUBLIC METHODS
    /**
     * Starts video playback from the given url
     * @param {string} url The url pointing to the media file, to be used by Windows Media Player.
     * @param {int} pProgId the video program's id, used for identifying current stream, or to get correct stream in live programs
     * @param {int} pHostProgId optional, as props.progId, but used for sub programs having a real host program
     * @param {boolean} pLive indicates that stream is live if included, and set to true
     * @param {int} pStartPos when starting playback, jumps playing position to the number of seconds from the start. startPos is optional.
     * @param {boolean} paused
     * @param {int} pEndPos playback stops when endPos is reached. endPos is optional.
     * @param {boolean} pCrop Should the video player crop to only show the playable area (between startPos and endPos). crop is optional.

     */
    this.start = function(url, pProgId, pHostProgId, pLive, pTimeBegin, pStartPos, paused, pEndPos, pCrop) {
        if (!p) {
            if (me.onCeased)
                me.onCeased();
            return;
        }
        try {
            resetTimers();
            me.finished = false;
            me.started = false;
            me.startPos = pStartPos ? pStartPos : 0;
            me.endPos = pEndPos;
            me.crop = pCrop ? pCrop : false;
            bufferState = 0;
            oldPlayState = -1;
            oldOpenState = -1;
            lastProgress = 100;
            noProgressCounter = 0;
            lastErrorCode = 0;
            retryCount = 0;
            waitingForUser = 0;

            metaUrl = url;
            progId = pProgId;
            hostProgId = pHostProgId;
            live = pLive;
            timeBegin = pTimeBegin;
            startPos = pStartPos;
            endPos = pEndPos;
            crop = pCrop;
            if (live == null) {
                live = false;
            }
            if (me.onStatusChange) {
                me.onStatusChange(me.MSG_OPENING);
            }
            if (live) {
                tryLive();
            } else {
                hUpdaterTimer = window.setInterval(wmpStatusUpdater, posUpdateInterval);
                p.URL = metaUrl;
                window.setTimeout(this.validatePlay, 500);
                p.controls.play();
                if (paused && !live) {
                    startAsPaused = true;
                } else {
                    startAsPaused = false;
                }
                if (me.onBufferingStart) me.onBufferingStart();
            }
            log.debug('Player.start', 'Started');
        } catch (e) {
            log.error('CorePlayer.start', e);
        }
    };

    this.validatePlay = function() {
        p.controls.play();
    };

    /**
     * Resets the core player
     * @param {boolean} resetWMP if true, also stops video play back
     */
    this.reset = function(resetWMP) {
        /*
              p.detachEvent('OpenStateChange', wmpOpenStChg);
              p.detachEvent('PlayStateChange', wmpPlayStChg);
              p.detachEvent('StatusChange', wmpStatusChange);
              p.detachEvent('Error', wmpError);
              p.detachEvent('Buffering', wmpBuffering);
          */

        // stop timeouts/intervals
        resetTimers();

        if (resetWMP && p) {
            p.close();
        }
    };

    /**
     * Starts playback after pause or stop (requires open media)
     */
    this.play = function() {
        try {
            if (p.openState == 13) p.controls.play(); // 13 = MediaOpen
            me.finished = false;
        } catch (e) {
            log.error('Player.play', e)
        }
    };

    /**
     * Pauses playback (requires open media)
     */
    this.pause = function() {
        try {
            if (p.openState == 13 && !live) p.controls.pause();
        } catch (e) {
            log.error('Player.pause', e)
        }
    };

    /**
     * Stops playback (requires open media) or attempt for playback, and resets playback position to 0
     */
    this.stop = function(close) {
        try {
            window.clearInterval(hLiveAttTimer);
        } catch(e) {
        }
        try {
            window.clearInterval(hLiveRetryTimer);
        } catch(e) {
        }
        try {
            p.controls.stop();
            if (close) p.close();
        } catch(e) {
        }
    };


    /**
     * Toggles between playback or pause (or stop for live streams)
     * @return new state - true if paused, false if playing
     * @type boolean
     */
    this.togglePlay = function() {
        try {
            log.debug('Player.togglePlay', p.openState + '/' + p.playState);
            if (p.openState == 13) { // 13 = MediaOpen
                if (live) {
                    if (p.playState == 3) { // 3 = Playing
                        p.controls.stop();
                        return true;
                    } else if (p.playState == 1) { // 1 = Stopped, 2 = Paused
                        p.controls.play();
                        return false;
                    }
                } else {
                    if (p.playState == 3) { // 3 = Playing
                        p.controls.pause();
                        return true;
                    } else if (p.playState == 2 || p.playState == 1) { // 1 = Stopped, 2 = Paused
                        p.controls.play();
                        return false;
                    }
                }
            } else if (p.openState == 6) { // 6 = PlaylistOpenNoMedia, which means finished
                p.controls.stop();
                p.controls.play();
            } else {
                return false;
            }
        } catch (e) {
            log.error('Player.playToggle', e);
            return false
        }
    };

    /**
     * Makes video playback go fullscreen
     */
    this.gotoFullscreen = function() {
        try {
            if (p.playState == 3) {
                //p.uiMode = 'full';
                p.fullScreen = true;
            }
        } catch (e) {
            log.error('gotoFullscreen', e)
        }
    };

    /**
     * Checks if video is playing back in fullscreen
     * @return true if fullscreen, otherwise false
     * @type boolean
     */
    this.isFullscreen = function() {
        return p.fullScreen;
    }

    /**
     * Gets the current bitrate of the video playback
     * @return bitrate, in kbit/s.
     * @type int
     */
    this.getBitrate = function() {
        try {
            return p.network.bitRate;
        } catch (e) {
            log.error('CorePlayer.getBitrate', e)
        }
    };


    /**
     * Increases the sound level of the video playback
     * @param {int} step the amount to increase. The volume has a range of 0 to 100.
     * @return new volume setting, between 0 to 100.
     * @type int
     */
    this.volumeUp = function(step) {
        try {
            if (!step) step = 10;
            p.settings.mute = false;
            var newVol = p.settings.volume + step;
            if (newVol > 100) newVol = 100;
            p.settings.volume = newVol;
            return newVol;
        } catch (e) {
            log.error('volumeUp', e)
        }
    };

    /**
     * Sets the sound level of the video playback
     * @param {int} level The new level, in the range 0 - 100.
     */
    this.setVolume = function(level) {
        try {
            p.settings.volume = level;
        } catch (e) {
            log.error('setVolume', e)
        }
    };

    /**
     * Gets the sound level of the video playback
     * @return The volume, in the range 0 - 100.
     * @type int
     */
    this.getVolume = function() {
        try {
            return p.settings.volume;
        } catch (e) {
            log.error('getVolume', e)
        }
    };

    /**
     * Decreases the sound level of the video playback
     * @param {int} step the amount to decrease. The volume has a range of 0 to 100.
     * @return new volume setting, between 0 to 100.
     * @type int
     */
    this.volumeDown = function(step) {
        try {
            if (!step) step = 10;
            p.settings.mute = false;
            var newVol = p.settings.volume - step;
            if (newVol < 0) newVol = 0;
            p.settings.volume = newVol;
            return newVol;
        } catch (e) {
            log.error('volumeDown', e)
        }
    };

    /**
     * Toggles the mute state of the video player. If sound is muted, it will be unmuted, otherwise it will be muted.
     * @return the new mute state: True if muted, otherwise false
     * @type boolean
     */
    this.toggleMute = function() {
        try {
            p.settings.mute = !p.settings.mute;
            log.debug('Player.toggleMute', p.settings.mute);
            return p.settings.mute;
        } catch (e) {
            log.error('toggleMute', e)
        }
    };

    /**
     * Mutes the video player.
     * @return previous mute state
     * @type boolean
     *  */
    this.mute = function() {
        try {
            var prevState = p.settings.mute;
            p.settings.mute = true;
            return prevState;
        } catch (e) {
            log.error('mute', e)
        }
    };

    /**
     * Unmutes the video player.
     */
    this.unmute = function() {
        try {
            var prevState = p.settings.mute;
            p.settings.mute = false;
            return prevState;
        } catch (e) {
            log.error('unmute', e)
        }
    };

    /**
     * Returns the mute state of the video player.
     * @return true if muted, otherwise false
     * @type boolean
     */
    this.isMuted = function() {
        try {
            return p.settings.mute;
        } catch (e) {
            log.error('ismuted', e)
        }
    };

    /**
     * Returns the position of the video player.
     * @return position in seconds
     * @type float
     */
    this.getPos = function() {
        try {
            return p.controls.currentPosition;
        } catch (e) {
            log.error('Coreplayer.getPos', e)
        }
    };

    /**
     * Returns the length of the video.
     * @return length in seconds
     * @type float
     */
    this.getDuration = function() {
        try {
            return p.currentMedia.duration;
        } catch (e) {
            log.error('Coreplayer.getDuration', e)
        }
    };

    /**
     * Moves playback position to a given time (absolute)
     * @param {int} position the new position in seconds from the start
     */
    this.gotoPos = function(position) {
        try {
            p.controls.currentPosition = position;
            //this.play();
        } catch (e) {
            log.error('gotoPos', e);
            if (!me.started)
                p.close();
        }
    };

    /**
     * Moves playback position by interval (relative)
     * @param {int} delta the interval in seconds
     */
    this.shiftPos = function(delta) {
        try {
            var newPos = p.controls.currentPosition + delta;
            if (newPos + delta / 2 < p.currentMedia.duration) {
                p.controls.currentPosition = newPos;
            }
        } catch (e) {
            log.error('shiftPos', e);
            if (!me.started)
                p.close();
        }
    };


    /**
     * Returns the playback state of the video player.
     * @return true if video is playing, otherwise false
     * @type boolean
     */
    this.isPlaying = function() {
        try {
            return !(p.playState < 2 || p.playState == 8 || p.playState == 10);
        } catch (e) {
            log.error('CorePlayer.isPlaying', e);
            return false;
        }
    }

    /**
     * Returns the stream mode for the video playing, as given in start
     * @return true if video is a live stream, otherwise false
     * @type boolean
     */
    this.isLive = function() {
        return live;
    };


    // EVENTS
    /**
     * [Callback hook]
     * Fires when status of the video player changes.
     * @param {int} type evaluates to one of the constants listed under
     * @param {string} message the message details. When type is MSG_WMP_STATUS, message contains a status text from Windows Media Player.
     * When type is MSG_WMP_BUFFERING, message contains the buffering progress given in percent.
     * When type is MSG_WMP_ERROR, message contains the error description from Windows Media Player.
     * Otherwise, message is null
     * @param {string} code only given when type is MSG_WMP_ERROR. Contains error code from Windows Media Player.
     * @type function
     */
    this.onStatusChange = null;
    // (type, message, code)

    /**
     * [Callback hook]
     * Fires in a regular interval after video has started (first buffering complete), supplying the time position and duration of the video.
     * @param {int} position current position expressed as number of seconds after the start of the video
     * @param {int} duration the duration of the video file expressed in seconds
     * @param {string} posFormatted current position relative to the start of the video expressed in HH:mm:ss format
     * @param {string} posFormatted duration of the video file expressed in HH:mm:ss format
     * @type function
     */
    this.onPosChange = null;
    // (position, duration, posFormatted, durFormatted)

    /**
     * [Callback hook]
     * Fires in a regular interval during video buffering
     * @param {int} progress the percentage of buffering completeness
     * @type function
     */
    this.onBufferProgress = null;
    // (position, duration, posFormatted, durFormatted)

    /**
     * [Callback hook]
     * Fires when Windows Media Player changes it's play state
     * @param {int} newState the updated state as defined in the WMP SDK.
     * @param {int} oldState the old state before the change, as defined in the WMP SDK.
     * @type function
     */
    this.onPlayStateChange = null;
    // (newState, oldState)

    /**
     * [Callback hook]
     * Fires when Windows Media Player changes it's open state
     * @param {int} newState the updated state as defined in the WMP SDK.
     * @param {int} oldState the old state before the change, as defined in the WMP SDK.
     * @type function
     */
    this.onOpenStateChange = null;
    // (newState, oldState)

    /**
     * [Callback hook]
     * @ignore
     * @type function
     */
    this.onQualityChange = null;
    // (newQuality)

    /**
     * [Callback hook]
     * Fires when a mouse button is clicked with the cursor within the WMP object.
     * For details, See the WMP SDK documentation for the click event
     * @param {int} button which mouse button being pressed
     * @param {int} shiftState the state of the shift button
     * @param {int} fX the horizontal cursor position
     * @param {int} fY the vertical cursor position
     * @type function
     */
    this.onPlayerClick = null;

    /**
     * [Callback hook]
     * Fires when video starts buffering for the first time
     * @type function
     */
    this.onBufferingStart = null;

    /**
     * [Callback hook]
     * Fires when video starts playing for the first time, when the first buffering is finished
     * @type function
     */
    this.onVideoStart = null;

    /**
     * [Callback hook]
     * Fires when video playback reaches the end. This occurs when an on demand file is finished, or a live stream is being shut down by the publisher.
     * @type function
     */
    this.onVideoEnd = null;

    /**
     * [Callback hook]
     * Fires when video object is removed from the DOM (HTML)
     * @type function
     */
    this.onCeased = null;

}

/**
 * Messaging
 *
 */
window.$MR = function() {
    return '';
}

function MessageResource() {
    try {
        var messages = new Object();

        function addMessage(key, message) {
            messages[key] = message;
        }

        function getMessage(key) {
            if (messages[key] == null && error) {
                error('Message key not found: ' + key);
                return '!?!' + key + '!?!';
            }
            return messages[key];
        }

        messages['sumo.web.video.nonIE.missingPlugin'] = '<p>Du beh�ver Windows Media Player f�r att se video. <a href="http://windowsmedia.com/download">Ladda ner Windows Media Player h�r</a>.</p>';
        messages['sumo.web.video.status.quality1'] = 'Videokvalitet (bandbredd):';
        messages['sumo.web.video.status.quality2'] = 'kbit/s.Klicka h�r f�r att �ndra kvalitet.';
        messages['sumo.web.video.status.wmperror1'] = 'Det uppstod ett fel i Windows Media Player.';
        messages['sumo.web.video.status.wmperror2'] = 'Klicka h�r f�r mer info.';
        messages['sumo.web.video.wmperrordialog1'] = '<p><br>Windows Media Player kan inte spela den h�r videon.</p>';
        messages['sumo.general.video.status.startingvideo'] = 'Startar video: ';
        messages['sumo.general.video.status.checkstarted'] = 'Kontrollerar om programmet har b�rjat.';
        messages['sumo.general.video.status.notstarted'] = 'Programmet har inte startat? Nytt f�rs�k inom kort.';
        messages['sumo.general.video.status.buffering'] = 'Buffrar';
    } catch(e) {
        getMessage = function() {
            return 'Error initializing MessageResource.';
        };
    }
    window.$MR = getMessage;
}

messageResource = new MessageResource();