﻿var hasVideo = 0;
$().ready(function() {
    OnReady();
});

function InjectVideo() {
    if (!$.browser.msie) {
        var divcontent = $('div.video-js-box').html();
        if (divcontent == null) {

        }
        else {
            hasVideo = 1;
            //alert(divcontent);
            var sb = "";
            var videoid = "video";
            var itemUrl = "";
            var width = "";
            var height = "";
            var videoUrl = "";

            var regexObject = new RegExp("<object([\\s\\S]+?)>", "ig");
            var regexwidth1 = new RegExp("width=(\\d[0-9]+)", "ig");
            var regexwidth2 = new RegExp("width=(\"\\d[0-9]+)", "ig");

            var regexheight1 = new RegExp("height=(\\d[0-9]+)", "ig");
            var regexheight2 = new RegExp("height=(\"\\d[0-9]+)", "ig");

            //var regexParam = new RegExp("<param name=\"flashVars\"([\\s\\S]+?)/?>", "ig");
            var regexParam1 = new RegExp("'url':'(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?'", "ig");
            var regexParam2 = new RegExp("'url':'(http|https):\/\/?", "ig");


            var obj = regexObject.exec(divcontent);
            //alert(obj);
            //var b = divcontent.replace(paramRegex, "<rade_source$1></rade_source><source$1/>");
            var obj1 = regexwidth1.exec(obj);
            if (obj1 == null) obj1 = regexwidth2.exec(obj);
            if (obj1 != null) {
                var values = ("" + obj1).split(",");
                width = values[1].replace("\"", "");
            }

            var obj2 = regexheight1.exec(obj);
            if (obj2 == null) obj2 = regexheight2.exec(obj);
            if (obj2 != null) {
                var values = ("" + obj2).split(",");
                height = values[1].replace("\"", "");
            }


            var obj3 = regexParam2.exec(divcontent);
            var index = divcontent.indexOf("'url':'");
            if (index > -1) {
                var index2 = divcontent.indexOf("'", index + 7);
                if (index2 > -1) {
                    videoUrl = divcontent.substr(index + 7, index2 - index - 7);
                }
            }

            //alert(videoUrl);
            var mediaType = "flash/flv;";
            if (videoUrl.lastIndexOf(".mp4") != -1) {
                mediaType = "video/mp4; codecs=&quot;avc1.42E01E, mp4a.40.2&quot;"
            }

            sb += "<video id=\"" + videoid + "\" class=\"video-js\" controls=\"controls\" preload=\"auto\" width=\"" + width + "\" height=\"" + height + "\">";
            sb += "<source src=\"" + videoUrl + "\" type=\"" + mediaType + "\">";
            sb += divcontent;
            sb += "</video>";
            //alert(sb);
            $('div.video-js-box').html(sb)
        }
    }
}

function OnReady() {
    InjectVideo();

    if (!$.browser.msie) {
        if (hasVideo == 1) {
            LoadVideoJS();

            VideoJS.DOMReady(function () {
                var myPlayer = VideoJS.setup("video");
            });
        }
    }
}

function LoadVideoJS() {
    
    // Self-executing function to prevent global vars and help with minification
    (function(window, undefined) {
        var document = window.document;

        // Using jresig's Class implementation http://ejohn.org/blog/simple-javascript-inheritance/
        (function() { var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/; this.JRClass = function() { }; JRClass.extend = function(prop) { var _super = this.prototype; initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } function JRClass() { if (!initializing && this.init) this.init.apply(this, arguments); } JRClass.prototype = prototype; JRClass.constructor = JRClass; JRClass.extend = arguments.callee; return JRClass; }; })();

        // Video JS Player Class
        var VideoJS = JRClass.extend({

            // Initialize the player for the supplied video tag element
            // element: video tag
            init: function(element, setOptions) {

                // Allow an ID string or an element
                if (typeof element == 'string') {
                    this.video = document.getElementById(element);
                } else {
                    this.video = element;
                }
                // Store reference to player on the video element.
                // So you can acess the player later: document.getElementById("video_id").player.play();
                this.video.player = this;
                this.values = {}; // Cache video values.
                this.elements = {}; // Store refs to controls elements.

                // Default Options
                this.options = {
                    autoplay: false,
                    preload: true,
                    useBuiltInControls: false, // Use the browser's controls (iPhone)
                    controlsBelow: false, // Display control bar below video vs. in front of
                    controlsAtStart: false, // Make controls visible when page loads
                    controlsHiding: true, // Hide controls when not over the video
                    defaultVolume: 0.85, // Will be overridden by localStorage volume if available
                    playerFallbackOrder: ["html5", "flash", "links"], // Players and order to use them
                    flashPlayer: "htmlObject",
                    flashPlayerVersion: false // Required flash version for fallback
                };
                // Override default options with global options
                if (typeof VideoJS.options == "object") { _V_.merge(this.options, VideoJS.options); }
                // Override default & global options with options specific to this player
                if (typeof setOptions == "object") { _V_.merge(this.options, setOptions); }
                // Override preload & autoplay with video attributes
                if (this.getPreloadAttribute() !== undefined) { this.options.preload = this.getPreloadAttribute(); }
                if (this.getAutoplayAttribute() !== undefined) { this.options.autoplay = this.getAutoplayAttribute(); }

                // Store reference to embed code pieces
                this.box = this.video.parentNode;
                this.linksFallback = this.getLinksFallback();
                this.hideLinksFallback(); // Will be shown again if "links" player is used

                // Loop through the player names list in options, "html5" etc.
                // For each player name, initialize the player with that name under VideoJS.players
                // If the player successfully initializes, we're done
                // If not, try the next player in the list
                this.each(this.options.playerFallbackOrder, function(playerType) {
                    if (this[playerType + "Supported"]()) { // Check if player type is supported
                        this[playerType + "Init"](); // Initialize player type
                        return true; // Stop looping though players
                    }
                });

                // Start Global Listeners - API doesn't exist before now
                this.activateElement(this, "player");
                this.activateElement(this.box, "box");
            },
            /* Behaviors
            ================================================================================ */
            behaviors: {},
            newBehavior: function(name, activate, functions) {
                this.behaviors[name] = activate;
                this.extend(functions);
            },
            activateElement: function(element, behavior) {
                // Allow passing and ID string
                if (typeof element == "string") { element = document.getElementById(element); }
                this.behaviors[behavior].call(this, element);
            },
            /* Errors/Warnings
            ================================================================================ */
            errors: [], // Array to track errors
            warnings: [],
            warning: function(warning) {
                this.warnings.push(warning);
                this.log(warning);
            },
            /* History of errors/events (not quite there yet)
            ================================================================================ */
            history: [],
            log: function(event) {
                if (!event) { return; }
                if (typeof event == "string") { event = { type: event }; }
                if (event.type) { this.history.push(event.type); }
                if (this.history.length >= 50) { this.history.shift(); }
                try { console.log(event.type); } catch (e) { try { opera.postError(event.type); } catch (e) { } }
            },
            /* Local Storage
            ================================================================================ */
            setLocalStorage: function(key, value) {
                if (!localStorage) { return; }
                try {
                    localStorage[key] = value;
                } catch (e) {
                    if (e.code == 22 || e.code == 1014) { // Webkit == 22 / Firefox == 1014
                        this.warning(VideoJS.warnings.localStorageFull);
                    }
                }
            },
            /* Helpers
            ================================================================================ */
            getPreloadAttribute: function() {
                if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("preload")) {
                    var preload = this.video.getAttribute("preload");
                    // Only included the attribute, thinking it was boolean
                    if (preload === "" || preload === "true") { return "auto"; }
                    if (preload === "false") { return "none"; }
                    return preload;
                }
            },
            getAutoplayAttribute: function() {
                if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("autoplay")) {
                    var autoplay = this.video.getAttribute("autoplay");
                    if (autoplay === "false") { return false; }
                    return true;
                }
            },
            // Calculates amoutn of buffer is full
            bufferedPercent: function() { return (this.duration()) ? this.buffered()[1] / this.duration() : 0; },
            // Each that maintains player as context
            // Break if true is returned
            each: function(arr, fn) {
                if (!arr || arr.length === 0) { return; }
                for (var i = 0, j = arr.length; i < j; i++) {
                    if (fn.call(this, arr[i], i)) { break; }
                }
            },
            extend: function(obj) {
                for (var attrname in obj) {
                    if (obj.hasOwnProperty(attrname)) { this[attrname] = obj[attrname]; }
                }
            }
        });
        VideoJS.player = VideoJS.prototype;

        ////////////////////////////////////////////////////////////////////////////////
        // Player Types
        ////////////////////////////////////////////////////////////////////////////////

        /* Flash Object Fallback (Player Type)
        ================================================================================ */
        VideoJS.player.extend({
            flashSupported: function() {
                if (!this.flashElement) { this.flashElement = this.getFlashElement(); }
                // Check if object exists & Flash Player version is supported
                if (this.flashElement && this.flashPlayerVersionSupported()) {
                    return true;
                } else {
                    return false;
                }
            },
            flashInit: function() {
                this.replaceWithFlash();
                this.element = this.flashElement;
                this.video.src = ""; // Stop video from downloading if HTML5 is still supported
                var flashPlayerType = VideoJS.flashPlayers[this.options.flashPlayer];
                this.extend(VideoJS.flashPlayers[this.options.flashPlayer].api);
                (flashPlayerType.init.context(this))();
            },
            // Get Flash Fallback object element from Embed Code
            getFlashElement: function() {
                var children = this.video.children;
                for (var i = 0, j = children.length; i < j; i++) {
                    if (children[i].className == "vjs-flash-fallback") {
                        return children[i];
                    }
                }
            },
            // Used to force a browser to fall back when it's an HTML5 browser but there's no supported sources
            replaceWithFlash: function() {
                // this.flashElement = this.video.removeChild(this.flashElement);
                if (this.flashElement) {
                    this.box.insertBefore(this.flashElement, this.video);
                    this.video.style.display = "none"; // Removing it was breaking later players
                }
            },
            // Check if browser can use this flash player
            flashPlayerVersionSupported: function() {
                var playerVersion = (this.options.flashPlayerVersion) ? this.options.flashPlayerVersion : VideoJS.flashPlayers[this.options.flashPlayer].flashPlayerVersion;
                return VideoJS.getFlashVersion() >= playerVersion;
            }
        });
        VideoJS.flashPlayers = {};
        VideoJS.flashPlayers.htmlObject = {
            flashPlayerVersion: 9,
            init: function() { return true; },
            api: { // No video API available with HTML Object embed method
                width: function(width) {
                    if (width !== undefined) {
                        this.element.width = width;
                        this.box.style.width = width + "px";
                        this.triggerResizeListeners();
                        return this;
                    }
                    return this.element.width;
                },
                height: function(height) {
                    if (height !== undefined) {
                        this.element.height = height;
                        this.box.style.height = height + "px";
                        this.triggerResizeListeners();
                        return this;
                    }
                    return this.element.height;
                }
            }
        };


        /* Download Links Fallback (Player Type)
        ================================================================================ */
        VideoJS.player.extend({
            linksSupported: function() { return true; },
            linksInit: function() {
                this.showLinksFallback();
                this.element = this.video;
            },
            // Get the download links block element
            getLinksFallback: function() { return this.box.getElementsByTagName("P")[0]; },
            // Hide no-video download paragraph
            hideLinksFallback: function() {
                if (this.linksFallback) { this.linksFallback.style.display = "none"; }
            },
            // Hide no-video download paragraph
            showLinksFallback: function() {
                if (this.linksFallback) { this.linksFallback.style.display = "block"; }
            }
        });

        ////////////////////////////////////////////////////////////////////////////////
        // Class Methods
        // Functions that don't apply to individual videos.
        ////////////////////////////////////////////////////////////////////////////////

        // Combine Objects - Use "safe" to protect from overwriting existing items
        VideoJS.merge = function(obj1, obj2, safe) {
            for (var attrname in obj2) {
                if (obj2.hasOwnProperty(attrname) && (!safe || !obj1.hasOwnProperty(attrname))) { obj1[attrname] = obj2[attrname]; }
            }
            return obj1;
        };
        VideoJS.extend = function(obj) { this.merge(this, obj, true); };

        VideoJS.extend({
            // Add VideoJS to all video tags with the video-js class when the DOM is ready
            setupAllWhenReady: function(options) {
                // Options is stored globally, and added ot any new player on init
                VideoJS.options = options;
                VideoJS.DOMReady(VideoJS.setup);
            },

            // Run the supplied function when the DOM is ready
            DOMReady: function(fn) {
                VideoJS.addToDOMReady(fn);
            },

            // Set up a specific video or array of video elements
            // "video" can be:
            //    false, undefined, or "All": set up all videos with the video-js class
            //    A video tag ID or video tag element: set up one video and return one player
            //    An array of video tag elements/IDs: set up each and return an array of players
            setup: function(videos, options) {
                var returnSingular = false,
    playerList = [],
    videoElement;

                // If videos is undefined or "All", set up all videos with the video-js class
                if (!videos || videos == "All") {
                    videos = VideoJS.getVideoJSTags();
                    // If videos is not an array, add to an array
                } else if (typeof videos != 'object' || videos.nodeType == 1) {
                    videos = [videos];
                    returnSingular = true;
                }

                // Loop through videos and create players for them
                for (var i = 0; i < videos.length; i++) {
                    if (typeof videos[i] == 'string') {
                        videoElement = document.getElementById(videos[i]);
                    } else { // assume DOM object
                        videoElement = videos[i];
                    }
                    playerList.push(new VideoJS(videoElement, options));
                }

                // Return one or all depending on what was passed in
                return (returnSingular) ? playerList[0] : playerList;
            },

            // Find video tags with the video-js class
            getVideoJSTags: function() {
                var videoTags = document.getElementsByTagName("video"),
    videoJSTags = [], videoTag;

                for (var i = 0, j = videoTags.length; i < j; i++) {
                    videoTag = videoTags[i];
                    if (videoTag.className.indexOf("video-js") != -1) {
                        videoJSTags.push(videoTag);
                    }
                }
                return videoJSTags;
            },

            // Check if the browser supports video.
            browserSupportsVideo: function() {
                if (typeof VideoJS.videoSupport != "undefined") { return VideoJS.videoSupport; }
                VideoJS.videoSupport = !!document.createElement('video').canPlayType;
                return VideoJS.videoSupport;
            },

            getFlashVersion: function() {
                // Cache Version
                if (typeof VideoJS.flashVersion != "undefined") { return VideoJS.flashVersion; }
                var version = 0, desc;
                if (typeof navigator.plugins != "undefined" && typeof navigator.plugins["Shockwave Flash"] == "object") {
                    desc = navigator.plugins["Shockwave Flash"].description;
                    if (desc && !(typeof navigator.mimeTypes != "undefined" && navigator.mimeTypes["application/x-shockwave-flash"] && !navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)) {
                        version = parseInt(desc.match(/^.*\s+([^\s]+)\.[^\s]+\s+[^\s]+$/)[1], 10);
                    }
                } else if (typeof window.ActiveXObject != "undefined") {
                    try {
                        var testObject = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                        if (testObject) {
                            version = parseInt(testObject.GetVariable("$version").match(/^[^\s]+\s(\d+)/)[1], 10);
                        }
                    }
                    catch (e) { }
                }
                VideoJS.flashVersion = version;
                return VideoJS.flashVersion;
            },

            // Browser & Device Checks
            isIE: function() { return ! +"\v1"; },
            isIPad: function() { return navigator.userAgent.match(/iPad/i) !== null; },
            isIPhone: function() { return navigator.userAgent.match(/iPhone/i) !== null; },
            isIOS: function() { return VideoJS.isIPhone() || VideoJS.isIPad(); },
            iOSVersion: function() {
                var match = navigator.userAgent.match(/OS (\d+)_/i);
                if (match && match[1]) { return match[1]; }
            },
            isAndroid: function() { return navigator.userAgent.match(/Android/i) !== null; },
            androidVersion: function() {
                var match = navigator.userAgent.match(/Android (\d+)\./i);
                if (match && match[1]) { return match[1]; }
            },

            warnings: {
                // Safari errors if you call functions on a video that hasn't loaded yet
                videoNotReady: "Video is not ready yet (try playing the video first).",
                // Getting a QUOTA_EXCEEDED_ERR when setting local storage occasionally
                localStorageFull: "Local Storage is Full"
            }
        });

        // Shim to make Video tag valid in IE
        if (VideoJS.isIE()) { document.createElement("video"); }

        // Expose to global
        window.VideoJS = window._V_ = VideoJS;

        /* HTML5 Player Type
        ================================================================================ */
        VideoJS.player.extend({
            html5Supported: function() {
                if (VideoJS.browserSupportsVideo() && this.canPlaySource()) {
                    return true;
                } else {
                    return false;
                }
            },
            html5Init: function() {
                this.element = this.video;

                this.fixPreloading(); // Support old browsers that used autobuffer
                this.supportProgressEvents(); // Support browsers that don't use 'buffered'

                // Set to stored volume OR 85%
                this.volume((localStorage && localStorage.volume) || this.options.defaultVolume);

                // Update interface for device needs
                if (VideoJS.isIOS()) {
                    this.options.useBuiltInControls = true;
                    this.iOSInterface();
                } else if (VideoJS.isAndroid()) {
                    this.options.useBuiltInControls = true;
                    this.androidInterface();
                }

                // Add VideoJS Controls
                if (!this.options.useBuiltInControls) {
                    this.video.controls = false;

                    if (this.options.controlsBelow) { _V_.addClass(this.box, "vjs-controls-below"); }

                    // Make a click on th video act as a play button
                    this.activateElement(this.video, "playToggle");

                    // Build Interface
                    this.buildStylesCheckDiv(); // Used to check if style are loaded
                    this.buildAndActivatePoster();
                    this.buildBigPlayButton();
                    this.buildAndActivateSpinner();
                    this.buildAndActivateControlBar();
                    this.loadInterface(); // Show everything once styles are loaded
                    this.getSubtitles();
                }
            },
            /* Source Managemet
            ================================================================================ */
            canPlaySource: function() {
                // Cache Result
                if (this.canPlaySourceResult) { return this.canPlaySourceResult; }
                // Loop through sources and check if any can play
                var children = this.video.children;
                for (var i = 0, j = children.length; i < j; i++) {
                    if (children[i].tagName.toUpperCase() == "SOURCE") {
                        var canPlay = this.video.canPlayType(children[i].type) || this.canPlayExt(children[i].src);
                        if (canPlay == "probably" || canPlay == "maybe") {
                            this.firstPlayableSource = children[i];
                            this.canPlaySourceResult = true;
                            return true;
                        }
                    }
                }
                this.canPlaySourceResult = false;
                return false;
            },
            // Check if the extention is compatible, for when type won't work
            canPlayExt: function(src) {
                if (!src) { return ""; }
                var match = src.match(/\.([^\.]+)$/);
                if (match && match[1]) {
                    var ext = match[1].toLowerCase();
                    // Android canPlayType doesn't work
                    if (VideoJS.isAndroid()) {
                        if (ext == "mp4" || ext == "m4v") { return "maybe"; }
                        // Allow Apple HTTP Streaming for iOS
                    } else if (VideoJS.isIOS()) {
                        if (ext == "m3u8") { return "maybe"; }
                    }
                }
                return "";
            },
            // Force the video source - Helps fix loading bugs in a handful of devices, like the iPad/iPhone poster bug
            // And iPad/iPhone javascript include location bug. And Android type attribute bug
            forceTheSource: function() {
                this.video.src = this.firstPlayableSource.src; // From canPlaySource()
                this.video.load();
            },
            /* Device Fixes
            ================================================================================ */
            // Support older browsers that used "autobuffer"
            fixPreloading: function() {
                if (typeof this.video.hasAttribute == "function" && this.video.hasAttribute("preload") && this.video.preload != "none") {
                    this.video.autobuffer = true; // Was a boolean
                } else {
                    this.video.autobuffer = false;
                    this.video.preload = "none";
                }
            },

            // Listen for Video Load Progress (currently does not if html file is local)
            // Buffered does't work in all browsers, so watching progress as well
            supportProgressEvents: function(e) {
                _V_.addListener(this.video, 'progress', this.playerOnVideoProgress.context(this));
            },
            playerOnVideoProgress: function(event) {
                this.setBufferedFromProgress(event);
            },
            setBufferedFromProgress: function(event) { // HTML5 Only
                if (event.total > 0) {
                    var newBufferEnd = (event.loaded / event.total) * this.duration();
                    if (newBufferEnd > this.values.bufferEnd) { this.values.bufferEnd = newBufferEnd; }
                }
            },

            iOSInterface: function() {
                if (VideoJS.iOSVersion() < 4) { this.forceTheSource(); } // Fix loading issues
                if (VideoJS.isIPad()) { // iPad could work with controlsBelow
                    this.buildAndActivateSpinner(); // Spinner still works well on iPad, since iPad doesn't have one
                }
            },

            // Fix android specific quirks
            // Use built-in controls, but add the big play button, since android doesn't have one.
            androidInterface: function() {
                this.forceTheSource(); // Fix loading issues
                _V_.addListener(this.video, "click", function() { this.play(); }); // Required to play
                this.buildBigPlayButton(); // But don't activate the normal way. Pause doesn't work right on android.
                _V_.addListener(this.bigPlayButton, "click", function() { this.play(); } .context(this));
                this.positionBox();
                this.showBigPlayButtons();
            },
            /* Wait for styles (TODO: move to _V_)
            ================================================================================ */
            loadInterface: function() {
                if (!this.stylesHaveLoaded()) {
                    // Don't want to create an endless loop either.
                    if (!this.positionRetries) { this.positionRetries = 1; }
                    if (this.positionRetries++ < 100) {
                        setTimeout(this.loadInterface.context(this), 10);
                        return;
                    }
                }
                this.hideStylesCheckDiv();
                this.showPoster();
                if (this.video.paused !== false) { this.showBigPlayButtons(); }
                if (this.options.controlsAtStart) { this.showControlBars(); }
                this.positionAll();
            },
            /* Control Bar
            ================================================================================ */
            buildAndActivateControlBar: function() {
                /* Creating this HTML
                <div class="vjs-controls">
                <div class="vjs-play-control">
                <span></span>
                </div>
                <div class="vjs-progress-control">
                <div class="vjs-progress-holder">
                <div class="vjs-load-progress"></div>
                <div class="vjs-play-progress"></div>
                </div>
                </div>
                <div class="vjs-time-control">
                <span class="vjs-current-time-display">00:00</span><span> / </span><span class="vjs-duration-display">00:00</span>
                </div>
                <div class="vjs-volume-control">
                <div>
                <span></span><span></span><span></span><span></span><span></span><span></span>
                </div>
                </div>
                <div class="vjs-fullscreen-control">
                <div>
                <span></span><span></span><span></span><span></span>
                </div>
                </div>
                </div>
                */

                // Create a div to hold the different controls
                this.controls = _V_.createElement("div", { className: "vjs-controls" });
                // Add the controls to the video's container
                this.box.appendChild(this.controls);
                this.activateElement(this.controls, "controlBar");
                this.activateElement(this.controls, "mouseOverVideoReporter");

                // Build the play control
                this.playControl = _V_.createElement("div", { className: "vjs-play-control", innerHTML: "<span></span>" });
                this.controls.appendChild(this.playControl);
                this.activateElement(this.playControl, "playToggle");

                // Build the progress control
                this.progressControl = _V_.createElement("div", { className: "vjs-progress-control" });
                this.controls.appendChild(this.progressControl);

                // Create a holder for the progress bars
                this.progressHolder = _V_.createElement("div", { className: "vjs-progress-holder" });
                this.progressControl.appendChild(this.progressHolder);
                this.activateElement(this.progressHolder, "currentTimeScrubber");

                // Create the loading progress display
                this.loadProgressBar = _V_.createElement("div", { className: "vjs-load-progress" });
                this.progressHolder.appendChild(this.loadProgressBar);
                this.activateElement(this.loadProgressBar, "loadProgressBar");

                // Create the playing progress display
                this.playProgressBar = _V_.createElement("div", { className: "vjs-play-progress" });
                this.progressHolder.appendChild(this.playProgressBar);
                this.activateElement(this.playProgressBar, "playProgressBar");

                // Create the progress time display (00:00 / 00:00)
                this.timeControl = _V_.createElement("div", { className: "vjs-time-control" });
                this.controls.appendChild(this.timeControl);

                // Create the current play time display
                this.currentTimeDisplay = _V_.createElement("span", { className: "vjs-current-time-display", innerHTML: "00:00" });
                this.timeControl.appendChild(this.currentTimeDisplay);
                this.activateElement(this.currentTimeDisplay, "currentTimeDisplay");

                // Add time separator
                this.timeSeparator = _V_.createElement("span", { innerHTML: " / " });
                this.timeControl.appendChild(this.timeSeparator);

                // Create the total duration display
                this.durationDisplay = _V_.createElement("span", { className: "vjs-duration-display", innerHTML: "00:00" });
                this.timeControl.appendChild(this.durationDisplay);
                this.activateElement(this.durationDisplay, "durationDisplay");

                // Create the volumne control
                this.volumeControl = _V_.createElement("div", {
                    className: "vjs-volume-control",
                    innerHTML: "<div><span></span><span></span><span></span><span></span><span></span><span></span></div>"
                });
                this.controls.appendChild(this.volumeControl);
                this.activateElement(this.volumeControl, "volumeScrubber");

                this.volumeDisplay = this.volumeControl.children[0];
                this.activateElement(this.volumeDisplay, "volumeDisplay");

                // Crete the fullscreen control
                this.fullscreenControl = _V_.createElement("div", {
                    className: "vjs-fullscreen-control",
                    innerHTML: "<div><span></span><span></span><span></span><span></span></div>"
                });
                this.controls.appendChild(this.fullscreenControl);
                this.activateElement(this.fullscreenControl, "fullscreenToggle");
            },
            /* Poster Image
            ================================================================================ */
            buildAndActivatePoster: function() {
                this.updatePosterSource();
                if (this.video.poster) {
                    this.poster = document.createElement("img");
                    // Add poster to video box
                    this.box.appendChild(this.poster);

                    // Add poster image data
                    this.poster.src = this.video.poster;
                    // Add poster styles
                    this.poster.className = "vjs-poster";
                    this.activateElement(this.poster, "poster");
                } else {
                    this.poster = false;
                }
            },
            /* Big Play Button
            ================================================================================ */
            buildBigPlayButton: function() {
                /* Creating this HTML
                <div class="vjs-big-play-button"><span></span></div>
                */
                this.bigPlayButton = _V_.createElement("div", {
                    className: "vjs-big-play-button",
                    innerHTML: "<span></span>"
                });
                this.box.appendChild(this.bigPlayButton);
                this.activateElement(this.bigPlayButton, "bigPlayButton");
            },
            /* Spinner (Loading)
            ================================================================================ */
            buildAndActivateSpinner: function() {
                this.spinner = _V_.createElement("div", {
                    className: "vjs-spinner",
                    innerHTML: "<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>"
                });
                this.box.appendChild(this.spinner);
                this.activateElement(this.spinner, "spinner");
            },
            /* Styles Check - Check if styles are loaded (move ot _V_)
            ================================================================================ */
            // Sometimes the CSS styles haven't been applied to the controls yet
            // when we're trying to calculate the height and position them correctly.
            // This causes a flicker where the controls are out of place.
            buildStylesCheckDiv: function() {
                this.stylesCheckDiv = _V_.createElement("div", { className: "vjs-styles-check" });
                this.stylesCheckDiv.style.position = "absolute";
                this.box.appendChild(this.stylesCheckDiv);
            },
            hideStylesCheckDiv: function() { this.stylesCheckDiv.style.display = "none"; },
            stylesHaveLoaded: function() {
                if (this.stylesCheckDiv.offsetHeight != 5) {
                    return false;
                } else {
                    return true;
                }
            },
            /* VideoJS Box - Holds all elements
            ================================================================================ */
            positionAll: function() {
                this.positionBox();
                this.positionControlBars();
                this.positionPoster();
            },
            positionBox: function() {
                // Set width based on fullscreen or not.
                if (this.videoIsFullScreen) {
                    this.box.style.width = "";
                    this.element.style.height = "";
                    if (this.options.controlsBelow) {
                        this.box.style.height = "";
                        this.element.style.height = (this.box.offsetHeight - this.controls.offsetHeight) + "px";
                    }
                } else {
                    this.box.style.width = this.width() + "px";
                    this.element.style.height = this.height() + "px";
                    if (this.options.controlsBelow) {
                        this.element.style.height = "";
                        // this.box.style.height = this.video.offsetHeight + this.controls.offsetHeight + "px";
                    }
                }
            },
            /* Subtitles
            ================================================================================ */
            getSubtitles: function() {
                var tracks = this.video.getElementsByTagName("TRACK");
                for (var i = 0, j = tracks.length; i < j; i++) {
                    if (tracks[i].getAttribute("kind") == "subtitles" && tracks[i].getAttribute("src")) {
                        this.subtitlesSource = tracks[i].getAttribute("src");
                        this.loadSubtitles();
                        this.buildSubtitles();
                    }
                }
            },
            loadSubtitles: function() { _V_.get(this.subtitlesSource, this.parseSubtitles.context(this)); },
            parseSubtitles: function(subText) {
                var lines = subText.split("\n"),
        line = "",
        subtitle, time, text;
                this.subtitles = [];
                this.currentSubtitle = false;
                this.lastSubtitleIndex = 0;

                for (var i = 0; i < lines.length; i++) {
                    line = _V_.trim(lines[i]); // Trim whitespace and linebreaks
                    if (line) { // Loop until a line with content

                        // First line - Number
                        subtitle = {
                            id: line, // Subtitle Number
                            index: this.subtitles.length // Position in Array
                        };

                        // Second line - Time
                        line = _V_.trim(lines[++i]);
                        time = line.split(" --> ");
                        subtitle.start = this.parseSubtitleTime(time[0]);
                        subtitle.end = this.parseSubtitleTime(time[1]);

                        // Additional lines - Subtitle Text
                        text = [];
                        for (var j = i; j < lines.length; j++) { // Loop until a blank line or end of lines
                            line = _V_.trim(lines[++i]);
                            if (!line) { break; }
                            text.push(line);
                        }
                        subtitle.text = text.join('<br/>');

                        // Add this subtitle
                        this.subtitles.push(subtitle);
                    }
                }
            },

            parseSubtitleTime: function(timeText) {
                var parts = timeText.split(':'),
        time = 0;
                // hours => seconds
                time += parseFloat(parts[0]) * 60 * 60;
                // minutes => seconds
                time += parseFloat(parts[1]) * 60;
                // get seconds
                var seconds = parts[2].split(/\.|,/); // Either . or ,
                time += parseFloat(seconds[0]);
                // add miliseconds
                ms = parseFloat(seconds[1]);
                if (ms) { time += ms / 1000; }
                return time;
            },

            buildSubtitles: function() {
                /* Creating this HTML
                <div class="vjs-subtitles"></div>
                */
                this.subtitlesDisplay = _V_.createElement("div", { className: 'vjs-subtitles' });
                this.box.appendChild(this.subtitlesDisplay);
                this.activateElement(this.subtitlesDisplay, "subtitlesDisplay");
            },

            /* Player API - Translate functionality from player to video
            ================================================================================ */
            addVideoListener: function(type, fn) { _V_.addListener(this.video, type, fn.rEvtContext(this)); },

            play: function() {
                this.video.play();
                return this;
            },
            onPlay: function(fn) { this.addVideoListener("play", fn); return this; },

            pause: function() {
                this.video.pause();
                return this;
            },
            onPause: function(fn) { this.addVideoListener("pause", fn); return this; },
            paused: function() { return this.video.paused; },

            currentTime: function(seconds) {
                if (seconds !== undefined) {
                    try { this.video.currentTime = seconds; }
                    catch (e) { this.warning(VideoJS.warnings.videoNotReady); }
                    this.values.currentTime = seconds;
                    return this;
                }
                return this.video.currentTime;
            },
            onCurrentTimeUpdate: function(fn) {
                this.currentTimeListeners.push(fn);
            },

            duration: function() {
                return this.video.duration;
            },

            buffered: function() {
                // Storing values allows them be overridden by setBufferedFromProgress
                if (this.values.bufferStart === undefined) {
                    this.values.bufferStart = 0;
                    this.values.bufferEnd = 0;
                }
                if (this.video.buffered && this.video.buffered.length > 0) {
                    var newEnd = this.video.buffered.end(0);
                    if (newEnd > this.values.bufferEnd) { this.values.bufferEnd = newEnd; }
                }
                return [this.values.bufferStart, this.values.bufferEnd];
            },

            volume: function(percentAsDecimal) {
                if (percentAsDecimal !== undefined) {
                    // Force value to between 0 and 1
                    this.values.volume = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
                    this.video.volume = this.values.volume;
                    this.setLocalStorage("volume", this.values.volume);
                    return this;
                }
                if (this.values.volume) { return this.values.volume; }
                return this.video.volume;
            },
            onVolumeChange: function(fn) { _V_.addListener(this.video, 'volumechange', fn.rEvtContext(this)); },

            width: function(width) {
                if (width !== undefined) {
                    this.video.width = width; // Not using style so it can be overridden on fullscreen.
                    this.box.style.width = width + "px";
                    this.triggerResizeListeners();
                    return this;
                }
                return this.video.offsetWidth;
            },
            height: function(height) {
                if (height !== undefined) {
                    this.video.height = height;
                    this.box.style.height = height + "px";
                    this.triggerResizeListeners();
                    return this;
                }
                return this.video.offsetHeight;
            },

            supportsFullScreen: function() {
                if (typeof this.video.webkitEnterFullScreen == 'function') {
                    // Seems to be broken in Chromium/Chrome
                    if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) {
                        return true;
                    }
                }
                return false;
            },

            html5EnterNativeFullScreen: function() {
                try {
                    this.video.webkitEnterFullScreen();
                } catch (e) {
                    if (e.code == 11) { this.warning(VideoJS.warnings.videoNotReady); }
                }
                return this;
            },

            // Turn on fullscreen (window) mode
            // Real fullscreen isn't available in browsers quite yet.
            enterFullScreen: function() {
                if (this.supportsFullScreen()) {
                    this.html5EnterNativeFullScreen();
                } else {
                    this.enterFullWindow();
                }
            },

            exitFullScreen: function() {
                if (this.supportsFullScreen()) {
                    // Shouldn't be called
                } else {
                    this.exitFullWindow();
                }
            },

            enterFullWindow: function() {
                this.videoIsFullScreen = true;
                // Storing original doc overflow value to return to when fullscreen is off
                this.docOrigOverflow = document.documentElement.style.overflow;
                // Add listener for esc key to exit fullscreen
                _V_.addListener(document, "keydown", this.fullscreenOnEscKey.rEvtContext(this));
                // Add listener for a window resize
                _V_.addListener(window, "resize", this.fullscreenOnWindowResize.rEvtContext(this));
                // Hide any scroll bars
                document.documentElement.style.overflow = 'hidden';
                // Apply fullscreen styles
                _V_.addClass(this.box, "vjs-fullscreen");
                // Resize the box, controller, and poster
                this.positionAll();
            },

            // Turn off fullscreen (window) mode
            exitFullWindow: function() {
                this.videoIsFullScreen = false;
                document.removeEventListener("keydown", this.fullscreenOnEscKey, false);
                window.removeEventListener("resize", this.fullscreenOnWindowResize, false);
                // Unhide scroll bars.
                document.documentElement.style.overflow = this.docOrigOverflow;
                // Remove fullscreen styles
                _V_.removeClass(this.box, "vjs-fullscreen");
                // Resize the box, controller, and poster to original sizes
                this.positionAll();
            },

            onError: function(fn) { this.addVideoListener("error", fn); return this; },
            onEnded: function(fn) {
                this.addVideoListener("ended", fn); return this;
            }
        });

        ////////////////////////////////////////////////////////////////////////////////
        // Element Behaviors
        // Tell elements how to act or react
        ////////////////////////////////////////////////////////////////////////////////

        /* Player Behaviors - How VideoJS reacts to what the video is doing.
        ================================================================================ */
        VideoJS.player.newBehavior("player", function(player) {
            this.onError(this.playerOnVideoError);
            // Listen for when the video is played
            this.onPlay(this.playerOnVideoPlay);
            this.onPlay(this.trackCurrentTime);
            // Listen for when the video is paused
            this.onPause(this.playerOnVideoPause);
            this.onPause(this.stopTrackingCurrentTime);
            // Listen for when the video ends
            this.onEnded(this.playerOnVideoEnded);
            // Set interval for load progress using buffer watching method
            // this.trackCurrentTime();
            this.trackBuffered();
            // Buffer Full
            this.onBufferedUpdate(this.isBufferFull);
        }, {
            playerOnVideoError: function(event) {
                this.log(event);
                this.log(this.video.error);
            },
            playerOnVideoPlay: function(event) { this.hasPlayed = true; },
            playerOnVideoPause: function(event) { },
            playerOnVideoEnded: function(event) {
                this.currentTime(0);
                this.pause();
            },

            /* Load Tracking -------------------------------------------------------------- */
            // Buffer watching method for load progress.
            // Used for browsers that don't support the progress event
            trackBuffered: function() {
                this.bufferedInterval = setInterval(this.triggerBufferedListeners.context(this), 500);
            },
            stopTrackingBuffered: function() { clearInterval(this.bufferedInterval); },
            bufferedListeners: [],
            onBufferedUpdate: function(fn) {
                this.bufferedListeners.push(fn);
            },
            triggerBufferedListeners: function() {
                this.isBufferFull();
                this.each(this.bufferedListeners, function(listener) {
                    (listener.context(this))();
                });
            },
            isBufferFull: function() {
                if (this.bufferedPercent() == 1) { this.stopTrackingBuffered(); }
            },

            /* Time Tracking -------------------------------------------------------------- */
            trackCurrentTime: function() {
                if (this.currentTimeInterval) { clearInterval(this.currentTimeInterval); }
                this.currentTimeInterval = setInterval(this.triggerCurrentTimeListeners.context(this), 100); // 42 = 24 fps
                this.trackingCurrentTime = true;
            },
            // Turn off play progress tracking (when paused or dragging)
            stopTrackingCurrentTime: function() {
                clearInterval(this.currentTimeInterval);
                this.trackingCurrentTime = false;
            },
            currentTimeListeners: [],
            // onCurrentTimeUpdate is in API section now
            triggerCurrentTimeListeners: function(late, newTime) { // FF passes milliseconds late as the first argument
                this.each(this.currentTimeListeners, function(listener) {
                    (listener.context(this))(newTime || this.currentTime());
                });
            },

            /* Resize Tracking -------------------------------------------------------------- */
            resizeListeners: [],
            onResize: function(fn) {
                this.resizeListeners.push(fn);
            },
            // Trigger anywhere the video/box size is changed.
            triggerResizeListeners: function() {
                this.each(this.resizeListeners, function(listener) {
                    (listener.context(this))();
                });
            }
        }
);
        /* Mouse Over Video Reporter Behaviors - i.e. Controls hiding based on mouse location
        ================================================================================ */
        VideoJS.player.newBehavior("mouseOverVideoReporter", function(element) {
            // Listen for the mouse move the video. Used to reveal the controller.
            _V_.addListener(element, "mousemove", this.mouseOverVideoReporterOnMouseMove.context(this));
            // Listen for the mouse moving out of the video. Used to hide the controller.
            _V_.addListener(element, "mouseout", this.mouseOverVideoReporterOnMouseOut.context(this));
        }, {
            mouseOverVideoReporterOnMouseMove: function() {
                this.showControlBars();
                clearInterval(this.mouseMoveTimeout);
                this.mouseMoveTimeout = setTimeout(this.hideControlBars.context(this), 4000);
            },
            mouseOverVideoReporterOnMouseOut: function(event) {
                // Prevent flicker by making sure mouse hasn't left the video
                var parent = event.relatedTarget;
                while (parent && parent !== this.box) {
                    parent = parent.parentNode;
                }
                if (parent !== this.box) {
                    this.hideControlBars();
                }
            }
        }
);
        /* Mouse Over Video Reporter Behaviors - i.e. Controls hiding based on mouse location
        ================================================================================ */
        VideoJS.player.newBehavior("box", function(element) {
            this.positionBox();
            _V_.addClass(element, "vjs-paused");
            this.activateElement(element, "mouseOverVideoReporter");
            this.onPlay(this.boxOnVideoPlay);
            this.onPause(this.boxOnVideoPause);
        }, {
            boxOnVideoPlay: function() {
                _V_.removeClass(this.box, "vjs-paused");
                _V_.addClass(this.box, "vjs-playing");
            },
            boxOnVideoPause: function() {
                _V_.removeClass(this.box, "vjs-playing");
                _V_.addClass(this.box, "vjs-paused");
            }
        }
);
        /* Poster Image Overlay
        ================================================================================ */
        VideoJS.player.newBehavior("poster", function(element) {
            this.activateElement(element, "mouseOverVideoReporter");
            this.activateElement(element, "playButton");
            this.onPlay(this.hidePoster);
            this.onEnded(this.showPoster);
            this.onResize(this.positionPoster);
        }, {
            showPoster: function() {
                if (!this.poster) { return; }
                this.poster.style.display = "block";
                this.positionPoster();
            },
            positionPoster: function() {
                // Only if the poster is visible
                if (!this.poster || this.poster.style.display == 'none') { return; }
                this.poster.style.height = this.height() + "px"; // Need incase controlsBelow
                this.poster.style.width = this.width() + "px"; // Could probably do 100% of box
            },
            hidePoster: function() {
                if (!this.poster) { return; }
                this.poster.style.display = "none";
            },
            // Update poster source from attribute or fallback image
            // iPad breaks if you include a poster attribute, so this fixes that
            updatePosterSource: function() {
                if (!this.video.poster) {
                    var images = this.video.getElementsByTagName("img");
                    if (images.length > 0) { this.video.poster = images[0].src; }
                }
            }
        }
);
        /* Control Bar Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("controlBar", function(element) {
            if (!this.controlBars) {
                this.controlBars = [];
                this.onResize(this.positionControlBars);
            }
            this.controlBars.push(element);
            _V_.addListener(element, "mousemove", this.onControlBarsMouseMove.context(this));
            _V_.addListener(element, "mouseout", this.onControlBarsMouseOut.context(this));
        }, {
            showControlBars: function() {
                if (!this.options.controlsAtStart && !this.hasPlayed) { return; }
                this.each(this.controlBars, function(bar) {
                    bar.style.display = "block";
                });
            },
            // Place controller relative to the video's position (now just resizing bars)
            positionControlBars: function() {
                this.updatePlayProgressBars();
                this.updateLoadProgressBars();
            },
            hideControlBars: function() {
                if (this.options.controlsHiding && !this.mouseIsOverControls) {
                    this.each(this.controlBars, function(bar) {
                        bar.style.display = "none";
                    });
                }
            },
            // Block controls from hiding when mouse is over them.
            onControlBarsMouseMove: function() { this.mouseIsOverControls = true; },
            onControlBarsMouseOut: function(event) {
                this.mouseIsOverControls = false;
            }
        }
);
        /* PlayToggle, PlayButton, PauseButton Behaviors
        ================================================================================ */
        // Play Toggle
        VideoJS.player.newBehavior("playToggle", function(element) {
            if (!this.elements.playToggles) {
                this.elements.playToggles = [];
                this.onPlay(this.playTogglesOnPlay);
                this.onPause(this.playTogglesOnPause);
            }
            this.elements.playToggles.push(element);
            _V_.addListener(element, "click", this.onPlayToggleClick.context(this));
        }, {
            onPlayToggleClick: function(event) {
                if (this.paused()) {
                    this.play();
                } else {
                    this.pause();
                }
            },
            playTogglesOnPlay: function(event) {
                this.each(this.elements.playToggles, function(toggle) {
                    _V_.removeClass(toggle, "vjs-paused");
                    _V_.addClass(toggle, "vjs-playing");
                });
            },
            playTogglesOnPause: function(event) {
                this.each(this.elements.playToggles, function(toggle) {
                    _V_.removeClass(toggle, "vjs-playing");
                    _V_.addClass(toggle, "vjs-paused");
                });
            }
        }
);
        // Play
        VideoJS.player.newBehavior("playButton", function(element) {
            _V_.addListener(element, "click", this.onPlayButtonClick.context(this));
        }, {
            onPlayButtonClick: function(event) { this.play(); }
        }
);
        // Pause
        VideoJS.player.newBehavior("pauseButton", function(element) {
            _V_.addListener(element, "click", this.onPauseButtonClick.context(this));
        }, {
            onPauseButtonClick: function(event) { this.pause(); }
        }
);
        /* Play Progress Bar Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("playProgressBar", function(element) {
            if (!this.playProgressBars) {
                this.playProgressBars = [];
                this.onCurrentTimeUpdate(this.updatePlayProgressBars);
            }
            this.playProgressBars.push(element);
        }, {
            // Ajust the play progress bar's width based on the current play time
            updatePlayProgressBars: function(newTime) {
                var progress = (newTime !== undefined) ? newTime / this.duration() : this.currentTime() / this.duration();
                if (isNaN(progress)) { progress = 0; }
                this.each(this.playProgressBars, function(bar) {
                    if (bar.style) { bar.style.width = _V_.round(progress * 100, 2) + "%"; }
                });
            }
        }
);
        /* Load Progress Bar Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("loadProgressBar", function(element) {
            if (!this.loadProgressBars) { this.loadProgressBars = []; }
            this.loadProgressBars.push(element);
            this.onBufferedUpdate(this.updateLoadProgressBars);
        }, {
            updateLoadProgressBars: function() {
                this.each(this.loadProgressBars, function(bar) {
                    if (bar.style) { bar.style.width = _V_.round(this.bufferedPercent() * 100, 2) + "%"; }
                });
            }
        }
);

        /* Current Time Display Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("currentTimeDisplay", function(element) {
            if (!this.currentTimeDisplays) {
                this.currentTimeDisplays = [];
                this.onCurrentTimeUpdate(this.updateCurrentTimeDisplays);
            }
            this.currentTimeDisplays.push(element);
        }, {
            // Update the displayed time (00:00)
            updateCurrentTimeDisplays: function(newTime) {
                if (!this.currentTimeDisplays) { return; }
                // Allows for smooth scrubbing, when player can't keep up.
                var time = (newTime) ? newTime : this.currentTime();
                this.each(this.currentTimeDisplays, function(dis) {
                    dis.innerHTML = _V_.formatTime(time);
                });
            }
        }
);

        /* Duration Display Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("durationDisplay", function(element) {
            if (!this.durationDisplays) {
                this.durationDisplays = [];
                this.onCurrentTimeUpdate(this.updateDurationDisplays);
            }
            this.durationDisplays.push(element);
        }, {
            updateDurationDisplays: function() {
                if (!this.durationDisplays) { return; }
                this.each(this.durationDisplays, function(dis) {
                    if (this.duration()) { dis.innerHTML = _V_.formatTime(this.duration()); }
                });
            }
        }
);

        /* Current Time Scrubber Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("currentTimeScrubber", function(element) {
            _V_.addListener(element, "mousedown", this.onCurrentTimeScrubberMouseDown.rEvtContext(this));
        }, {
            // Adjust the play position when the user drags on the progress bar
            onCurrentTimeScrubberMouseDown: function(event, scrubber) {
                event.preventDefault();
                this.currentScrubber = scrubber;

                this.stopTrackingCurrentTime(); // Allows for smooth scrubbing

                this.videoWasPlaying = !this.paused();
                this.pause();

                _V_.blockTextSelection();
                this.setCurrentTimeWithScrubber(event);
                _V_.addListener(document, "mousemove", this.onCurrentTimeScrubberMouseMove.rEvtContext(this));
                _V_.addListener(document, "mouseup", this.onCurrentTimeScrubberMouseUp.rEvtContext(this));
            },
            onCurrentTimeScrubberMouseMove: function(event) { // Removeable
                this.setCurrentTimeWithScrubber(event);
            },
            onCurrentTimeScrubberMouseUp: function(event) { // Removeable
                _V_.unblockTextSelection();
                document.removeEventListener("mousemove", this.onCurrentTimeScrubberMouseMove, false);
                document.removeEventListener("mouseup", this.onCurrentTimeScrubberMouseUp, false);
                if (this.videoWasPlaying) {
                    this.play();
                    this.trackCurrentTime();
                }
            },
            setCurrentTimeWithScrubber: function(event) {
                var newProgress = _V_.getRelativePosition(event.pageX, this.currentScrubber);
                var newTime = newProgress * this.duration();
                this.triggerCurrentTimeListeners(0, newTime); // Allows for smooth scrubbing
                // Don't let video end while scrubbing.
                if (newTime == this.duration()) { newTime = newTime - 0.1; }
                this.currentTime(newTime);
            }
        }
);
        /* Volume Display Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("volumeDisplay", function(element) {
            if (!this.volumeDisplays) {
                this.volumeDisplays = [];
                this.onVolumeChange(this.updateVolumeDisplays);
            }
            this.volumeDisplays.push(element);
            this.updateVolumeDisplay(element); // Set the display to the initial volume
        }, {
            // Update the volume control display
            // Unique to these default controls. Uses borders to create the look of bars.
            updateVolumeDisplays: function() {
                if (!this.volumeDisplays) { return; }
                this.each(this.volumeDisplays, function(dis) {
                    this.updateVolumeDisplay(dis);
                });
            },
            updateVolumeDisplay: function(display) {
                var volNum = Math.ceil(this.volume() * 6);
                this.each(display.children, function(child, num) {
                    if (num < volNum) {
                        _V_.addClass(child, "vjs-volume-level-on");
                    } else {
                        _V_.removeClass(child, "vjs-volume-level-on");
                    }
                });
            }
        }
);
        /* Volume Scrubber Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("volumeScrubber", function(element) {
            _V_.addListener(element, "mousedown", this.onVolumeScrubberMouseDown.rEvtContext(this));
        }, {
            // Adjust the volume when the user drags on the volume control
            onVolumeScrubberMouseDown: function(event, scrubber) {
                // event.preventDefault();
                _V_.blockTextSelection();
                this.currentScrubber = scrubber;
                this.setVolumeWithScrubber(event);
                _V_.addListener(document, "mousemove", this.onVolumeScrubberMouseMove.rEvtContext(this));
                _V_.addListener(document, "mouseup", this.onVolumeScrubberMouseUp.rEvtContext(this));
            },
            onVolumeScrubberMouseMove: function(event) {
                this.setVolumeWithScrubber(event);
            },
            onVolumeScrubberMouseUp: function(event) {
                this.setVolumeWithScrubber(event);
                _V_.unblockTextSelection();
                document.removeEventListener("mousemove", this.onVolumeScrubberMouseMove, false);
                document.removeEventListener("mouseup", this.onVolumeScrubberMouseUp, false);
            },
            setVolumeWithScrubber: function(event) {
                var newVol = _V_.getRelativePosition(event.pageX, this.currentScrubber);
                this.volume(newVol);
            }
        }
);
        /* Fullscreen Toggle Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("fullscreenToggle", function(element) {
            _V_.addListener(element, "click", this.onFullscreenToggleClick.context(this));
        }, {
            // When the user clicks on the fullscreen button, update fullscreen setting
            onFullscreenToggleClick: function(event) {
                if (!this.videoIsFullScreen) {
                    this.enterFullScreen();
                } else {
                    this.exitFullScreen();
                }
            },

            fullscreenOnWindowResize: function(event) { // Removeable
                this.positionControlBars();
            },
            // Create listener for esc key while in full screen mode
            fullscreenOnEscKey: function(event) { // Removeable
                if (event.keyCode == 27) {
                    this.exitFullScreen();
                }
            }
        }
);
        /* Big Play Button Behaviors
        ================================================================================ */
        VideoJS.player.newBehavior("bigPlayButton", function(element) {
            if (!this.elements.bigPlayButtons) {
                this.elements.bigPlayButtons = [];
                this.onPlay(this.bigPlayButtonsOnPlay);
                this.onEnded(this.bigPlayButtonsOnEnded);
            }
            this.elements.bigPlayButtons.push(element);
            this.activateElement(element, "playButton");
        }, {
            bigPlayButtonsOnPlay: function(event) { this.hideBigPlayButtons(); },
            bigPlayButtonsOnEnded: function(event) { this.showBigPlayButtons(); },
            showBigPlayButtons: function() {
                this.each(this.elements.bigPlayButtons, function(element) {
                    element.style.display = "block";
                });
            },
            hideBigPlayButtons: function() {
                this.each(this.elements.bigPlayButtons, function(element) {
                    element.style.display = "none";
                });
            }
        }
);
        /* Spinner
        ================================================================================ */
        VideoJS.player.newBehavior("spinner", function(element) {
            if (!this.spinners) {
                this.spinners = [];
                _V_.addListener(this.video, "loadeddata", this.spinnersOnVideoLoadedData.context(this));
                _V_.addListener(this.video, "loadstart", this.spinnersOnVideoLoadStart.context(this));
                _V_.addListener(this.video, "seeking", this.spinnersOnVideoSeeking.context(this));
                _V_.addListener(this.video, "seeked", this.spinnersOnVideoSeeked.context(this));
                _V_.addListener(this.video, "canplay", this.spinnersOnVideoCanPlay.context(this));
                _V_.addListener(this.video, "canplaythrough", this.spinnersOnVideoCanPlayThrough.context(this));
                _V_.addListener(this.video, "waiting", this.spinnersOnVideoWaiting.context(this));
                _V_.addListener(this.video, "stalled", this.spinnersOnVideoStalled.context(this));
                _V_.addListener(this.video, "suspend", this.spinnersOnVideoSuspend.context(this));
                _V_.addListener(this.video, "playing", this.spinnersOnVideoPlaying.context(this));
                _V_.addListener(this.video, "timeupdate", this.spinnersOnVideoTimeUpdate.context(this));
            }
            this.spinners.push(element);
        }, {
            showSpinners: function() {
                this.each(this.spinners, function(spinner) {
                    spinner.style.display = "block";
                });
                clearInterval(this.spinnerInterval);
                this.spinnerInterval = setInterval(this.rotateSpinners.context(this), 100);
            },
            hideSpinners: function() {
                this.each(this.spinners, function(spinner) {
                    spinner.style.display = "none";
                });
                clearInterval(this.spinnerInterval);
            },
            spinnersRotated: 0,
            rotateSpinners: function() {
                this.each(this.spinners, function(spinner) {
                    // spinner.style.transform =       'scale(0.5) rotate('+this.spinnersRotated+'deg)';
                    spinner.style.WebkitTransform = 'scale(0.5) rotate(' + this.spinnersRotated + 'deg)';
                    spinner.style.MozTransform = 'scale(0.5) rotate(' + this.spinnersRotated + 'deg)';
                });
                if (this.spinnersRotated == 360) { this.spinnersRotated = 0; }
                this.spinnersRotated += 45;
            },
            spinnersOnVideoLoadedData: function(event) { this.hideSpinners(); },
            spinnersOnVideoLoadStart: function(event) { this.showSpinners(); },
            spinnersOnVideoSeeking: function(event) { /* this.showSpinners(); */ },
            spinnersOnVideoSeeked: function(event) { /* this.hideSpinners(); */ },
            spinnersOnVideoCanPlay: function(event) { /* this.hideSpinners(); */ },
            spinnersOnVideoCanPlayThrough: function(event) { this.hideSpinners(); },
            spinnersOnVideoWaiting: function(event) {
                // Safari sometimes triggers waiting inappropriately
                // Like after video has played, any you play again.
                this.showSpinners();
            },
            spinnersOnVideoStalled: function(event) { },
            spinnersOnVideoSuspend: function(event) { },
            spinnersOnVideoPlaying: function(event) { this.hideSpinners(); },
            spinnersOnVideoTimeUpdate: function(event) {
                // Safari sometimes calls waiting and doesn't recover
                if (this.spinner.style.display == "block") { this.hideSpinners(); }
            }
        }
);
        /* Subtitles
        ================================================================================ */
        VideoJS.player.newBehavior("subtitlesDisplay", function(element) {
            if (!this.subtitleDisplays) {
                this.subtitleDisplays = [];
                this.onCurrentTimeUpdate(this.subtitleDisplaysOnVideoTimeUpdate);
                this.onEnded(function() { this.lastSubtitleIndex = 0; } .context(this));
            }
            this.subtitleDisplays.push(element);
        }, {
            subtitleDisplaysOnVideoTimeUpdate: function(time) {
                // Assuming all subtitles are in order by time, and do not overlap
                if (this.subtitles) {
                    // If current subtitle should stay showing, don't do anything. Otherwise, find new subtitle.
                    if (!this.currentSubtitle || this.currentSubtitle.start >= time || this.currentSubtitle.end < time) {
                        var newSubIndex = false,
                        // Loop in reverse if lastSubtitle is after current time (optimization)
                        // Meaning the user is scrubbing in reverse or rewinding
              reverse = (this.subtitles[this.lastSubtitleIndex].start > time),
                        // If reverse, step back 1 becase we know it's not the lastSubtitle
              i = this.lastSubtitleIndex - (reverse) ? 1 : 0;
                        while (true) { // Loop until broken
                            if (reverse) { // Looping in reverse
                                // Stop if no more, or this subtitle ends before the current time (no earlier subtitles should apply)
                                if (i < 0 || this.subtitles[i].end < time) { break; }
                                // End is greater than time, so if start is less, show this subtitle
                                if (this.subtitles[i].start < time) {
                                    newSubIndex = i;
                                    break;
                                }
                                i--;
                            } else { // Looping forward
                                // Stop if no more, or this subtitle starts after time (no later subtitles should apply)
                                if (i >= this.subtitles.length || this.subtitles[i].start > time) { break; }
                                // Start is less than time, so if end is later, show this subtitle
                                if (this.subtitles[i].end > time) {
                                    newSubIndex = i;
                                    break;
                                }
                                i++;
                            }
                        }

                        // Set or clear current subtitle
                        if (newSubIndex !== false) {
                            this.currentSubtitle = this.subtitles[newSubIndex];
                            this.lastSubtitleIndex = newSubIndex;
                            this.updateSubtitleDisplays(this.currentSubtitle.text);
                        } else if (this.currentSubtitle) {
                            this.currentSubtitle = false;
                            this.updateSubtitleDisplays("");
                        }
                    }
                }
            },
            updateSubtitleDisplays: function(val) {
                this.each(this.subtitleDisplays, function(disp) {
                    disp.innerHTML = val;
                });
            }
        }
);

        ////////////////////////////////////////////////////////////////////////////////
        // Convenience Functions (mini library)
        // Functions not specific to video or VideoJS and could probably be replaced with a library like jQuery
        ////////////////////////////////////////////////////////////////////////////////

        VideoJS.extend({

            addClass: function(element, classToAdd) {
                if ((" " + element.className + " ").indexOf(" " + classToAdd + " ") == -1) {
                    element.className = element.className === "" ? classToAdd : element.className + " " + classToAdd;
                }
            },
            removeClass: function(element, classToRemove) {
                if (element.className.indexOf(classToRemove) == -1) { return; }
                var classNames = element.className.split(/\s+/);
                classNames.splice(classNames.lastIndexOf(classToRemove), 1);
                element.className = classNames.join(" ");
            },
            createElement: function(tagName, attributes) {
                return this.merge(document.createElement(tagName), attributes);
            },

            // Attempt to block the ability to select text while dragging controls
            blockTextSelection: function() {
                document.body.focus();
                document.onselectstart = function() { return false; };
            },
            // Turn off text selection blocking
            unblockTextSelection: function() { document.onselectstart = function() { return true; }; },

            // Return seconds as MM:SS
            formatTime: function(secs) {
                var seconds = Math.round(secs);
                var minutes = Math.floor(seconds / 60);
                minutes = (minutes >= 10) ? minutes : "0" + minutes;
                seconds = Math.floor(seconds % 60);
                seconds = (seconds >= 10) ? seconds : "0" + seconds;
                return minutes + ":" + seconds;
            },

            // Return the relative horizonal position of an event as a value from 0-1
            getRelativePosition: function(x, relativeElement) {
                return Math.max(0, Math.min(1, (x - this.findPosX(relativeElement)) / relativeElement.offsetWidth));
            },
            // Get an objects position on the page
            findPosX: function(obj) {
                var curleft = obj.offsetLeft;
                while (obj = obj.offsetParent) {
                    curleft += obj.offsetLeft;
                }
                return curleft;
            },
            getComputedStyleValue: function(element, style) {
                return window.getComputedStyle(element, null).getPropertyValue(style);
            },

            round: function(num, dec) {
                if (!dec) { dec = 0; }
                return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
            },

            addListener: function(element, type, handler) {
                if (element.addEventListener) {
                    element.addEventListener(type, handler, false);
                } else if (element.attachEvent) {
                    element.attachEvent("on" + type, handler);
                }
            },
            removeListener: function(element, type, handler) {
                if (element.removeEventListener) {
                    element.removeEventListener(type, handler, false);
                } else if (element.attachEvent) {
                    element.detachEvent("on" + type, handler);
                }
            },

            get: function(url, onSuccess) {
                if (typeof XMLHttpRequest == "undefined") {
                    XMLHttpRequest = function() {
                        try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) { }
                        try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (f) { }
                        try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (g) { }
                        //Microsoft.XMLHTTP points to Msxml2.XMLHTTP.3.0 and is redundant
                        throw new Error("This browser does not support XMLHttpRequest.");
                    };
                }
                var request = new XMLHttpRequest();
                request.open("GET", url);
                request.onreadystatechange = function() {
                    if (request.readyState == 4 && request.status == 200) {
                        onSuccess(request.responseText);
                    }
                } .context(this);
                request.send();
            },

            trim: function(string) { return string.toString().replace(/^\s+/, "").replace(/\s+$/, ""); },

            // DOM Ready functionality adapted from jQuery. http://jquery.com/
            bindDOMReady: function() {
                if (document.readyState === "complete") {
                    return VideoJS.onDOMReady();
                }
                if (document.addEventListener) {
                    document.addEventListener("DOMContentLoaded", VideoJS.DOMContentLoaded, false);
                    window.addEventListener("load", VideoJS.onDOMReady, false);
                } else if (document.attachEvent) {
                    document.attachEvent("onreadystatechange", VideoJS.DOMContentLoaded);
                    window.attachEvent("onload", VideoJS.onDOMReady);
                }
            },

            DOMContentLoaded: function() {
                if (document.addEventListener) {
                    document.removeEventListener("DOMContentLoaded", VideoJS.DOMContentLoaded, false);
                    VideoJS.onDOMReady();
                } else if (document.attachEvent) {
                    if (document.readyState === "complete") {
                        document.detachEvent("onreadystatechange", VideoJS.DOMContentLoaded);
                        VideoJS.onDOMReady();
                    }
                }
            },

            // Functions to be run once the DOM is loaded
            DOMReadyList: [],
            addToDOMReady: function(fn) {
                if (VideoJS.DOMIsReady) {
                    fn.call(document);
                } else {
                    VideoJS.DOMReadyList.push(fn);
                }
            },

            DOMIsReady: false,
            onDOMReady: function() {
                if (VideoJS.DOMIsReady) { return; }
                if (!document.body) { return setTimeout(VideoJS.onDOMReady, 13); }
                VideoJS.DOMIsReady = true;
                if (VideoJS.DOMReadyList) {
                    for (var i = 0; i < VideoJS.DOMReadyList.length; i++) {
                        VideoJS.DOMReadyList[i].call(document);
                    }
                    VideoJS.DOMReadyList = null;
                }
            }
        });
        VideoJS.bindDOMReady();

        // Allows for binding context to functions
        // when using in event listeners and timeouts
        Function.prototype.context = function(obj) {
            var method = this,
  temp = function() {
      return method.apply(obj, arguments);
  };
            return temp;
        };

        // Like context, in that it creates a closure
        // But insteaad keep "this" intact, and passes the var as the second argument of the function
        // Need for event listeners where you need to know what called the event
        // Only use with event callbacks
        Function.prototype.evtContext = function(obj) {
            var method = this,
  temp = function() {
      var origContext = this;
      return method.call(obj, arguments[0], origContext);
  };
            return temp;
        };

        // Removeable Event listener with Context
        // Replaces the original function with a version that has context
        // So it can be removed using the original function name.
        // In order to work, a version of the function must already exist in the player/prototype
        Function.prototype.rEvtContext = function(obj, funcParent) {
            if (this.hasContext === true) { return this; }
            if (!funcParent) { funcParent = obj; }
            for (var attrname in funcParent) {
                if (funcParent[attrname] == this) {
                    funcParent[attrname] = this.evtContext(obj);
                    funcParent[attrname].hasContext = true;
                    return funcParent[attrname];
                }
            }
            return this.evtContext(obj);
        };

        // jQuery Plugin
        if (window.jQuery) {
            (function($) {
                $.fn.VideoJS = function(options) {
                    this.each(function() {
                        VideoJS.setup(this, options);
                    });
                    return this;
                };
                $.fn.player = function() {
                    return this[0].player;
                };
            })(jQuery);
        }


        // Expose to global
        window.VideoJS = window._V_ = VideoJS;

        // End self-executing function
    })(window);
}
