diff --git a/build-player.js b/build-player.js
index 7383f61d..e062b2dc 100644
--- a/build-player.js
+++ b/build-player.js
@@ -8,6 +8,8 @@ var order = [
'youtube.coffee',
'dailymotion.coffee',
'videojs.coffee',
+ 'playerjs.coffee',
+ 'streamable.coffee',
'gdrive-player.coffee',
'raw-file.coffee',
'soundcloud.coffee',
diff --git a/package.json b/package.json
index 1bb6c863..44e0d44b 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
- "version": "3.48.0",
+ "version": "3.49.0",
"repository": {
"url": "http://github.com/calzoneman/sync"
},
diff --git a/player/playerjs.coffee b/player/playerjs.coffee
new file mode 100644
index 00000000..1f90a368
--- /dev/null
+++ b/player/playerjs.coffee
@@ -0,0 +1,89 @@
+window.PlayerJSPlayer = class PlayerJSPlayer extends Player
+ constructor: (data) ->
+ if not (this instanceof PlayerJSPlayer)
+ return new PlayerJSPlayer(data)
+
+ @load(data)
+
+ load: (data) ->
+ @setMediaProperties(data)
+ @ready = false
+ @finishing = false
+
+ if not data.meta.playerjs
+ throw new Error('Invalid input: missing meta.playerjs')
+
+ waitUntilDefined(window, 'playerjs', =>
+ iframe = $('')
+ .attr(src: data.meta.playerjs.src)
+
+ removeOld(iframe)
+
+ @player = new playerjs.Player(iframe[0])
+ @player.on('ready', =>
+ @player.on('error', (error) =>
+ console.error('PlayerJS error', error.stack)
+ )
+ @player.on('ended', ->
+ # Streamable seems to not implement this since it loops
+ # gotta use the timeupdate hack below
+ if CLIENT.leader
+ socket.emit('playNext')
+ )
+ @player.on('timeupdate', (time) =>
+ if time.duration - time.seconds < 1 and not @finishing
+ setTimeout(=>
+ if CLIENT.leader
+ socket.emit('playNext')
+ @pause()
+ , (time.duration - time.seconds) * 1000)
+ @finishing = true
+ )
+ @player.on('play', ->
+ @paused = false
+ if CLIENT.leader
+ sendVideoUpdate()
+ )
+ @player.on('pause', ->
+ @paused = true
+ if CLIENT.leader
+ sendVideoUpdate()
+ )
+
+ @player.setVolume(VOLUME * 100)
+
+ @ready = true
+ )
+ )
+
+ play: ->
+ @paused = false
+ if @player and @ready
+ @player.play()
+
+ pause: ->
+ @paused = true
+ if @player and @ready
+ @player.pause()
+
+ seekTo: (time) ->
+ if @player and @ready
+ @player.setCurrentTime(time)
+
+ setVolume: (volume) ->
+ if @player and @ready
+ @player.setVolume(volume * 100)
+
+ getTime: (cb) ->
+ if @player and @ready
+ @player.getCurrentTime(cb)
+ else
+ cb(0)
+
+ getVolume: (cb) ->
+ if @player and @ready
+ @player.getVolume((volume) ->
+ cb(volume / 100)
+ )
+ else
+ cb(VOLUME)
diff --git a/player/streamable.coffee b/player/streamable.coffee
new file mode 100644
index 00000000..c7f89fcf
--- /dev/null
+++ b/player/streamable.coffee
@@ -0,0 +1,12 @@
+window.StreamablePlayer = class StreamablePlayer extends PlayerJSPlayer
+ constructor: (data) ->
+ if not (this instanceof StreamablePlayer)
+ return new StreamablePlayer(data)
+
+ super(data)
+
+ load: (data) ->
+ data.meta.playerjs =
+ src: "https://streamable.com/e/#{data.id}"
+
+ super(data)
diff --git a/player/update.coffee b/player/update.coffee
index 6f17643b..7d1f44b3 100644
--- a/player/update.coffee
+++ b/player/update.coffee
@@ -17,7 +17,7 @@ TYPE_MAP =
im: ImgurPlayer
vm: VideoJSPlayer
hl: HLSPlayer
- sb: VideoJSPlayer
+ sb: StreamablePlayer
tc: VideoJSPlayer
cm: VideoJSPlayer
@@ -28,7 +28,7 @@ window.loadMediaPlayer = (data) ->
catch error
console.error error
- if data.meta.direct and data.type != 'gd'
+ if data.meta.direct and data.type is 'vi'
try
window.PLAYER = new VideoJSPlayer(data)
catch e
diff --git a/templates/channel.pug b/templates/channel.pug
index 5de098bd..a37db367 100644
--- a/templates/channel.pug
+++ b/templates/channel.pug
@@ -255,4 +255,5 @@ html(lang="en")
script(defer, src="/js/video.js")
script(defer, src="/js/videojs-contrib-hls.min.js")
script(defer, src="/js/videojs-resolution-switcher.js")
+ script(defer, src="/js/playerjs-0.0.12.js")
script(defer, src="https://player.twitch.tv/js/embed/v1.js")
diff --git a/www/js/player.js b/www/js/player.js
index 5b1ede70..70bb7243 100644
--- a/www/js/player.js
+++ b/www/js/player.js
@@ -1,5 +1,5 @@
(function() {
- var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, GoogleDrivePlayer, GoogleDriveYouTubePlayer, HLSPlayer, ImgurPlayer, LivestreamPlayer, Player, RTMPPlayer, SmashcastPlayer, SoundCloudPlayer, TYPE_MAP, TwitchPlayer, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, getSourceLabel, sortSources,
+ var CUSTOM_EMBED_WARNING, CustomEmbedPlayer, DEFAULT_ERROR, DailymotionPlayer, EmbedPlayer, FilePlayer, GoogleDrivePlayer, GoogleDriveYouTubePlayer, HLSPlayer, ImgurPlayer, LivestreamPlayer, Player, PlayerJSPlayer, RTMPPlayer, SmashcastPlayer, SoundCloudPlayer, StreamablePlayer, TYPE_MAP, TwitchPlayer, UstreamPlayer, VideoJSPlayer, VimeoPlayer, YouTubePlayer, codecToMimeType, genParam, getSourceLabel, sortSources,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
@@ -699,6 +699,139 @@
})(Player);
+ window.PlayerJSPlayer = PlayerJSPlayer = (function(superClass) {
+ extend(PlayerJSPlayer, superClass);
+
+ function PlayerJSPlayer(data) {
+ if (!(this instanceof PlayerJSPlayer)) {
+ return new PlayerJSPlayer(data);
+ }
+ this.load(data);
+ }
+
+ PlayerJSPlayer.prototype.load = function(data) {
+ this.setMediaProperties(data);
+ this.ready = false;
+ this.finishing = false;
+ if (!data.meta.playerjs) {
+ throw new Error('Invalid input: missing meta.playerjs');
+ }
+ return waitUntilDefined(window, 'playerjs', (function(_this) {
+ return function() {
+ var iframe;
+ iframe = $('').attr({
+ src: data.meta.playerjs.src
+ });
+ removeOld(iframe);
+ _this.player = new playerjs.Player(iframe[0]);
+ return _this.player.on('ready', function() {
+ _this.player.on('error', function(error) {
+ return console.error('PlayerJS error', error.stack);
+ });
+ _this.player.on('ended', function() {
+ if (CLIENT.leader) {
+ return socket.emit('playNext');
+ }
+ });
+ _this.player.on('timeupdate', function(time) {
+ if (time.duration - time.seconds < 1 && !_this.finishing) {
+ setTimeout(function() {
+ if (CLIENT.leader) {
+ socket.emit('playNext');
+ }
+ return _this.pause();
+ }, (time.duration - time.seconds) * 1000);
+ return _this.finishing = true;
+ }
+ });
+ _this.player.on('play', function() {
+ this.paused = false;
+ if (CLIENT.leader) {
+ return sendVideoUpdate();
+ }
+ });
+ _this.player.on('pause', function() {
+ this.paused = true;
+ if (CLIENT.leader) {
+ return sendVideoUpdate();
+ }
+ });
+ _this.player.setVolume(VOLUME * 100);
+ return _this.ready = true;
+ });
+ };
+ })(this));
+ };
+
+ PlayerJSPlayer.prototype.play = function() {
+ this.paused = false;
+ if (this.player && this.ready) {
+ return this.player.play();
+ }
+ };
+
+ PlayerJSPlayer.prototype.pause = function() {
+ this.paused = true;
+ if (this.player && this.ready) {
+ return this.player.pause();
+ }
+ };
+
+ PlayerJSPlayer.prototype.seekTo = function(time) {
+ if (this.player && this.ready) {
+ return this.player.setCurrentTime(time);
+ }
+ };
+
+ PlayerJSPlayer.prototype.setVolume = function(volume) {
+ if (this.player && this.ready) {
+ return this.player.setVolume(volume * 100);
+ }
+ };
+
+ PlayerJSPlayer.prototype.getTime = function(cb) {
+ if (this.player && this.ready) {
+ return this.player.getCurrentTime(cb);
+ } else {
+ return cb(0);
+ }
+ };
+
+ PlayerJSPlayer.prototype.getVolume = function(cb) {
+ if (this.player && this.ready) {
+ return this.player.getVolume(function(volume) {
+ return cb(volume / 100);
+ });
+ } else {
+ return cb(VOLUME);
+ }
+ };
+
+ return PlayerJSPlayer;
+
+ })(Player);
+
+ window.StreamablePlayer = StreamablePlayer = (function(superClass) {
+ extend(StreamablePlayer, superClass);
+
+ function StreamablePlayer(data) {
+ if (!(this instanceof StreamablePlayer)) {
+ return new StreamablePlayer(data);
+ }
+ StreamablePlayer.__super__.constructor.call(this, data);
+ }
+
+ StreamablePlayer.prototype.load = function(data) {
+ data.meta.playerjs = {
+ src: "https://streamable.com/e/" + data.id
+ };
+ return StreamablePlayer.__super__.load.call(this, data);
+ };
+
+ return StreamablePlayer;
+
+ })(PlayerJSPlayer);
+
window.GoogleDrivePlayer = GoogleDrivePlayer = (function(superClass) {
extend(GoogleDrivePlayer, superClass);
@@ -1553,7 +1686,7 @@
im: ImgurPlayer,
vm: VideoJSPlayer,
hl: HLSPlayer,
- sb: VideoJSPlayer,
+ sb: StreamablePlayer,
tc: VideoJSPlayer,
cm: VideoJSPlayer
};
@@ -1568,7 +1701,7 @@
error = error1;
console.error(error);
}
- if (data.meta.direct && data.type !== 'gd') {
+ if (data.meta.direct && data.type === 'vi') {
try {
return window.PLAYER = new VideoJSPlayer(data);
} catch (error1) {
diff --git a/www/js/playerjs-0.0.12.js b/www/js/playerjs-0.0.12.js
new file mode 100644
index 00000000..f91b54fa
--- /dev/null
+++ b/www/js/playerjs-0.0.12.js
@@ -0,0 +1,1187 @@
+/*! Player.js - v0.0.12 - 2016-10-20
+* http://github.com/embedly/player.js
+* Copyright (c) 2016 Embedly; Licensed BSD */
+(function(window, document){
+var playerjs = {};
+
+playerjs.DEBUG = false;
+playerjs.VERSION = '0.0.11';
+playerjs.CONTEXT = 'player.js';
+playerjs.POST_MESSAGE = !!window.postMessage;
+
+/*
+* Utils.
+*/
+playerjs.origin = function(url){
+ // Grab the origin of a URL
+ if (url.substr(0, 2) === '//'){
+ url = window.location.protocol + url;
+ }
+
+ return url.split('/').slice(0,3).join('/');
+};
+
+playerjs.addEvent = function(elem, type, eventHandle) {
+ if (!elem) { return; }
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ } else {
+ elem["on"+type]=eventHandle;
+ }
+};
+
+// usage: log('inside coolFunc',this,arguments);
+// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
+playerjs.log = function(){
+ playerjs.log.history = playerjs.log.history || []; // store logs to an array for reference
+ playerjs.log.history.push(arguments);
+ if(window.console && playerjs.DEBUG){
+ window.console.log( Array.prototype.slice.call(arguments) );
+ }
+};
+
+// isFunctions
+playerjs.isString = function (obj) {
+ return Object.prototype.toString.call(obj) === '[object String]';
+};
+
+playerjs.isObject = function(obj){
+ return Object.prototype.toString.call(obj) === "[object Object]";
+};
+
+playerjs.isArray = function(obj){
+ return Object.prototype.toString.call(obj) === "[object Array]";
+};
+
+playerjs.isNone = function(obj){
+ return (obj === null || obj === undefined);
+};
+
+playerjs.has = function(obj, key){
+ return Object.prototype.hasOwnProperty.call(obj, key);
+};
+
+// ie8 doesn't support indexOf in arrays, based on underscore.
+playerjs.indexOf = function(array, item) {
+ if (array == null){ return -1; }
+ var i = 0, length = array.length;
+ if (Array.prototype.IndexOf && array.indexOf === Array.prototype.IndexOf) {
+ return array.indexOf(item);
+ }
+ for (; i < length; i++) {
+ if (array[i] === item) { return i; }
+ }
+ return -1;
+};
+
+// Assert
+playerjs.assert = function(test, msg) {
+ if (!test) {
+ throw msg || "Player.js Assert Failed";
+ }
+};
+/*
+* Keeper is just a method for keeping track of all the callbacks.
+*/
+
+playerjs.Keeper = function(){
+ this.init();
+};
+
+playerjs.Keeper.prototype.init = function(){
+ this.data = {};
+};
+
+playerjs.Keeper.prototype.getUUID = function(){
+ // Create a random id. #http://stackoverflow.com/a/2117523/564191
+ return 'listener-xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+};
+
+playerjs.Keeper.prototype.has = function(event, id){
+ if (!this.data.hasOwnProperty(event)){
+ return false;
+ }
+
+ if (playerjs.isNone(id)){
+ return true;
+ }
+
+ // Figure out if we have the event.
+ var events = this.data[event];
+
+ for (var i = 0; i < events.length; i++){
+ if (events[i].id === id){
+ return true;
+ }
+ }
+
+ return false;
+};
+
+playerjs.Keeper.prototype.add = function(id, event, cb, ctx, one){
+ var d = {
+ id: id,
+ event: event,
+ cb: cb,
+ ctx: ctx,
+ one: one
+ };
+
+ if (this.has(event)){
+ this.data[event].push(d);
+ } else {
+ this.data[event] = [d];
+ }
+};
+
+playerjs.Keeper.prototype.execute = function(event, id, data, ctx){
+ if (!this.has(event, id)){
+ return false;
+ }
+
+ var keep = [],
+ execute = [];
+
+ for (var i=0; i< this.data[event].length; i++){
+ var d = this.data[event][i];
+
+ // There are omni events, in that they do not have an id. i.e "ready".
+ // Or there is an ID and we only want to execute the right id'd method.
+ if (playerjs.isNone(id) || (!playerjs.isNone(id) && d.id === id )){
+
+ execute.push({
+ cb: d.cb,
+ ctx: d.ctx? d.ctx: ctx,
+ data: data
+ });
+
+ // If we only wanted to execute this once.
+ if (d.one === false){
+ keep.push(d);
+ }
+ } else {
+ keep.push(d);
+ }
+ }
+
+ if (keep.length === 0){
+ delete this.data[event];
+ } else {
+ this.data[event] = keep;
+ }
+
+ // We need to execute everything after we deal with the one stuff. otherwise
+ // we have issues to order of operations.
+ for (var n=0; n < execute.length; n++){
+ var e = execute[n];
+ e.cb.call(e.ctx, e.data);
+ }
+};
+
+playerjs.Keeper.prototype.on = function(id, event, cb, ctx){
+ this.add(id, event, cb, ctx, false);
+};
+
+playerjs.Keeper.prototype.one = function(id, event, cb, ctx){
+ this.add(id, event, cb, ctx, true);
+};
+
+playerjs.Keeper.prototype.off = function(event, cb){
+ // We should probably restructure so this is a bit less of a pain.
+ var listeners = [];
+
+ if (!this.data.hasOwnProperty(event)){
+ return listeners;
+ }
+
+ var keep = [];
+
+ // Loop through everything.
+ for (var i=0; i< this.data[event].length; i++){
+ var data = this.data[event][i];
+ // If we only keep if there was a CB and the CB is there.
+ if (!playerjs.isNone(cb) && data.cb !== cb) {
+ keep.push(data);
+ } else if (!playerjs.isNone(data.id)) {
+ listeners.push(data.id);
+ }
+ }
+
+ if (keep.length === 0){
+ delete this.data[event];
+ } else {
+ this.data[event] = keep;
+ }
+
+ return listeners;
+};
+
+/*
+* Player.js is a javascript library for interacting with iframes via
+* postMessage that use an Open Player Spec
+*
+*/
+
+playerjs.Player = function(elem, options){
+ if (!(this instanceof playerjs.Player)) {
+ return new playerjs.Player(elem, options);
+ }
+ this.init(elem, options);
+};
+
+playerjs.EVENTS = {
+ READY: 'ready',
+ PLAY: 'play',
+ PAUSE: 'pause',
+ ENDED: 'ended',
+ TIMEUPDATE: 'timeupdate',
+ PROGRESS: 'progress',
+ ERROR: 'error'
+};
+
+playerjs.EVENTS.all = function(){
+ var all = [];
+ for (var key in playerjs.EVENTS) {
+ if (playerjs.has(playerjs.EVENTS, key) && playerjs.isString(playerjs.EVENTS[key])) {
+ all.push(playerjs.EVENTS[key]);
+ }
+ }
+ return all;
+};
+
+playerjs.METHODS = {
+ PLAY: 'play',
+ PAUSE: 'pause',
+ GETPAUSED: 'getPaused',
+ MUTE: 'mute',
+ UNMUTE: 'unmute',
+ GETMUTED: 'getMuted',
+ SETVOLUME: 'setVolume',
+ GETVOLUME: 'getVolume',
+ GETDURATION: 'getDuration',
+ SETCURRENTTIME: 'setCurrentTime',
+ GETCURRENTTIME:'getCurrentTime',
+ SETLOOP: 'setLoop',
+ GETLOOP: 'getLoop',
+ REMOVEEVENTLISTENER: 'removeEventListener',
+ ADDEVENTLISTENER: 'addEventListener'
+};
+
+playerjs.METHODS.all = function(){
+ var all = [];
+ for (var key in playerjs.METHODS) {
+ if (playerjs.has(playerjs.METHODS, key) && playerjs.isString(playerjs.METHODS[key])) {
+ all.push(playerjs.METHODS[key]);
+ }
+ }
+ return all;
+};
+
+playerjs.READIED = [];
+
+playerjs.Player.prototype.init = function(elem, options){
+
+ var self = this;
+
+ if (playerjs.isString(elem)){
+ elem = document.getElementById(elem);
+ }
+
+ this.elem = elem;
+
+ // Figure out the origin of where we are sending messages.
+ this.origin = playerjs.origin(elem.src);
+
+ // Event handling.
+ this.keeper = new playerjs.Keeper();
+
+ // Queuing before ready.
+ this.isReady = false;
+ this.queue = [];
+
+ // Assume that everything is supported, unless we know otherwise.
+ this.events = playerjs.EVENTS.all();
+ this.methods = playerjs.METHODS.all();
+
+ if (playerjs.POST_MESSAGE){
+ // Set up the reciever.
+ playerjs.addEvent(window, 'message', function(e){
+ self.receive(e);
+ });
+ } else {
+ playerjs.log('Post Message is not Available.');
+ }
+
+ // See if we caught the src event first, otherwise assume we haven't loaded
+ if (playerjs.indexOf(playerjs.READIED, elem.src) > -1){
+ self.loaded = true;
+ } else {
+ // Try the onload event, just lets us give another test.
+ this.elem.onload = function(){
+ self.loaded = true;
+ };
+ }
+};
+
+playerjs.Player.prototype.send = function(data, callback, ctx){
+ // Add the context and version to the data.
+ data.context = playerjs.CONTEXT;
+ data.version = playerjs.VERSION;
+
+ // We are expecting a response.
+ if (callback) {
+ // Create a UUID
+ var id = this.keeper.getUUID();
+
+ // Set the listener.
+ data.listener = id;
+
+ // Only hang on to this listener once.
+ this.keeper.one(id, data.method, callback, ctx);
+ }
+
+ if (!this.isReady && data.value !== 'ready'){
+ playerjs.log('Player.queue', data);
+ this.queue.push(data);
+ return false;
+ }
+
+ playerjs.log('Player.send', data, this.origin);
+
+ if (this.loaded === true){
+ this.elem.contentWindow.postMessage(JSON.stringify(data), this.origin);
+ }
+
+ return true;
+};
+
+playerjs.Player.prototype.receive = function(e){
+ playerjs.log('Player.receive', e);
+
+ if (e.origin !== this.origin){
+ return false;
+ }
+
+ var data;
+ try {
+ data = JSON.parse(e.data);
+ } catch (err){
+ // Not a valid response.
+ return false;
+ }
+
+ // abort if this message wasn't a player.js message
+ if (data.context !== playerjs.CONTEXT) {
+ return false;
+ }
+
+ // We need to determine if we are ready.
+ if (data.event === 'ready' && data.value && data.value.src === this.elem.src){
+ this.ready(data);
+ }
+
+ if (this.keeper.has(data.event, data.listener)){
+ this.keeper.execute(data.event, data.listener, data.value, this);
+ }
+};
+
+
+playerjs.Player.prototype.ready = function(data){
+
+ if (this.isReady === true){
+ return false;
+ }
+
+ // If we got a list of supported methods, we should set them.
+ if (data.value.events){
+ this.events = data.value.events;
+ }
+ if (data.value.methods){
+ this.methods = data.value.methods;
+ }
+
+ // set ready.
+ this.isReady = true;
+ this.loaded = true;
+
+ // Clear the queue
+ for (var i=0; i 0) {
+ for (var i in listeners){
+ this.send({
+ method: 'removeEventListener',
+ value: event,
+ listener: listeners[i]
+ });
+ return true;
+ }
+ }
+
+ return false;
+};
+
+// Based on what ready passed back, we can determine if the events/method are
+// supported by the player.
+playerjs.Player.prototype.supports = function(evtOrMethod, names){
+
+ playerjs.assert(playerjs.indexOf(['method', 'event'], evtOrMethod) > -1,
+ 'evtOrMethod needs to be either "event" or "method" got ' + evtOrMethod);
+
+ // Make everything an array.
+ names = playerjs.isArray(names) ? names : [names];
+
+ var all = evtOrMethod === 'event' ? this.events : this.methods;
+
+ for (var i=0; i < names.length; i++){
+ if (playerjs.indexOf(all, names[i]) === -1){
+ return false;
+ }
+ }
+
+ return true;
+};
+
+//create function to add to the Player prototype
+function createPrototypeFunction(name) {
+
+ return function() {
+
+ var data = {
+ method: name
+ };
+
+ var args = Array.prototype.slice.call(arguments);
+
+ //for getters add the passed parameters to the arguments for the send call
+ if (/^get/.test(name)) {
+ playerjs.assert(args.length > 0, 'Get methods require a callback.');
+ args.unshift(data);
+ } else {
+ //for setter add the first arg to the value field
+ if (/^set/.test(name)) {
+ playerjs.assert(args.length !== 0, 'Set methods require a value.');
+ data.value = args[0];
+ }
+ args = [data];
+ }
+
+ this.send.apply(this, args);
+ };
+}
+
+// Loop through the methods to add them to the prototype.
+for (var i = 0, l = playerjs.METHODS.all().length; i < l; i++) {
+ var methodName = playerjs.METHODS.all()[i];
+
+ // We don't want to overwrite existing methods.
+ if (!playerjs.Player.prototype.hasOwnProperty(methodName)){
+ playerjs.Player.prototype[methodName] = createPrototypeFunction(methodName);
+ }
+}
+
+// We need to catch all ready events in case the iframe is ready before the
+// player is invoked.
+playerjs.addEvent(window, 'message', function(e){
+ var data;
+ try {
+ data = JSON.parse(e.data);
+ } catch (err){
+ return false;
+ }
+
+ // abort if this message wasn't a player.js message
+ if (data.context !== playerjs.CONTEXT) {
+ return false;
+ }
+
+ // We need to determine if we are ready.
+ if (data.event === 'ready' && data.value && data.value.src){
+ playerjs.READIED.push(data.value.src);
+ }
+});
+
+/*
+* Does all the wiring up for the backend.
+*
+* var receiver = new playerjs.Receiver();
+* receiver.on('play', function(){ video.play() });
+* receiver.on('getDuration', function(callback){ callback(video.duration) });
+* receiver.emit('timeupdate', {});
+*/
+
+playerjs.Receiver = function(events, methods){
+ this.init(events, methods);
+};
+
+playerjs.Receiver.prototype.init = function(events, methods){
+ var self = this;
+
+ // Deal with the ready crap.
+ this.isReady = false;
+
+ // Bind the window message.
+ this.origin = playerjs.origin(document.referrer);
+
+ //Create a holder for all the methods.
+ this.methods = {};
+
+ // holds all the information about what's supported
+ this.supported = {
+ events: events ? events : playerjs.EVENTS.all(),
+ methods: methods ? methods : playerjs.METHODS.all()
+ };
+
+ // Deals with the adding and removing of event listeners.
+ this.eventListeners = {};
+
+ // We can't send any messages.
+ this.reject = !(window.self !== window.top && playerjs.POST_MESSAGE);
+
+ // We aren't in an iframe, don't listen.
+ if (!this.reject){
+ playerjs.addEvent(window, 'message', function(e){
+ self.receive(e);
+ });
+ }
+};
+
+playerjs.Receiver.prototype.receive = function(e){
+ // Only want to listen to events that came from our origin.
+ if (e.origin !== this.origin){
+ return false;
+ }
+
+ // Browsers that support postMessage also support JSON.
+ var data = {};
+ if (playerjs.isObject(e.data)){
+ data = e.data;
+ } else {
+ try {
+ data = window.JSON.parse(e.data);
+ } catch (err){
+ playerjs.log('JSON Parse Error', err);
+ }
+ }
+
+ playerjs.log('Receiver.receive', e, data);
+
+ // Nothing for us to do.
+ if (!data.method){
+ return false;
+ }
+
+ // make sure the context is correct.
+ if (data.context !== playerjs.CONTEXT){
+ return false;
+ }
+
+ // Make sure we have a valid method.
+ if (playerjs.indexOf(playerjs.METHODS.all(), data.method) === -1){
+ this.emit('error', {
+ code: 2,
+ msg: 'Invalid Method "'+data.method+'"'
+ });
+ return false;
+ }
+
+ // See if we added a listener
+ var listener = !playerjs.isNone(data.listener) ? data.listener : null;
+
+ // Add Event Listener.
+ if (data.method === 'addEventListener') {
+ if (this.eventListeners.hasOwnProperty(data.value)) {
+ //If the listener is the same, i.e. null only add it once.
+ if (playerjs.indexOf(this.eventListeners[data.value], listener) === -1){
+ this.eventListeners[data.value].push(listener);
+ }
+ } else {
+ this.eventListeners[data.value] = [listener];
+ }
+
+ if (data.value === 'ready' && this.isReady){
+ this.ready();
+ }
+ }
+ // Remove the event listener.
+ else if (data.method === 'removeEventListener') {
+ if (this.eventListeners.hasOwnProperty(data.value)) {
+ var index = playerjs.indexOf(this.eventListeners[data.value], listener);
+
+ // if we find the element, remove it.
+ if (index > -1){
+ this.eventListeners[data.value].splice(index, 1);
+ }
+
+ if (this.eventListeners[data.value].length === 0){
+ delete this.eventListeners[data.value];
+ }
+ }
+ }
+ // Go get it.
+ else {
+ this.get(data.method, data.value, listener);
+ }
+};
+
+playerjs.Receiver.prototype.get = function(method, value, listener){
+ var self = this;
+
+ // Now lets do it.
+ if (!this.methods.hasOwnProperty(method)){
+ this.emit('error', {
+ code: 3,
+ msg: 'Method Not Supported"'+method+'"'
+ });
+ return false;
+ }
+
+ var func = this.methods[method];
+
+ if (method.substr(0,3) === 'get') {
+ var callback = function(val){
+ self.send(method, val, listener);
+ };
+ func.call(this, callback);
+ } else {
+ func.call(this, value);
+ }
+};
+
+playerjs.Receiver.prototype.on = function(event, callback){
+ this.methods[event] = callback;
+};
+
+playerjs.Receiver.prototype.send = function(event, value, listener){
+
+ playerjs.log('Receiver.send', event, value, listener);
+
+ if (this.reject){
+ // We are not in a frame, or we don't support POST_MESSAGE
+ playerjs.log('Receiver.send.reject', event, value, listener);
+ return false;
+ }
+
+ var data = {
+ context: playerjs.CONTEXT,
+ version: playerjs.VERSION,
+ event: event
+ };
+
+ if (!playerjs.isNone(value)){
+ data.value = value;
+ }
+
+ if (!playerjs.isNone(listener)){
+ data.listener = listener;
+ }
+
+ var msg = JSON.stringify(data);
+ window.parent.postMessage(msg, this.origin === "" ? '*' : this.origin);
+};
+
+playerjs.Receiver.prototype.emit = function(event, value){
+
+ if (!this.eventListeners.hasOwnProperty(event)){
+ return false;
+ }
+
+ playerjs.log('Instance.emit', event, value, this.eventListeners[event]);
+
+ for (var i=0; i < this.eventListeners[event].length; i++){
+ var listener = this.eventListeners[event][i];
+ this.send(event, value, listener);
+ }
+
+ return true;
+};
+
+playerjs.Receiver.prototype.ready = function(){
+ playerjs.log('Receiver.ready');
+ this.isReady = true;
+
+ var data = {
+ src: window.location.toString(),
+ events: this.supported.events,
+ methods: this.supported.methods
+ };
+
+ if (!this.emit('ready', data)){
+ this.send('ready', data);
+ }
+
+};
+
+playerjs.HTML5Adapter = function(video){
+ if (!(this instanceof playerjs.HTML5Adapter)) {
+ return new playerjs.HTML5Adapter(video);
+ }
+ this.init(video);
+};
+
+playerjs.HTML5Adapter.prototype.init = function(video){
+
+ playerjs.assert(video, 'playerjs.VideoJSReceiver requires a video element');
+
+ // Set up the actual receiver
+ var receiver = this.receiver = new playerjs.Receiver();
+
+ /* EVENTS */
+ video.addEventListener('playing', function(){
+ receiver.emit('play');
+ });
+
+ video.addEventListener('pause', function(){
+ receiver.emit('pause');
+ });
+
+ video.addEventListener('ended', function(){
+ receiver.emit('ended');
+ });
+
+ video.addEventListener('timeupdate', function(){
+ receiver.emit('timeupdate', {
+ seconds: video.currentTime,
+ duration: video.duration
+ });
+ });
+
+ video.addEventListener('progress', function(){
+ receiver.emit('buffered', {
+ percent: video.buffered.length
+ });
+ });
+
+ /* Methods */
+ receiver.on('play', function(){
+ video.play();
+ });
+
+ receiver.on('pause', function(){
+ video.pause();
+ });
+
+ receiver.on('getPaused', function(callback){
+ callback(video.paused);
+ });
+
+ receiver.on('getCurrentTime', function(callback){
+ callback(video.currentTime);
+ });
+
+ receiver.on('setCurrentTime', function(value){
+ video.currentTime = value;
+ });
+
+ receiver.on('getDuration', function(callback){
+ callback(video.duration);
+ });
+
+ receiver.on('getVolume', function(callback){
+ callback(video.volume * 100);
+ });
+
+ receiver.on('setVolume', function(value){
+ video.volume = value/100;
+ });
+
+ receiver.on('mute', function(){
+ video.muted = true;
+ });
+
+ receiver.on('unmute', function(){
+ video.muted = false;
+ });
+
+ receiver.on('getMuted', function(callback){
+ callback(video.muted);
+ });
+
+ receiver.on('getLoop', function(callback){
+ callback(video.loop);
+ });
+
+ receiver.on('setLoop', function(value){
+ video.loop = value;
+ });
+};
+
+/* Call when the video has loaded */
+playerjs.HTML5Adapter.prototype.ready = function(){
+ this.receiver.ready();
+};
+//http://www.longtailvideo.com/support/jw-player/28851/javascript-api-reference
+playerjs.JWPlayerAdapter = function(player){
+ if (!(this instanceof playerjs.JWPlayerAdapter)) {
+ return new playerjs.JWPlayerAdapter(player);
+ }
+ this.init(player);
+};
+
+playerjs.JWPlayerAdapter.prototype.init = function(player){
+
+ playerjs.assert(player, 'playerjs.JWPlayerAdapter requires a player object');
+
+ // Set up the actual receiver
+ var receiver = this.receiver = new playerjs.Receiver();
+
+ // JWPlayer doesn't have a seLoop, so we can do it ourself.
+ this.looped = false;
+
+ /* EVENTS */
+ player.onPause(function(){
+ receiver.emit('pause');
+ });
+
+ player.onPlay(function(){
+ receiver.emit('play');
+ });
+
+ player.onTime(function(e){
+ var seconds = e.position,
+ duration = e.duration;
+
+ if (!seconds || !duration){
+ return false;
+ }
+
+ var value = {
+ seconds: seconds,
+ duration: duration
+ };
+ receiver.emit('timeupdate', value);
+ });
+
+ var self = this;
+ player.onComplete(function(){
+ // Fake the looping
+ if (self.looped === true){
+ // By default jwplayer seeks after play.
+ player.seek(0);
+ } else {
+ // Else throw the ended event.
+ receiver.emit('ended');
+ }
+ });
+
+ player.onError(function(){
+ receiver.emit('error');
+ });
+
+
+ /* METHODS */
+ receiver.on('play', function(){
+ player.play(true);
+ });
+
+ receiver.on('pause', function(){
+ player.pause(true);
+ });
+
+ receiver.on('getPaused', function(callback){
+ callback(player.getState().toLowerCase() !== 'PLAYING'.toLowerCase());
+ });
+
+ receiver.on('getCurrentTime', function(callback){
+ callback(player.getPosition());
+ });
+
+ receiver.on('setCurrentTime', function(value){
+ player.seek(value);
+ });
+
+ receiver.on('getDuration', function(callback){
+ callback(player.getDuration());
+ });
+
+ receiver.on('getVolume', function(callback){
+ callback(player.getVolume());
+ });
+
+ receiver.on('setVolume', function(value){
+ player.setVolume(value);
+ });
+
+ receiver.on('mute', function(){
+ player.setMute(true);
+ });
+
+ receiver.on('unmute', function(){
+ player.setMute(false);
+ });
+
+ receiver.on('getMuted', function(callback){
+ callback(player.getMute() === true);
+ });
+
+ receiver.on('getLoop', function(callback){
+ callback(this.looped);
+ }, this);
+
+ receiver.on('setLoop', function(value){
+ this.looped = value;
+ }, this);
+};
+
+/* Call when the video.js is ready */
+playerjs.JWPlayerAdapter.prototype.ready = function(){
+ this.receiver.ready();
+};
+
+playerjs.MockAdapter = function(){
+ if (!(this instanceof playerjs.MockAdapter)) {
+ return new playerjs.MockAdapter();
+ }
+ this.init();
+};
+
+playerjs.MockAdapter.prototype.init = function(){
+
+ // Our mock video
+ var video = {
+ duration: 20,
+ currentTime: 0,
+ interval: null,
+ timeupdate: function(){},
+ volume: 100,
+ mute: false,
+ playing: false,
+ loop : false,
+ play: function(){
+ video.interval = setInterval(function(){
+ video.currentTime += 0.25;
+ video.timeupdate({
+ seconds: video.currentTime,
+ duration: video.duration
+ });
+ }, 250);
+ video.playing = true;
+ },
+ pause: function(){
+ clearInterval(video.interval);
+ video.playing = false;
+ }
+ };
+
+ // Set up the actual receiver
+ var receiver = this.receiver = new playerjs.Receiver();
+
+ receiver.on('play', function(){
+ var self = this;
+ video.play();
+ this.emit('play');
+ video.timeupdate = function(data){
+ self.emit('timeupdate', data);
+ };
+ });
+
+ receiver.on('pause', function(){
+ video.pause();
+ this.emit('pause');
+ });
+
+ receiver.on('getPaused', function(callback){
+ callback(!video.playing);
+ });
+
+ receiver.on('getCurrentTime', function(callback){
+ callback(video.currentTime);
+ });
+
+ receiver.on('setCurrentTime', function(value){
+ video.currentTime = value;
+ });
+
+ receiver.on('getDuration', function(callback){
+ callback(video.duration);
+ });
+
+ receiver.on('getVolume', function(callback){
+ callback(video.volume);
+ });
+
+ receiver.on('setVolume', function(value){
+ video.volume = value;
+ });
+
+ receiver.on('mute', function(){
+ video.mute = true;
+ });
+
+ receiver.on('unmute', function(){
+ video.mute = false;
+ });
+
+ receiver.on('getMuted', function(callback){
+ callback(video.mute);
+ });
+
+ receiver.on('getLoop', function(callback){
+ callback(video.loop);
+ });
+
+ receiver.on('setLoop', function(value){
+ video.loop = value;
+ });
+};
+
+/* Call when the video has loaded */
+playerjs.MockAdapter.prototype.ready = function(){
+ this.receiver.ready();
+};
+playerjs.VideoJSAdapter = function(player){
+ if (!(this instanceof playerjs.VideoJSAdapter)) {
+ return new playerjs.VideoJSAdapter(player);
+ }
+ this.init(player);
+};
+
+playerjs.VideoJSAdapter.prototype.init = function(player){
+
+ playerjs.assert(player, 'playerjs.VideoJSReceiver requires a player object');
+
+ // Set up the actual receiver
+ var receiver = this.receiver = new playerjs.Receiver();
+
+ /* EVENTS */
+ player.on("pause", function(){
+ receiver.emit('pause');
+ });
+
+ player.on("play", function(){
+ receiver.emit('play');
+ });
+
+ player.on("timeupdate", function(e){
+ var seconds = player.currentTime(),
+ duration = player.duration();
+
+ if (!seconds || !duration){
+ return false;
+ }
+
+ var value = {
+ seconds: seconds,
+ duration: duration
+ };
+ receiver.emit('timeupdate', value);
+ });
+
+ player.on("ended", function(){
+ receiver.emit('ended');
+ });
+
+ player.on("error", function(){
+ receiver.emit('error');
+ });
+
+
+ /* METHODS */
+ receiver.on('play', function(){
+ player.play();
+ });
+
+ receiver.on('pause', function(){
+ player.pause();
+ });
+
+ receiver.on('getPaused', function(callback){
+ callback(player.paused());
+ });
+
+ receiver.on('getCurrentTime', function(callback){
+ callback(player.currentTime());
+ });
+
+ receiver.on('setCurrentTime', function(value){
+ player.currentTime(value);
+ });
+
+ receiver.on('getDuration', function(callback){
+ callback(player.duration());
+ });
+
+ receiver.on('getVolume', function(callback){
+ callback(player.volume() * 100);
+ });
+
+ receiver.on('setVolume', function(value){
+ player.volume(value/100);
+ });
+
+ receiver.on('mute', function(){
+ player.volume(0);
+ });
+
+ receiver.on('unmute', function(){
+ player.volume(1);
+ });
+
+ receiver.on('getMuted', function(callback){
+ callback(player.volume() === 0);
+ });
+
+ receiver.on('getLoop', function(callback){
+ callback(player.loop());
+ });
+
+ receiver.on('setLoop', function(value){
+ player.loop(value);
+ });
+};
+
+/* Call when the video.js is ready */
+playerjs.VideoJSAdapter.prototype.ready = function(){
+ this.receiver.ready();
+};
+
+ if (typeof define === 'function' && define.amd) {
+ define(function () {
+ return playerjs
+ })
+ } else if (typeof module === 'object' && module.exports) {
+ module.exports = playerjs
+ } else {
+ window.playerjs = playerjs;
+ }
+})(window, document);