(function ($) {

AblePlayer.prototype.initVimeoPlayer = function () {

        var thisObj, deferred, promise, containerId, vimeoId, autoplay, videoDimensions, options;
        thisObj = this;

        deferred = new $.Deferred();
        promise = deferred.promise();

        deferred.resolve();

        containerId = this.mediaId + '_vimeo';

        // add container to which Vimeo player iframe will be appended
        this.$mediaContainer.prepend($('<div>').attr('id', containerId));

        // if a described version is available && user prefers desription
        // init player using the described version
        if (this.vimeoDescId && this.prefDesc) {
                vimeoId = this.vimeoDescId;
        }
        else {
                vimeoId = this.vimeoId;
        }
        this.activeVimeoId = vimeoId;

        // Notes re. Vimeo Embed Options:
        // If a video is owned by a user with a paid Plus, PRO, or Business account,
        // setting the "background" option to "true" will hide the default controls.
        // It has no effect on videos owned by a free basic account owner (their controls cannot be hidden).
        // Also, setting "background" to "true" has a couple of side effects:
        // In addition to hiding the controls, it also autoplays and loops the video.
        // If the player is initialized with options to set both "autoplay" and "loop" to "false",
        // this does not override the "background" setting.
        // Passing this.autoplay and this.loop anyway, just in case it works someday
        // Meanwhile, workaround is to setup an event listener to immediately pause after video autoplays

        if (this.autoplay && this.okToPlay) {
                autoplay = 'true';
        }
        else {
                autoplay = 'false';
        }

        videoDimensions = this.getVimeoDimensions(this.activeVimeoId, containerId);
        if (videoDimensions) {
                this.vimeoWidth = videoDimensions[0];
                this.vimeoHeight = videoDimensions[1];
                this.aspectRatio = thisObj.ytWidth / thisObj.ytHeight;
        }
        else {
                // dimensions are initially unknown
                // sending null values to Vimeo results in a video that uses the default Vimeo dimensions
                // these can then be scraped from the iframe and applied to this.$ableWrapper
                this.vimeoWidth = null;
                this.vimeoHeight = null;
        }
        options = {
                        id: vimeoId,
                        width: this.vimeoWidth,
                        background: true,
                        autoplay: this.autoplay,
                        loop: this.loop
        };

        this.vimeoPlayer = new Vimeo.Player(containerId, options);

        this.vimeoPlayer.ready().then(function() {

                if (!thisObj.hasPlaylist) {
                        // remove the media element, since Vimeo replaces that with its own element in an iframe
                        // this is handled differently for playlists. See buildplayer.js > cuePlaylistItem()
                        thisObj.$media.remove();

                        // define variables that will impact player setup

                        // vimeoSupportsPlaybackRateChange
                        // changing playbackRate is only supported if the video is hosted on a Pro or Business account
                        // unfortunately there is no direct way to query for that information.
                        // this.vimeoPlayer.getPlaybackRate() returns a value, regardless of account type
                        // This is a hack:
                        // Attempt to change the playbackRate. If it results in an error, assume changing playbackRate is not supported.
                        // Supported playbackRate values are 0.5 to 2.
                        thisObj.vimeoPlaybackRate = 1;
                        thisObj.vimeoPlayer.setPlaybackRate(thisObj.vimeoPlaybackRate).then(function(playbackRate) {
                        // playback rate was set
                                thisObj.vimeoSupportsPlaybackRateChange = true;
                        }).catch(function(error) {
                                thisObj.vimeoSupportsPlaybackRateChange = false;
                        });
                        deferred.resolve();
                }
        });
        return promise;
};

AblePlayer.prototype.getVimeoPaused = function () {

        var deferred, promise;
        deferred = new $.Deferred();
        promise = deferred.promise();

        this.vimeoPlayer.getPaused().then(function (paused) {
                // paused is Boolean
                deferred.resolve(paused);
        });

        return promise;
}

AblePlayer.prototype.getVimeoEnded = function () {

        var deferred, promise;
        deferred = new $.Deferred();
        promise = deferred.promise();

        this.vimeoPlayer.getEnded().then(function (ended) {
                // ended is Boolean
                deferred.resolve(ended);
        });

        return promise;
}

AblePlayer.prototype.getVimeoState = function () {

        var thisObj, deferred, promise, promises, gettingPausedPromise, gettingEndedPromise;

        thisObj = this;

        deferred = new $.Deferred();
        promise = deferred.promise();
        promises = [];

        gettingPausedPromise = this.vimeoPlayer.getPaused();
        gettingEndedPromise = this.vimeoPlayer.getEnded();

        promises.push(gettingPausedPromise);
        promises.push(gettingEndedPromise);

        gettingPausedPromise.then(function (paused) {
                deferred.resolve(paused);
        });
        gettingEndedPromise.then(function (ended) {
                deferred.resolve(ended);
        });
        $.when.apply($, promises).then(function () {
                deferred.resolve();
        });
        return promise;
}

AblePlayer.prototype.getVimeoDimensions = function (vimeoContainerId) {

        // get dimensions of YouTube video, return array with width & height
        // Sources, in order of priority:
        // 1. The width and height attributes on <video>
        // 2. YouTube (not yet supported; can't seem to get this data via YouTube Data API without OAuth!)

        var d, url, $iframe, width, height;

        d = [];

        if (typeof this.playerMaxWidth !== 'undefined') {
                d[0] = this.playerMaxWidth;
                // optional: set height as well; not required though since YouTube will adjust height to match width
                if (typeof this.playerMaxHeight !== 'undefined') {
                        d[1] = this.playerMaxHeight;
                }
                return d;
        }
        else {
                if (typeof $('#' + vimeoContainerId) !== 'undefined') {
                        $iframe = $('#' + vimeoContainerId);
                        width = $iframe.width();
                        height = $iframe.height();
                        if (width > 0 && height > 0) {
                                d[0] = width;
                                d[1] = height;
                                return d;
                        }
                }
        }
        return false;
};

AblePlayer.prototype.resizeVimeoPlayer = function(youTubeId, youTubeContainerId) {

        // called after player is ready, if youTube dimensions were previously unknown
        // Now need to get them from the iframe element that YouTube injected
        // and resize Able Player to match
        var d, width, height;
        if (typeof this.aspectRatio !== 'undefined') {
                // video dimensions have already been collected
                if (this.restoringAfterFullScreen) {
                        // restore using saved values
                        if (this.youTubePlayer) {
                                this.youTubePlayer.setSize(this.ytWidth, this.ytHeight);
                        }
                        this.restoringAfterFullScreen = false;
                }
                else {
                        // recalculate with new wrapper size
                        width = this.$ableWrapper.parent().width();
                        height = Math.round(width / this.aspectRatio);
                        this.$ableWrapper.css({
                                'max-width': width + 'px',
                                'width': ''
                        });
                        this.youTubePlayer.setSize(width, height);
                        if (this.fullscreen) {
                                this.youTubePlayer.setSize(width, height);
                        }
                        else {
                                // resizing due to a change in window size, not full screen
                                this.youTubePlayer.setSize(this.ytWidth, this.ytHeight);
                        }
                }
        }
        else {
                d = this.getYouTubeDimensions(youTubeId, youTubeContainerId);
                if (d) {
                        width = d[0];
                        height = d[1];
                        if (width > 0 && height > 0) {
                                this.aspectRatio = width / height;
                                this.ytWidth = width;
                                this.ytHeight = height;
                                if (width !== this.$ableWrapper.width()) {
                                        // now that we've retrieved YouTube's default width,
                                        // need to adjust to fit the current player wrapper
                                        width = this.$ableWrapper.width();
                                        height = Math.round(width / this.aspectRatio);
                                        if (this.youTubePlayer) {
                                                this.youTubePlayer.setSize(width, height);
                                        }
                                }
                        }
                }
        }
};

AblePlayer.prototype.setupVimeoCaptions = function () {

        // called from setupAltCaptions if player is YouTube and there are no <track> captions

        // use YouTube Data API to get caption data from YouTube
        // function is called only if these conditions are met:
        // 1. this.player === 'youtube'
        // 2. there are no <track> elements with kind="captions"
        // 3. youTubeDataApiKey is defined

        var deferred = new $.Deferred();
        var promise = deferred.promise();

        var thisObj, googleApiPromise, youTubeId, i;

        thisObj = this;

        // if a described version is available && user prefers desription
        // Use the described version, and get its captions
        if (this.youTubeDescId && this.prefDesc) {
                youTubeId = this.youTubeDescId;
        }
        else {
                youTubeId = this.youTubeId;
        }
        if (typeof youTubeDataAPIKey !== 'undefined') {
                // Wait until Google Client API is loaded
                // When loaded, it sets global var googleApiReady to true

                // Thanks to Paul Tavares for $.doWhen()
                // https://gist.github.com/purtuga/8257269
                $.doWhen({
                        when: function(){
                                return googleApiReady;
                        },
                        interval: 100, // ms
                        attempts: 1000
                })
                .done(function(){
                                deferred.resolve();
                })
                .fail(function(){
                        console.log('Unable to initialize Google API. YouTube captions are currently unavailable.');
                });
        }
        else {
                deferred.resolve();
        }
        return promise;
};

AblePlayer.prototype.getVimeoCaptionTracks = function () {

        // get data via Vimeo Player API, and push data to this.captions
        // Note: Vimeo doesn't expose the caption cues themselves
        // so this.captions will only include metadata about caption tracks; not cues
        var deferred = new $.Deferred();
        var promise = deferred.promise();

        var thisObj, i, trackId, isDefaultTrack;

        thisObj = this;

        this.vimeoPlayer.getTextTracks().then(function(tracks) {

                        // each Vimeo track includes the following:
                        // label (local name of the language)
                        // language (2-character code)
                        // kind (captions or subtitles, as declared by video owner)
                        // mode ('disabled' or 'showing')

                        if (tracks.length) {

                                // create a new button for each caption track
                                for (i=0; i<tracks.length; i++) {

                                        thisObj.hasCaptions = true;
                                        thisObj.usingVimeoCaptions = true;
                                        if (thisObj.prefCaptions === 1) {
                                                        thisObj.captionsOn = true;
                                        }
                                        else {
                                                thisObj.captionsOn = false;
                                        }
                                        // assign the default track based on language of the player
                                        if (tracks[i]['language'] === thisObj.lang) {
                                                isDefaultTrack = true;
                                        }
                                        else {
                                                        isDefaultTrack = false;
                                        }
                                        thisObj.tracks.push({
                                                        'kind': tracks[i]['kind'],
                                                'language': tracks[i]['language'],
                                                'label': tracks[i]['label'],
                                                'def': isDefaultTrack
                                        });
                                }

                                // setupPopups again with new captions array, replacing original
                                thisObj.setupPopups('captions');
                                deferred.resolve();
                        }
                        else {
                                        thisObj.hasCaptions = false;
                                thisObj.usingVimeoCaptions = false;
                                deferred.resolve();
                        }
                });

        return promise;
};

AblePlayer.prototype.initVimeoCaptionModule = function () {
        // This function is called when YouTube onApiChange event fires
        // to indicate that the player has loaded (or unloaded) a module with exposed API methods
        // it isn't fired until the video starts playing
        // and only fires if captions are available for this video (automated captions don't count)
        // If no captions are available, onApichange event never fires & this function is never called

        // YouTube iFrame API documentation is incomplete related to captions
        // Found undocumented features on user forums and by playing around
        // Details are here: http://terrillthompson.com/blog/648
        // Summary:
        // User might get either the AS3 (Flash) or HTML5 YouTube player
        // The API uses a different caption module for each player (AS3 = 'cc'; HTML5 = 'captions')
        // There are differences in the data and methods available through these modules
        // This function therefore is used to determine which captions module is being used
        // If it's a known module, this.ytCaptionModule will be used elsewhere to control captions
        var options, fontSize, displaySettings;

        options = this.youTubePlayer.getOptions();
        if (options.length) {
                for (var i=0; i<options.length; i++) {
                        if (options[i] == 'cc') { // this is the AS3 (Flash) player
                                this.ytCaptionModule = 'cc';
                                if (!this.hasCaptions) {
                                        // there are captions available via other sources (e.g., <track>)
                                        // so use these
                                        this.hasCaptions = true;
                                        this.usingYouTubeCaptions = true;
                                }
                                break;
                        }
                        else if (options[i] == 'captions') { // this is the HTML5 player
                                this.ytCaptionModule = 'captions';
                                if (!this.hasCaptions) {
                                        // there are captions available via other sources (e.g., <track>)
                                        // so use these
                                        this.hasCaptions = true;
                                        this.usingYouTubeCaptions = true;
                                }
                                break;
                        }
                }
                if (typeof this.ytCaptionModule !== 'undefined') {
                        if (this.usingYouTubeCaptions) {
                                // set default languaage
                                this.youTubePlayer.setOption(this.ytCaptionModule, 'track', {'languageCode': this.captionLang});
                                // set font size using Able Player prefs (values are -1, 0, 1, 2, and 3, where 0 is default)
                                this.youTubePlayer.setOption(this.ytCaptionModule,'fontSize',this.translatePrefs('size',this.prefCaptionsSize,'youtube'));
                                // ideally could set other display options too, but no others seem to be supported by setOption()
                        }
                        else {
                                // now that we know which cc module was loaded, unload it!
                                // we don't want it if we're using local <track> elements for captions
                                this.youTubePlayer.unloadModule(this.ytCaptionModule)
                        }
                }
        }
        else {
                // no modules were loaded onApiChange
                // unfortunately, gonna have to disable captions if we can't control them
                this.hasCaptions = false;
                this.usingYouTubeCaptions = false;
        }
        this.refreshControls('captions');
};

AblePlayer.prototype.getVimeoPosterUrl = function (youTubeId, width) {

                 // return a URL for retrieving a YouTube poster image
                 // supported values of width: 120, 320, 480, 640

                 var url = 'https://img.youtube.com/vi/' + youTubeId;
                 if (width == '120') {
                         // default (small) thumbnail, 120 x 90
                         return url + '/default.jpg';
                 }
                 else if (width == '320') {
                         // medium quality thumbnail, 320 x 180
                         return url + '/hqdefault.jpg';
                 }
                 else if (width == '480') {
                         // high quality thumbnail, 480 x 360
                         return url + '/hqdefault.jpg';
                 }
                 else if (width == '640') {
                         // standard definition poster image, 640 x 480
                         return url + '/sddefault.jpg';
                 }
                 return false;
};

})(jQuery);