/** * EGroupware eTemplate2 - JS Description object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link https://www.egroupware.org * @author Hadi Nategh * @copyright EGroupware GmbH 2020-2021 */ /*egw:uses /vendor/bower-asset/jquery/dist/jquery.js; et2_core_interfaces; et2_core_baseWidget; */ import { et2_baseWidget } from './et2_core_baseWidget'; import { ClassWithAttributes } from "./et2_core_inheritance"; import { et2_register_widget } from "./et2_core_widget"; /** * This widget represents the HTML5 video tag with all its optional attributes * * The widget can be created in the following ways: * * var videoTag = et2_createWidget("video", { * video_src: "../../test.mp4", * src_type: "video/mp4", * muted: true, * autoplay: true, * controls: true, * poster: "../../poster.jpg", * loop: true, * height: 100, * width: 200, * }); * * Or by adding XET-tag in your template (.xet) file: * * */ /** * Class which implements the "video" XET-Tag * * @augments et2_baseWidget */ export class et2_video extends et2_baseWidget { constructor(_parent, _attrs, _child) { super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_video._attributes, _child || {})); this.video = null; /** * keeps internal state of previousTime video played * @private */ this._previousTime = 0; this.set_src_type(this.options.src_type); } set_src_type(_type) { this.options.src_type = _type; if (this.video && this._isYoutube() === (this.video[0].tagName === 'DIV')) { return; } //Create Video tag this.video = jQuery(document.createElement(this._isYoutube() ? "div" : "video")) .addClass('et2_video') .attr('id', this.dom_id); if (this._isYoutube()) { // this div will be replaced by youtube iframe api when youtube gets ready this.youtubeFrame = jQuery(document.createElement('div')) .appendTo(this.video) .attr('id', et2_video.youtubePrefixId + this.id); if (!document.getElementById('youtube-api-script')) { //Load youtube iframe api let tag = document.createElement('script'); tag.id = 'youtube-api-script'; tag.src = "https://www.youtube.com/iframe_api"; let firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); } } if (!this._isYoutube() && this.options.controls) { this.video.attr("controls", 1); } if (!this._isYoutube() && this.options.autoplay) { this.video.attr("autoplay", 1); } if (this.options.muted) { this.video.attr("muted", 1); } if (this.options.video_src) { this.set_src(this.options.video_src); } if (this.options.loop) { this.video.attr("loop", 1); } this.setDOMNode(this.video[0]); this.set_width(this.options.width || 'auto'); this.set_height(this.options.height || 'auto'); } /** * Set video src * * @param {string} _value url */ set_src(_value) { let self = this; this.options.video_src = _value; if (_value && !this._isYoutube()) { this.video.attr('src', _value); if (this.options.src_type) { this.video.attr('type', this.options.src_type); } } else if (_value) { if (typeof YT == 'undefined') { //initiate youtube Api object, it gets called automatically by iframe_api script from the api window.onYouTubeIframeAPIReady = this._onYoutubeIframeAPIReady; window.addEventListener('et2_video.onYoutubeIframeAPIReady', function () { self._createYoutubePlayer(self.options.video_src); }); } else { self._createYoutubePlayer(self.options.video_src); } } } /** * Set autoplay option for video * -If autoplay is set, video would be played automatically after the page is loaded * * @param {string} _value true set the autoplay and false not to set */ set_autoplay(_value) { if (_value && !this._isYoutube()) { this.video.attr("autoplay", _value); } } /** * Set controls option for video * * @param {string} _value true set the autoplay and false not to set */ set_controls(_value) { if (_value && !this._isYoutube()) { this.video.attr("controls", _value); } } /** * Set poster attribute in order to specify * an image to be shown while video is loading or before user play it * * @param {string} _url url or image spec like "api/mime128_video" */ set_poster(_url) { if (_url) { if (_url[0] !== '/' && !_url.match(/^https?:\/\//)) { _url = this.egw().image(_url); } this.video.attr("poster", _url); } } /** * Seek to a time / position * * @param _vtime in seconds */ seek_video(_vtime) { if (this._isYoutube()) { if (this.youtube.seekTo) { this.youtube.seekTo(_vtime, true); this._currentTime = _vtime; } } else { this.video[0].currentTime = _vtime; } } /** * Play video */ play_video() { if (this._isYoutube()) { let self = this; return new Promise(function (resolve) { if (self.youtube.playVideo) { self.youtube.playVideo(); resolve(); } }); } return this.video[0].play(); } /** * Pause video */ pause_video() { if (this._isYoutube()) { if (this.youtube.pauseVideo) { this.youtube.pauseVideo(); this.currentTime(this.youtube.getCurrentTime()); } } else { this.video[0].pause(); } } /** * play video * ***Internal use and should not be overriden in its extended class*** */ play() { var _a; return this._isYoutube() && ((_a = this.youtube) === null || _a === void 0 ? void 0 : _a.playVideo) ? this.youtube.playVideo() : this.video[0].play(); } /** * Get/set current video time / position in seconds * @return returns currentTime */ currentTime(_time) { var _a; if (_time) { if (this._isYoutube()) { this.youtube.seekTo(_time); } else { this.video[0].currentTime = _time; } return this._currentTime = _time; } if (this._isYoutube()) { if (typeof this._currentTime != 'undefined') { return this._currentTime; } return ((_a = this.youtube) === null || _a === void 0 ? void 0 : _a.getCurrentTime) ? this.youtube.getCurrentTime() : 0; } else { return this.video[0].currentTime; } } /** * get duration time * @return returns duration time */ duration() { var _a; if (this._isYoutube()) { return ((_a = this.youtube) === null || _a === void 0 ? void 0 : _a.getDuration) ? this.youtube.getDuration() : 0; } else { return this.video[0].duration; } } /** * get pasued * @return returns paused flag */ paused() { if (this._isYoutube()) { return this.youtube.getPlayerState() == et2_video.youtube_player_states.paused; } return this.video[0].paused; } /** * get ended * @return returns ended flag */ ended() { if (this._isYoutube()) { return this.youtube.getPlayerState() == et2_video.youtube_player_states.ended; } return this.video[0].ended; } /** * get/set priviousTime * @param _time * @return returns time */ previousTime(_time) { if (_time) this._previousTime = _time; return this._previousTime; } doLoadingFinished() { super.doLoadingFinished(); let self = this; if (!this._isYoutube()) { this.video[0].addEventListener("loadedmetadata", function () { self._onReady(); }); this.video[0].addEventListener("timeupdate", function () { self._onTimeUpdate(); }); } else { // need to create the player after the DOM is ready otherwise player won't show up if (window.YT) this._createYoutubePlayer(this.options.video_src); } return false; } videoLoadnigIsFinished() { if (this.options.starttime) { this.seek_video(this.options.starttime); // unfortunately, youtube api autoplays the video after seekTo on initiation // and there's no way to stop that therefore we need to trick it by manually // pausing the video (this would bring up the spinner with the black screen, // in order to avoid that we let the video plays for a second then we pause). // since the youtube timeline is one second advanced we need to seek back to // the original stattime although this time because it was manually paused // we won't have the spinner and black screen instead we get the preview. if (this._isYoutube()) window.setTimeout(function () { this.youtube.pauseVideo(); this.youtube.seekTo(this.options.starttime); ; }.bind(this), 1000); } } _onReady() { // need to set the video dom to transformed iframe if (this._isYoutube() && this.youtube.getIframe) this.youtubeFrame = jQuery(this.youtube.getIframe()); let event = document.createEvent("Event"); event.initEvent('et2_video.onReady.' + this.id, true, true); this.video[0].dispatchEvent(event); } _onTimeUpdate() { // update currentTime manually since youtube currentTime might be updated due to the loading if (this._isYoutube() && this.youtube.getCurrentTime) this._currentTime = this.youtube.getCurrentTime(); let event = document.createEvent("Event"); event.initEvent('et2_video.onTimeUpdate.' + this.id, true, true); this.video[0].dispatchEvent(event); } /** * check if the video is a youtube type * @return return true if it's a youtube type video * @private */ _isYoutube() { return !!this.options.src_type.match('youtube'); } _onStateChangeYoutube(_data) { switch (_data.data) { case et2_video.youtube_player_states.unstarted: // do nothing break; case et2_video.youtube_player_states.playing: this._youtubeOntimeUpdateIntrv = window.setInterval(jQuery.proxy(this._onTimeUpdate, this), 100); break; default: window.clearInterval(this._youtubeOntimeUpdateIntrv); } console.log(_data); } /** * youtube on IframeAPI ready event */ _onYoutubeIframeAPIReady() { let event = document.createEvent("Event"); event.initEvent('et2_video.onYoutubeIframeAPIReady', true, true); window.dispatchEvent(event); } /** * create youtube player * * @param _value */ _createYoutubePlayer(_value) { const matches = _value === null || _value === void 0 ? void 0 : _value.match(et2_video.youtubeRegexp); if (matches && typeof YT != 'undefined') { this.youtube = new YT.Player(et2_video.youtubePrefixId + this.id, { height: this.options.height || '400', width: '100%', playerVars: { 'autoplay': 0, 'controls': 0, 'modestbranding': 1, 'fs': 0, 'disablekb': 1, 'rel': 0, 'iv_load_policy': 0, 'cc_load_policy': 0 }, videoId: matches[4], events: { 'onReady': jQuery.proxy(this._onReady, this), 'onStateChange': jQuery.proxy(this._onStateChangeYoutube, this) } }); } } } et2_video._attributes = { "video_src": { "name": "Video", "type": "string", "description": "Source of video to display" }, "src_type": { "name": "Source type", "type": "string", "description": "Defines the type the stream source provided" }, "muted": { "name": "Audio control", "type": "boolean", "default": false, "description": "Defines that the audio output of the video should be muted" }, "autoplay": { "name": "Autoplay", "type": "boolean", "default": false, "description": "Defines if Video will start playing as soon as it is ready" }, starttime: { "name": "Inital position of video", "type": "float", "default": 0, "description": "Set initial position of video" }, "controls": { "name": "Control buttons", "type": "boolean", "default": false, "description": "Defines if Video controls, play/pause buttons should be displayed" }, "poster": { "name": "Video Poster", "type": "string", "default": "", "description": "Specifies an image to be shown while video is loading or before user play it" }, "loop": { "name": "Video loop", "type": "boolean", "default": false, "description": "Defines if the video should be played repeatedly" } }; et2_video.youtube_player_states = { unstarted: -1, ended: 0, playing: 1, paused: 2, buffering: 3, video_cued: 5 }; /** * prefix id used for addressing youtube player dom * @private */ et2_video.youtubePrefixId = "frame-"; et2_video.youtubeRegexp = new RegExp(/^https:\/\/((www\.|m\.)?youtube(-nocookie)?\.com|youtu\.be)\/.*(?:\/|%3D|v=|vi=)([0-9A-z-_]{11})(?:[%#?&]|$)/m); et2_register_widget(et2_video, ["video"]); //# sourceMappingURL=et2_widget_video.js.map