/* * Copyright 2016 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var EventEmitter = require('eventemitter3'); var IFrameMessageSender = require('./iframe-message-sender'); var Message = require('../message'); var Util = require('../util'); // Save the executing script. This will be used to calculate the embed URL. var CURRENT_SCRIPT_SRC = Util.getCurrentScript().src; var FAKE_FULLSCREEN_CLASS = 'vrview-fake-fullscreen'; /** * Entry point for the VR View JS API. * * Emits the following events: * ready: When the player is loaded. * modechange: When the viewing mode changes (normal, fullscreen, VR). * click (id): When a hotspot is clicked. */ function Player(selector, contentInfo) { // Create a VR View iframe depending on the parameters. var iframe = this.createIframe_(contentInfo); this.iframe = iframe; var parentEl = document.querySelector(selector); parentEl.appendChild(iframe); // Make a sender as well, for relying commands to the child IFrame. this.sender = new IFrameMessageSender(iframe); // Listen to messages from the IFrame. window.addEventListener('message', this.onMessage_.bind(this), false); // Expose a public .isPaused attribute. this.isPaused = false; // Expose a public .isMuted attribute. this.isMuted = false; if (typeof contentInfo.muted !== 'undefined') { this.isMuted = contentInfo.muted; } // Other public attributes this.currentTime = 0; this.duration = 0; this.volume = contentInfo.volume != undefined ? contentInfo.volume : 1; if (Util.isIOS()) { this.injectFullscreenStylesheet_(); } } Player.prototype = new EventEmitter(); /** * @param pitch {Number} The latitude of center, specified in degrees, between * -90 and 90, with 0 at the horizon. * @param yaw {Number} The longitude of center, specified in degrees, between * -180 and 180, with 0 at the image center. * @param radius {Number} The radius of the hotspot, specified in meters. * @param distance {Number} The distance of the hotspot from camera, specified * in meters. * @param hotspotId {String} The ID of the hotspot. */ Player.prototype.addHotspot = function(hotspotId, params) { // TODO: Add validation to params. var data = { pitch: params.pitch, yaw: params.yaw, radius: params.radius, distance: params.distance, id: hotspotId }; this.sender.send({type: Message.ADD_HOTSPOT, data: data}); }; Player.prototype.play = function() { this.sender.send({type: Message.PLAY}); }; Player.prototype.pause = function() { this.sender.send({type: Message.PAUSE}); }; /** * Equivalent of HTML5 setSrc(). * @param {String} contentInfo */ Player.prototype.setContent = function(contentInfo) { this.absolutifyPaths_(contentInfo); var data = { contentInfo: contentInfo }; this.sender.send({type: Message.SET_CONTENT, data: data}); }; /** * Sets the software volume of the video. 0 is mute, 1 is max. */ Player.prototype.setVolume = function(volumeLevel) { var data = { volumeLevel: volumeLevel }; this.sender.send({type: Message.SET_VOLUME, data: data}); }; Player.prototype.getVolume = function() { return this.volume; }; /** * Sets the mute state of the video element. true is muted, false is unmuted. */ Player.prototype.mute = function(muteState) { var data = { muteState: muteState }; this.sender.send({type: Message.MUTED, data: data}); }; /** * Set the current time of the media being played * @param {Number} time */ Player.prototype.setCurrentTime = function(time) { var data = { currentTime: time }; this.sender.send({type: Message.SET_CURRENT_TIME, data: data}); }; Player.prototype.getCurrentTime = function() { return this.currentTime; }; Player.prototype.getDuration = function() { return this.duration; }; Player.prototype.setFullscreen = function() { this.sender.send({type: Message.SET_FULLSCREEN}); }; /** * Helper for creating an iframe. * * @return {IFrameElement} The iframe. */ Player.prototype.createIframe_ = function(contentInfo) { this.absolutifyPaths_(contentInfo); var iframe = document.createElement('iframe'); iframe.setAttribute('allowfullscreen', true); iframe.setAttribute('scrolling', 'no'); iframe.style.border = 0; // Handle iframe size if width and height are specified. if (contentInfo.hasOwnProperty('width')) { iframe.setAttribute('width', contentInfo.width); delete contentInfo.width; } if (contentInfo.hasOwnProperty('height')) { iframe.setAttribute('height', contentInfo.height); delete contentInfo.height; } var url = this.getEmbedUrl_() + Util.createGetParams(contentInfo); iframe.src = url; return iframe; }; Player.prototype.onMessage_ = function(event) { var message = event.data; if (!message || !message.type) { console.warn('Received message with no type.'); return; } var type = message.type.toLowerCase(); var data = message.data; switch (type) { case 'ready': if (data !== undefined && data.duration !== undefined) { this.duration = data.duration; } case 'modechange': case 'error': case 'click': case 'ended': case 'getposition': this.emit(type, data); break; case 'volumechange': this.volume = data; this.emit('volumechange', data); break; case 'muted': this.isMuted = data; this.emit('mute', data); break; case 'timeupdate': this.currentTime = data; this.emit('timeupdate', { currentTime: this.currentTime, duration: this.duration }); break; case 'play': case 'paused': this.isPaused = data; if (this.isPaused) { this.emit('pause', data); } else { this.emit('play', data); } break; case 'enter-fullscreen': case 'enter-vr': this.setFakeFullscreen_(true); break; case 'exit-fullscreen': this.setFakeFullscreen_(false); break; default: console.warn('Got unknown message of type %s from %s', message.type, message.origin); } }; /** * Note: iOS doesn't support the fullscreen API. * In standalone