UNPKG

infusion

Version:

Infusion is an application framework for developing flexible stuff with JavaScript

287 lines (260 loc) 10.9 kB
/* Copyright The Infusion copyright holders See the AUTHORS.md file at the top-level directory of this distribution and at https://github.com/fluid-project/infusion/raw/master/AUTHORS.md. Licensed under the Educational Community License (ECL), Version 2.0 or the New BSD license. You may not use this file except in compliance with one these Licenses. You may obtain a copy of the ECL 2.0 License and BSD License at https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt */ /* global YT */ var fluid_3_0_0 = fluid_3_0_0 || {}; (function ($, fluid) { "use strict"; /******************************************************************************* * captions * * An enactor that is capable of enabling captions on embedded YouTube videos *******************************************************************************/ fluid.defaults("fluid.prefs.enactor.captions", { gradeNames: ["fluid.prefs.enactor", "fluid.viewComponent"], preferenceMap: { "fluid.prefs.captions": { "model.enabled": "value" } }, events: { onVideoElementLocated: null }, selectors: { videos: "iframe[src^=\"https://www.youtube.com/embed/\"]" }, model: { enabled: false }, components: { ytAPI: { type: "fluid.prefs.enactor.captions.ytAPI" } }, dynamicComponents: { player: { type: "fluid.prefs.enactor.captions.youTubePlayer", createOnEvent: "onVideoElementLocated", container: "{arguments}.0", options: { model: { captions: "{captions}.model.enabled" } } } }, listeners: { "onCreate.initPlayers": "{that}.initPlayers" }, invokers: { initPlayers: { funcName: "fluid.prefs.enactor.captions.initPlayers", args: ["{that}", "{ytAPI}.notifyWhenLoaded", "{that}.dom.videos"] } } }); /** * When the YouTube API is available, the onVideoElementLocated event will fire for each video element located by * the `videos` argument. Each of these event calls will fire with a jQuery object containing a single video * element. This allows for initializing dynamicComponents (fluid.prefs.enactor.captions.youTubePlayer) for each * video element. * * @param {Component} that - the component * @param {Function} getYtApi - a function that returns a promise indicating if the YouTube API is available * @param {jQuery|Element} videos - the videos to fire onVideoElementLocated events with * * @return {Promise} - A promise that follows the promise returned by the getYtApi function */ fluid.prefs.enactor.captions.initPlayers = function (that, getYtApi, videos) { var promise = fluid.promise(); var ytAPINotice = getYtApi(); promise.then(function () { $(videos).each(function (index, elm) { that.events.onVideoElementLocated.fire($(elm)); }); }); fluid.promise.follow(ytAPINotice, promise); return promise; }; /********************************************************************************************* * fluid.prefs.enactor.captions.window is a singleton component to be used for assigning * * values onto the window object. * *********************************************************************************************/ fluid.defaults("fluid.prefs.enactor.captions.ytAPI", { gradeNames: ["fluid.component", "fluid.resolveRootSingle"], singleRootType: "fluid.prefs.enactor.captions.window", events: { onYouTubeAPILoaded: null }, members: { global: window }, invokers: { notifyWhenLoaded: { funcName: "fluid.prefs.enactor.captions.ytAPI.notifyWhenLoaded", args: ["{that}"] } } }); /** * Used to determine when the YouTube API is available for use. It will test if the API is already available, and if * not, will bind to the onYouTubeIframeAPIReady method that is called when the YouTube API finishes loading. * When the YouTube API is ready, the promise will resolve an the onYouTubeAPILoaded event will fire. * * NOTE: After FLUID-6148 (https://issues.fluidproject.org/browse/FLUID-6148) is complete, it should be possible for * the framework to handle this asynchrony directly in an expander for the player member in * fluid.prefs.enactor.captions.youTubePlayer. * * @param {Component} that - the component itself * * @return {Promise} - a promise resolved after the YouTube API has loaded. */ fluid.prefs.enactor.captions.ytAPI.notifyWhenLoaded = function (that) { var promise = fluid.promise(); promise.then(function () { that.events.onYouTubeAPILoaded.fire(); }, function (error) { fluid.log(fluid.logLevel.WARN, error); }); if (fluid.get(window, ["YT", "Player"])) { promise.resolve(); } else { // the YouTube iframe api will call onYouTubeIframeAPIReady after the api has loaded fluid.set(that.global, "onYouTubeIframeAPIReady", promise.resolve); } return promise; }; /** * See: https://developers.google.com/youtube/iframe_api_reference#Events for details on the YouTube player * events. This includes when they are fired and what data is passed along. */ fluid.defaults("fluid.prefs.enactor.captions.youTubePlayer", { gradeNames: ["fluid.viewComponent"], events: { onReady: null, onStateChange: null, onPlaybackQualityChange: null, onPlaybackRateChange: null, onError: null, onApiChange: null }, model: { captions: false, track: {} }, members: { player: { expander: { funcName: "fluid.prefs.enactor.captions.youTubePlayer.initYTPlayer", args: ["{that}"] } }, tracklist: [] }, invokers: { applyCaptions: { funcName: "fluid.prefs.enactor.captions.youTubePlayer.applyCaptions", args: ["{that}.player", "{that}.model.track", "{that}.model.captions"] } }, modelListeners: { "setCaptions": { listener: "{that}.applyCaptions", path: ["captions", "track"], excludeSource: "init" } }, listeners: { "onApiChange.prepTrack": { listener: "fluid.prefs.enactor.captions.youTubePlayer.prepTrack", args: ["{that}", "{that}.player"] }, "onApiChange.applyCaptions": { listener: "{that}.applyCaptions", priority: "after:prepTrack" } } }); /** * Adds the "enablejsapi=1" query parameter to the query string at the end of the src attribute. * If "enablejsapi" already exists it will modify its value to 1. This is required for API access * to the embedded YouTube video. * * @param {jQuery|Element} videoElm - a reference to the existing embedded YouTube video. */ fluid.prefs.enactor.captions.youTubePlayer.enableJSAPI = function (videoElm) { videoElm = $(videoElm); var url = new URL(videoElm.attr("src")); url.searchParams.set("enablejsapi", 1); videoElm.attr("src", url.toString()); }; /** * An instance of a YouTube player from the YouTube iframe API * * @typedef {Object} YTPlayer */ /** * Initializes the YT.Player using the existing embedded video (component's container). An ID will be added to the * video element if one does not already exist. * * @param {Component} that - the component * @return {YTPlayer} - an instance of a YouTube player controlling the embedded video */ fluid.prefs.enactor.captions.youTubePlayer.initYTPlayer = function (that) { var id = fluid.allocateSimpleId(that.container); fluid.prefs.enactor.captions.youTubePlayer.enableJSAPI(that.container); return new YT.Player(id, { events: { onReady: that.events.onReady.fire, onStateChange: that.events.onStateChange.fire, onPlaybackQualityChange: that.events.onPlaybackQualityChange.fire, onPlaybackRateChange: that.events.onPlaybackRateChange.fire, onError: that.events.onError.fire, onApiChange: that.events.onApiChange.fire } }); }; /** * Enables/disables the captions on an embedded YouTube video. Requires that the player be initiallized and the API * ready for use. * * @param {YTPlayer} player - an instance of a YouTube player * @param {Object} track - a track object for the {YTPlayer} * @param {Boolean} state - true - captions enabled; false - captions disabled. */ fluid.prefs.enactor.captions.youTubePlayer.applyCaptions = function (player, track, state) { // The loadModule method from the player must be ready first. This is made available after // the onApiChange event has fired. if (player.loadModule) { if (state) { player.loadModule("captions"); player.setOption("captions", "track", track); } else { player.unloadModule("captions"); } } }; /** * Prepares the track to be used when captions are enabled. It will use the first track in the tracklist, and update * the "track" model path with it. * * @param {Component} that - the component * @param {YTPlayer} player - an instance of a YouTube player */ fluid.prefs.enactor.captions.youTubePlayer.prepTrack = function (that, player) { player.loadModule("captions"); var tracklist = player.getOption("captions", "tracklist"); if (tracklist.length && !that.tracklist.length) { // set the tracklist and use first track for the captions that.tracklist = tracklist; that.applier.change("track", tracklist[0], "ADD", "prepTrack"); } }; })(jQuery, fluid_3_0_0);