Browse Source

Move sandbox code to own file, refactor common sandboxing code to service.

pull/195/head
Joachim Bauch 10 years ago
parent
commit
7cc196f4e0
  1. 2
      Makefile.am
  2. 7
      build/build.js
  3. 4
      src/styles/components/_youtubevideo.scss
  4. 66
      static/js/directives/youtubevideo.js
  5. 248
      static/js/sandboxes/youtube.js
  6. 66
      static/js/services/sandbox.js
  7. 9
      static/js/services/services.js
  8. 226
      static/partials/youtubevideo_sandbox.html

2
Makefile.am

@ -131,6 +131,7 @@ install:
$(INSTALL) -d $(SHARE)/www/static/translation $(INSTALL) -d $(SHARE)/www/static/translation
$(INSTALL) -d $(SHARE)/www/static/css $(INSTALL) -d $(SHARE)/www/static/css
$(INSTALL) -d $(SHARE)/www/static/js/libs/pdf $(INSTALL) -d $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -d $(SHARE)/www/static/js/sandboxes
$(INSTALL) bin/$(EXENAME) $(BIN) $(INSTALL) bin/$(EXENAME) $(BIN)
$(INSTALL) html/* $(SHARE)/www/html $(INSTALL) html/* $(SHARE)/www/html
$(INSTALL) static/img/* $(SHARE)/www/static/img $(INSTALL) static/img/* $(SHARE)/www/static/img
@ -142,6 +143,7 @@ install:
$(INSTALL) $(OUTPUT_JS)/*.js $(SHARE)/www/static/js $(INSTALL) $(OUTPUT_JS)/*.js $(SHARE)/www/static/js
$(INSTALL) $(OUTPUT_JS)/libs/pdf/*.js $(SHARE)/www/static/js/libs/pdf $(INSTALL) $(OUTPUT_JS)/libs/pdf/*.js $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -D static/js/libs/webodf.js $(SHARE)/www/static/js/libs/webodf.js $(INSTALL) -D static/js/libs/webodf.js $(SHARE)/www/static/js/libs/webodf.js
$(INSTALL) $(OUTPUT_JS)/sandboxes/*.js $(SHARE)/www/static/js/sandboxes
clean: clean:
$(GO) clean -i -r app/... 2>/dev/null || true $(GO) clean -i -r app/... 2>/dev/null || true

7
build/build.js

@ -76,6 +76,13 @@
override: { override: {
skipModuleInsertion: true skipModuleInsertion: true
} }
},
{
name: 'sandboxes/youtube',
dir: './out/sandboxes',
override: {
skipModuleInsertion: true
}
} }
] ]
}) })

4
src/styles/components/_youtubevideo.scss

@ -94,6 +94,10 @@
position: relative; position: relative;
} }
#youtubecontainer.fullscreen { // scss-lint:disable IdSelector
width: 100%;
}
#youtubeplayerinfo { // scss-lint:disable IdSelector #youtubeplayerinfo { // scss-lint:disable IdSelector
bottom: 10%; bottom: 10%;
left: 0; left: 0;

66
static/js/directives/youtubevideo.js

@ -20,33 +20,20 @@
*/ */
"use strict"; "use strict";
define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'text!partials/youtubevideo_sandbox.html', 'bigscreen'], function($, _, moment, template, sandboxTemplate, BigScreen) { define(['require', 'jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'text!partials/youtubevideo_sandbox.html', 'bigscreen'], function(require, $, _, moment, template, sandboxTemplate, BigScreen) {
return ["$window", "$document", "mediaStream", "alertify", "translation", "safeApply", "appData", "$q", function($window, $document, mediaStream, alertify, translation, safeApply, appData, $q) { return ["$window", "$document", "mediaStream", "alertify", "translation", "safeApply", "appData", "$q", "restURL", "sandbox", function($window, $document, mediaStream, alertify, translation, safeApply, appData, $q, restURL, sandbox) {
var YOUTUBE_IFRAME_API_URL = "//www.youtube.com/iframe_api"; var YOUTUBE_IFRAME_API_URL = "//www.youtube.com/iframe_api";
var origin = $window.location.protocol + "//" + $window.location.host; var isYouTubeIframeAPIReadyDefer = $q.defer();
var isYouTubeIframeAPIReady = isYouTubeIframeAPIReadyDefer.promise;
var Sandbox = function(iframe) { var SandboxPlayer = function(sandbox, params) {
this.iframe = iframe; this.sandbox = sandbox;
var template = sandboxTemplate;
template = template.replace(/__PARENT__ORIGIN__/g, origin);
this.iframe.src = "data:text/html;charset=utf-8," + encodeURI(template);
this.target = this.iframe.contentWindow;
this.state = -1; this.state = -1;
this.position = 0; this.position = 0;
this.lastPositionUpdate = null; this.lastPositionUpdate = null;
};
Sandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type}
msg[type] = message;
this.target.postMessage(msg, "*");
};
var SandboxPlayer = function(sandbox, params) {
this.sandbox = sandbox;
this.sandbox.postMessage("loadPlayer", params); this.sandbox.postMessage("loadPlayer", params);
}; };
@ -117,18 +104,15 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
var isPaused = null; var isPaused = null;
var playReceivedNow = null; var playReceivedNow = null;
var initialState = null; var initialState = null;
var sandboxFrame = $("#youtubeplayer", $element)[0];
var sandbox = new Sandbox($("#youtubeplayer", $element)[0]); var template = sandboxTemplate;
template = template.replace(/__PARENT_ORIGIN__/g, $window.location.protocol + "//" + $window.location.host);
var isYouTubeIframeAPIReadyDefer = $q.defer(); template = template.replace(/__YOUTUBE_SANDBOX_JS_URL__/g, restURL.createAbsoluteUrl(require.toUrl('sandboxes/youtube') + ".js"));
var isYouTubeIframeAPIReady = isYouTubeIframeAPIReadyDefer.promise; var sandboxApi = sandbox.createSandbox(sandboxFrame, template);
var onPostMessage = function(event) { sandboxApi.e.on("message", function(event, message) {
if (event.origin !== "null" || event.source !== sandbox.target) { var msg = message.data;
// the sandboxed data-url iframe has "null" as origin
return;
}
var msg = event.data;
var data = msg[msg.type] || {}; var data = msg[msg.type] || {};
switch (msg.type) { switch (msg.type) {
case "youtube.apiReady": case "youtube.apiReady":
@ -162,15 +146,17 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
} }
break; break;
default: default:
console.log("Unknown message received", event); console.log("Unknown message received", message);
break; break;
} }
}; });
$window.addEventListener("message", onPostMessage, false);
$scope.$on("$destroy", function() { $scope.$on("$destroy", function() {
$window.removeEventListener("message", onPostMessage, false); if (player) {
player.destroy();
}
sandboxApi.destroy();
sandboxApi = null;
}); });
var errorIds = { var errorIds = {
@ -293,7 +279,6 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
var playVideo = function(id, position, state) { var playVideo = function(id, position, state) {
playerReady.done(function() { playerReady.done(function() {
$("#youtubeplayer").show();
$scope.playbackActive = true; $scope.playbackActive = true;
isPaused = null; isPaused = null;
if (playReceivedNow) { if (playReceivedNow) {
@ -328,7 +313,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
isYouTubeIframeAPIReady.then(function() { isYouTubeIframeAPIReady.then(function() {
if (!player) { if (!player) {
var origin = $window.location.protocol + "//" + $window.location.host; var origin = $window.location.protocol + "//" + $window.location.host;
player = new SandboxPlayer(sandbox, { player = new SandboxPlayer(sandboxApi, {
height: "390", height: "390",
width: "640", width: "640",
playerVars: { playerVars: {
@ -343,7 +328,6 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
"origin": origin "origin": origin
} }
}); });
$("#youtubeplayer").show();
safeApply($scope, function(scope) { safeApply($scope, function(scope) {
// YT player events don't fire in Firefox if // YT player events don't fire in Firefox if
// player is not visible, so show while loading // player is not visible, so show while loading
@ -521,7 +505,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
}; };
$scope.loadYouTubeAPI = function() { $scope.loadYouTubeAPI = function() {
sandbox.postMessage("loadApi", {"url": $window.location.protocol + YOUTUBE_IFRAME_API_URL}); sandboxApi.postMessage("loadApi", {"url": $window.location.protocol + YOUTUBE_IFRAME_API_URL});
}; };
$scope.showYouTubeVideo = function() { $scope.showYouTubeVideo = function() {
@ -601,7 +585,11 @@ define(['jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'te
$scope.toggleFullscreen = function(elem) { $scope.toggleFullscreen = function(elem) {
if (BigScreen.enabled) { if (BigScreen.enabled) {
BigScreen.toggle(elem); BigScreen.toggle(elem, function() {
$(elem).addClass("fullscreen");
}, function() {
$(elem).removeClass("fullscreen");
});
} }
}; };

248
static/js/sandboxes/youtube.js

@ -0,0 +1,248 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
"use strict";
(function () {
var script = document.getElementsByTagName("script")[0];
var PARENT_ORIGIN = script.getAttribute("data-parent-origin");
var YouTubeSandbox = function(window) {
this.head = document.getElementsByTagName('head')[0];
this.window = window;
this.addedIframeScript = false;
this.player = null;
this.seekDetector = null;
this.prevTime = null;
this.prevNow = null;
};
YouTubeSandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type};
msg[type] = message;
this.window.parent.postMessage(msg, PARENT_ORIGIN);
};
YouTubeSandbox.prototype.onYouTubeIframeAPIReady = function() {
this.postMessage("youtube.apiReady", {"apiReady": true});
};
YouTubeSandbox.prototype.loadApi = function(url) {
if (!this.addedIframeScript) {
var that = this;
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;
script.onerror = function(evt) {
that.postMessage("youtube.error", {"msgid": "loadScriptFailed"});
that.head.removeChild(script);
that.addedIframeScript = false;
};
this.head.appendChild(script);
this.addedIframeScript = true;
}
};
YouTubeSandbox.prototype.loadPlayer = function(params) {
if (!this.player) {
var that = this;
var stateEvents = {
"-1": "youtube.unstarted",
"0": "youtube.ended",
"1": "youtube.playing",
"2": "youtube.paused",
"3": "youtube.buffering",
"5": "youtube.videocued"
};
var playerVars = params.playerVars || {};
delete playerVars.origin;
this.player = new this.window.YT.Player("youtubeplayer", {
height: params.height || "390",
width: params.width || "640",
playerVars: playerVars,
events: {
"onReady": function(event) {
that.postMessage("youtube.volume", {"volume": that.player.getVolume()});
that.postMessage("youtube.playerReady", {"playerReady": true});
},
"onStateChange": function(event) {
var msg = stateEvents[event.data];
if (typeof msg === "undefined") {
console.warn("Unknown YouTube player state", event)
return;
}
switch (msg) {
case "youtube.playing":
that.prevTime = null;
that.startDetectSeek();
break;
case "youtube.buffering":
that.startDetectSeek();
break;
case "youtube.paused":
that.stopDetectSeek();
break;
case "youtube.ended":
that.stopDetectSeek();
break;
}
that.postMessage("youtube.event", {"event": msg, "state": event.data, "position": that.player.getCurrentTime()});
}
}
});
}
};
YouTubeSandbox.prototype.destroyPlayer = function() {
this.stopDetectSeek();
if (this.player) {
this.player.destroy();
this.player = null;
}
};
YouTubeSandbox.prototype.loadVideo = function(id, position) {
this.prevTime = null;
this.prevNow = null;
if (typeof(position) !== "undefined") {
this.player.loadVideoById(id, position);
} else {
this.player.loadVideoById(id);
}
};
YouTubeSandbox.prototype.playVideo = function() {
this.player.playVideo();
};
YouTubeSandbox.prototype.pauseVideo = function() {
this.player.pauseVideo();
};
YouTubeSandbox.prototype.stopVideo = function() {
this.player.stopVideo();
};
YouTubeSandbox.prototype.seekTo = function(position, allowSeekAhead) {
if (typeof(allowSeekAhead) !== "undefined") {
this.player.seekTo(position, allowSeekAhead);
} else {
this.player.seekTo(position);
}
};
YouTubeSandbox.prototype.setVolume = function(volume) {
this.player.setVolume(volume);
};
YouTubeSandbox.prototype.startDetectSeek = function() {
var that = this;
var checkSeek = function() {
if (!that.player) {
return;
}
var now = new Date();
var time = that.player.getCurrentTime();
that.postMessage("youtube.position", {"position": time});
if (that.prevTime === null) {
that.prevTime = time;
}
if (that.prevNow === null) {
that.prevNow = now;
}
var deltaTime = Math.abs(time - that.prevTime);
var deltaNow = (now - that.prevNow) * 0.001;
if (deltaTime > deltaNow * 1.1) {
that.postMessage("youtube.event", {"event": "youtube.seeked", "position": time});
}
that.prevNow = now;
that.prevTime = time;
};
if (!this.seekDetector) {
this.seekDetector = this.window.setInterval(function() {
checkSeek();
}, 1000);
}
checkSeek();
};
YouTubeSandbox.prototype.stopDetectSeek = function() {
if (this.seekDetector) {
this.window.clearInterval(this.seekDetector);
this.seekDetector = null;
}
this.prevNow = null;
};
var sandbox = new YouTubeSandbox(window);
window.onYouTubeIframeAPIReady = function() {
sandbox.onYouTubeIframeAPIReady();
};
window.addEventListener("message", function(event) {
if (event.origin !== PARENT_ORIGIN) {
// only accept messages from spreed-webrtc
return;
}
var msg = event.data;
var data = msg[msg.type] || {};
switch (msg.type) {
case "loadApi":
sandbox.loadApi(data.url);
break;
case "loadPlayer":
sandbox.loadPlayer(data);
break;
case "destroyPlayer":
sandbox.destroyPlayer();
break;
case "loadVideo":
sandbox.loadVideo(data.id, data.position);
break;
case "playVideo":
sandbox.playVideo();
break;
case "pauseVideo":
sandbox.pauseVideo();
break;
case "stopVideo":
sandbox.stopVideo();
break;
case "seekTo":
sandbox.seekTo(data.position, data.allowSeekAhead);
break;
case "setVolume":
sandbox.setVolume(data.volume);
break;
default:
console.log("Unknown message received", event);
break;
}
}, false);
console.log("YouTube sandbox ready.");
})();

66
static/js/services/sandbox.js

@ -0,0 +1,66 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
"use strict";
define(["jquery", "underscore"], function($, _) {
return ["$window", function($window) {
var Sandbox = function(iframe, template) {
this.iframe = iframe;
this.iframe.src = "data:text/html;charset=utf-8," + encodeURI(template);
this.target = this.iframe.contentWindow;
this.e = $({});
this.handler = _.bind(this.onPostMessageReceived, this);
$window.addEventListener("message", this.handler, false);
};
Sandbox.prototype.destroy = function() {
if (this.handler) {
$window.removeEventListener("message", this.handler, false);
this.handler = null;
}
};
Sandbox.prototype.onPostMessageReceived = function(event) {
if (event.origin !== "null" || event.source !== this.target) {
// the sandboxed data-url iframe has "null" as origin
return;
}
this.e.triggerHandler("message", [event]);
};
Sandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type}
msg[type] = message;
this.target.postMessage(msg, "*");
};
return {
createSandbox: function(iframe, template) {
return new Sandbox(iframe, template);
}
};
}];
});

9
static/js/services/services.js

@ -67,7 +67,8 @@ define([
'services/roompin', 'services/roompin',
'services/constraints', 'services/constraints',
'services/modules', 'services/modules',
'services/mediadevices'], function(_, 'services/mediadevices',
'services/sandbox'], function(_,
desktopNotify, desktopNotify,
playSound, playSound,
safeApply, safeApply,
@ -112,7 +113,8 @@ restURL,
roompin, roompin,
constraints, constraints,
modules, modules,
mediaDevices) { mediaDevices,
sandbox) {
var services = { var services = {
desktopNotify: desktopNotify, desktopNotify: desktopNotify,
@ -159,7 +161,8 @@ mediaDevices) {
roompin: roompin, roompin: roompin,
constraints: constraints, constraints: constraints,
modules: modules, modules: modules,
mediaDevices: mediaDevices mediaDevices: mediaDevices,
sandbox: sandbox
}; };
var initialize = function(angModule) { var initialize = function(angModule) {

226
static/partials/youtubevideo_sandbox.html

@ -21,230 +21,6 @@
</head> </head>
<body> <body>
<div id="youtubeplayer"></div> <div id="youtubeplayer"></div>
<script> <script src="__YOUTUBE_SANDBOX_JS_URL__" data-parent-origin="__PARENT_ORIGIN__"></script>
(function () {
var PARENT_ORIGIN = "__PARENT__ORIGIN__"; // will get replaced with real origin
var YouTubeSandbox = function(window) {
this.head = document.getElementsByTagName('head')[0];
this.window = window;
this.addedIframeScript = false;
this.player = null;
this.seekDetector = null;
this.prevTime = null;
this.prevNow = null;
};
YouTubeSandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type};
msg[type] = message;
this.window.parent.postMessage(msg, PARENT_ORIGIN);
};
YouTubeSandbox.prototype.onYouTubeIframeAPIReady = function() {
this.postMessage("youtube.apiReady", {"apiReady": true});
};
YouTubeSandbox.prototype.loadApi = function(url) {
if (!this.addedIframeScript) {
var that = this;
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;
script.onerror = function(evt) {
that.postMessage("youtube.error", {"msgid": "loadScriptFailed"});
that.head.removeChild(script);
that.addedIframeScript = false;
};
this.head.appendChild(script);
this.addedIframeScript = true;
}
};
YouTubeSandbox.prototype.loadPlayer = function(params) {
if (!this.player) {
var that = this;
var stateEvents = {
"-1": "youtube.unstarted",
"0": "youtube.ended",
"1": "youtube.playing",
"2": "youtube.paused",
"3": "youtube.buffering",
"5": "youtube.videocued"
};
var playerVars = params.playerVars || {};
delete playerVars["origin"];
this.player = new this.window.YT.Player("youtubeplayer", {
height: params.height || "390",
width: params.width || "640",
playerVars: playerVars,
events: {
"onReady": function(event) {
that.postMessage("youtube.volume", {"volume": that.player.getVolume()});
that.postMessage("youtube.playerReady", {"playerReady": true});
},
"onStateChange": function(event) {
var msg = stateEvents[event.data];
if (typeof msg === "undefined") {
console.warn("Unknown YouTube player state", event)
return;
}
switch (msg) {
case "youtube.playing":
that.prevTime = null;
that.startDetectSeek();
break;
case "youtube.buffering":
that.startDetectSeek();
break;
case "youtube.paused":
that.stopDetectSeek();
break;
case "youtube.ended":
that.stopDetectSeek();
break;
}
that.postMessage("youtube.event", {"event": msg, "state": event.data, "position": that.player.getCurrentTime()});
}
}
});
}
};
YouTubeSandbox.prototype.destroyPlayer = function() {
this.stopDetectSeek();
if (this.player) {
this.player.destroy();
this.player = null;
}
};
YouTubeSandbox.prototype.loadVideo = function(id, position) {
this.prevTime = null;
this.prevNow = null;
if (typeof(position) !== "undefined") {
this.player.loadVideoById(id, position);
} else {
this.player.loadVideoById(id);
}
};
YouTubeSandbox.prototype.playVideo = function() {
this.player.playVideo();
};
YouTubeSandbox.prototype.pauseVideo = function() {
this.player.pauseVideo();
};
YouTubeSandbox.prototype.stopVideo = function() {
this.player.stopVideo();
};
YouTubeSandbox.prototype.seekTo = function(position, allowSeekAhead) {
if (typeof(allowSeekAhead) !== "undefined") {
this.player.seekTo(position, allowSeekAhead);
} else {
this.player.seekTo(position);
}
};
YouTubeSandbox.prototype.setVolume = function(volume) {
this.player.setVolume(volume);
};
YouTubeSandbox.prototype.startDetectSeek = function() {
var that = this;
var checkSeek = function() {
if (!that.player) {
return;
}
var now = new Date();
var time = that.player.getCurrentTime();
that.postMessage("youtube.position", {"position": time});
if (that.prevTime === null) {
that.prevTime = time;
}
if (that.prevNow === null) {
that.prevNow = now;
}
var deltaTime = Math.abs(time - that.prevTime);
var deltaNow = (now - that.prevNow) * 0.001;
if (deltaTime > deltaNow * 1.1) {
that.postMessage("youtube.event", {"event": "youtube.seeked", "position": time});
}
that.prevNow = now;
that.prevTime = time;
};
if (!this.seekDetector) {
this.seekDetector = this.window.setInterval(function() {
checkSeek();
}, 1000);
}
checkSeek();
};
YouTubeSandbox.prototype.stopDetectSeek = function() {
if (this.seekDetector) {
this.window.clearInterval(this.seekDetector);
this.seekDetector = null;
}
this.prevNow = null;
};
var sandbox = new YouTubeSandbox(window);
window.onYouTubeIframeAPIReady = function() {
sandbox.onYouTubeIframeAPIReady();
};
window.addEventListener("message", function(event) {
if (event.origin !== PARENT_ORIGIN) {
// only accept messages from spreed-webrtc
return;
}
var msg = event.data;
var data = msg[msg.type] || {};
switch (msg.type) {
case "loadApi":
sandbox.loadApi(data.url);
break;
case "loadPlayer":
sandbox.loadPlayer(data);
break;
case "destroyPlayer":
sandbox.destroyPlayer();
break;
case "loadVideo":
sandbox.loadVideo(data.id, data.position);
break;
case "playVideo":
sandbox.playVideo();
break;
case "pauseVideo":
sandbox.pauseVideo();
break;
case "stopVideo":
sandbox.stopVideo();
break;
case "seekTo":
sandbox.seekTo(data.position, data.allowSeekAhead);
break;
case "setVolume":
sandbox.setVolume(data.volume);
break;
default:
console.log("Unknown message received", event);
break;
}
}, false);
})();
</script>
</body> </body>
</html> </html>

Loading…
Cancel
Save