Merge branch 'player-rewrite' into 3.0

This commit is contained in:
calzoneman 2015-07-06 11:28:30 -07:00
commit 83e3c44a6d
43 changed files with 3573 additions and 2720 deletions

29
build-player.js Normal file
View File

@ -0,0 +1,29 @@
var coffee = require('coffee-script');
var fs = require('fs');
var path = require('path');
var order = [
'base.coffee',
'vimeo.coffee',
'youtube.coffee',
'dailymotion.coffee',
'videojs.coffee',
'raw-file.coffee',
'soundcloud.coffee',
'embed.coffee',
'twitch.coffee',
'livestream.com.coffee',
'custom-embed.coffee',
'rtmp.coffee',
'hitbox.coffee',
'ustream.coffee',
'imgur.coffee',
'update.coffee'
];
var buffer = '';
order.forEach(function (file) {
buffer += fs.readFileSync(path.join('player', file)) + '\n';
});
fs.writeFileSync(path.join('www', 'js', 'player.js'), coffee.compile(buffer));

View File

@ -1,3 +1,4 @@
var Vimeo = require("cytube-mediaquery/lib/provider/vimeo");
var ChannelModule = require("./module");
var Config = require("../config");
var InfoGetter = require("../get-info");
@ -7,6 +8,7 @@ function MediaRefresherModule(channel) {
ChannelModule.apply(this, arguments);
this._interval = false;
this._media = null;
this._playlist = channel.modules.playlist;
}
MediaRefresherModule.prototype = Object.create(ChannelModule.prototype);
@ -15,18 +17,26 @@ MediaRefresherModule.prototype.onPreMediaChange = function (data, cb) {
if (this._interval) clearInterval(this._interval);
this._media = data;
var pl = this._playlist;
switch (data.type) {
case "gd":
pl._refreshing = true;
return this.initGoogleDocs(data, function () {
pl._refreshing = false;
cb(null, ChannelModule.PASSTHROUGH);
});
case "gp":
pl._refreshing = true;
return this.initGooglePlus(data, function () {
pl._refreshing = false;
cb(null, ChannelModule.PASSTHROUGH);
});
case "vi":
pl._refreshing = true;
return this.initVimeo(data, function () {
pl._refreshing = false;
cb(null, ChannelModule.PASSTHROUGH);
});
default:
@ -55,18 +65,20 @@ MediaRefresherModule.prototype.initVimeo = function (data, cb) {
var self = this;
self.channel.activeLock.lock();
InfoGetter.vimeoWorkaround(data.id, function (hack) {
if (self.dead || self.channel.dead) {
Vimeo.extract(data.id).then(function (direct) {
if (self.dead || self.channel.dead)
return;
}
if (self._media === data) {
data.meta.direct = direct;
self.channel.logger.log("[mediarefresher] Refreshed vimeo video with ID " +
data.id);
data.meta.direct = hack;
}
self.channel.activeLock.release();
if (cb) cb();
}).catch(function (err) {
Logger.errlog.log("Unexpected vimeo::extract() fail: " + err.stack);
if (cb) cb();
});
};

View File

@ -3,11 +3,11 @@ var AsyncQueue = require("../asyncqueue");
var Media = require("../media");
var util = require("../utilities");
var InfoGetter = require("../get-info");
var vimeoWorkaround = InfoGetter.vimeoWorkaround;
var Config = require("../config");
var Flags = require("../flags");
var db = require("../database");
var Logger = require("../logger");
var CustomEmbedFilter = require("../customembed").filter;
const MAX_ITEMS = Config.get("playlist.max-items");
@ -90,7 +90,7 @@ function PlaylistModule(channel) {
this._leadInterval = false;
this._lastUpdate = 0;
this._counter = 0;
this._gdRefreshTimer = false;
this._refreshing = false;
if (this.channel.modules.chat) {
this.channel.modules.chat.registerCommand("/clean", this.handleClean.bind(this));
@ -110,6 +110,16 @@ PlaylistModule.prototype.load = function (data) {
var i = 0;
playlist.pos = parseInt(playlist.pos);
playlist.pl.forEach(function (item) {
if (item.media.type === "cu" && item.media.id.indexOf("cu:") !== 0) {
try {
item.media = CustomEmbedFilter(item.media.id);
} catch (e) {
return;
}
} else if (item.media.type === "gd" || item.media.type === "gp") {
delete item.media.meta.gpdirect;
}
var m = new Media(item.media.id, item.media.title, item.media.seconds,
item.media.type, item.media.meta || {});
var newitem = new PlaylistItem(m, {
@ -133,10 +143,8 @@ PlaylistModule.prototype.load = function (data) {
PlaylistModule.prototype.save = function (data) {
var arr = this.items.toArray().map(function (item) {
/* Clear Google Docs and Vimeo meta */
/* Clear Google Docs/Google+ and Vimeo meta */
if (item.media && item.media.meta) {
delete item.media.meta.object;
delete item.media.meta.params;
delete item.media.meta.direct;
}
return item;
@ -167,11 +175,6 @@ PlaylistModule.prototype.unload = function () {
this._leadInterval = false;
}
if (this._gdRefreshTimer) {
clearInterval(this._gdRefreshTimer);
this._gdRefreshTimer = false;
}
this.channel = null;
};
@ -265,7 +268,7 @@ PlaylistModule.prototype.sendPlaylist = function (users) {
};
PlaylistModule.prototype.sendChangeMedia = function (users) {
if (!this.current || !this.current.media) {
if (!this.current || !this.current.media || this._refreshing) {
return;
}
@ -1116,8 +1119,6 @@ PlaylistModule.prototype.refreshGoogleDocs = function (cb) {
}
var abort = function () {
clearInterval(self._gdRefreshTimer);
self._gdRefreshTimer = false;
if (self.current) {
self.current.media.meta.object = self.current.media.meta.object || null;
self.current.media.meta.failed = true;

View File

@ -1,22 +1,96 @@
const allowed = ["iframe", "object", "param", "embed"];
const tag_re = /<\s*\/?\s*([a-z]+)(\s*([a-z]+)\s*=\s*('[^']*'|"[^"]*"|[^"'>]*))*\s*>/ig;
var cheerio = require("cheerio");
var crypto = require("crypto");
var Media = require("./media");
function filter(str) {
if (typeof str !== "string") {
return "";
function sha256(input) {
var hash = crypto.createHash("sha256");
hash.update(input);
return hash.digest("base64");
}
function filter(input) {
var $ = cheerio.load(input, { xmlMode: true });
var meta = getMeta($);
var id = "cu:" + sha256(input);
return new Media(id, "Custom Media", "--:--", "cu", meta);
}
function getMeta($) {
var tag = $("embed");
if (tag.length !== 0) {
return filterEmbed(tag[0]);
}
tag = $("object");
if (tag.length !== 0) {
return filterObject(tag[0]);
}
tag = $("iframe");
if (tag.length !== 0) {
return filterIframe(tag[0]);
}
str = str.replace(tag_re, function (match, tag) {
if(!~allowed.indexOf(tag.toLowerCase())) {
return match.replace("<", "&lt;").replace(">", "&gt;");
throw new Error("Invalid embed. Input must be an <iframe>, <object>, or " +
"<embed> tag.");
}
const ALLOWED_PARAMS = /^(flashvars|bgcolor|movie)$/i;
function filterEmbed(tag) {
if (tag.attribs.type !== "application/x-shockwave-flash") {
throw new Error("Invalid embed. Only type 'application/x-shockwave-flash' " +
"is allowed for <embed> tags.");
}
var meta = {
embed: {
tag: "object",
src: tag.attribs.src,
params: {}
}
return match;
});
str = str.replace(/(\bon\w*\s*=\s*('[^']*'|"[^"]"|[^\s><]*))/ig, function () {
return "";
}
for (var key in tag.attribs) {
if (ALLOWED_PARAMS.test(key)) {
meta.embed.params[key] = tag.attribs[key];
}
}
return meta;
}
function filterObject(tag) {
if (tag.attribs.type !== "application/x-shockwave-flash") {
throw new Error("Invalid embed. Only type 'application/x-shockwave-flash' " +
"is allowed for <embed> tags.");
}
var meta = {
embed: {
tag: "object",
src: tag.attribs.data,
params: {}
}
};
tag.children.forEach(function (child) {
if (child.name !== "param") return;
if (!ALLOWED_PARAMS.test(child.attribs.name)) return;
meta.embed.params[child.attribs.name] = child.attribs.value;
});
return str.substring(0, 20000);
return meta;
}
function filterIframe(tag) {
var meta = {
embed: {
tag: "iframe",
src: tag.attribs.src
}
};
return meta;
}
exports.filter = filter;

View File

@ -427,7 +427,8 @@ module.exports = {
var meta = JSON.stringify({
bitrate: media.meta.bitrate,
codec: media.meta.codec,
scuri: media.meta.scuri
scuri: media.meta.scuri,
embed: media.meta.embed
});
db.query("INSERT INTO `channel_libraries` " +

View File

@ -2,7 +2,7 @@ var db = require("../database");
var Logger = require("../logger");
var Q = require("q");
const DB_VERSION = 5;
const DB_VERSION = 6;
var hasUpdates = [];
module.exports.checkVersion = function () {
@ -54,6 +54,8 @@ function update(version, cb) {
})
} else if (version < 5) {
fixUtf8mb4(cb);
} else if (version < 6) {
fixCustomEmbeds(cb);
}
}
@ -240,3 +242,31 @@ function fixUtf8mb4(cb) {
Logger.errlog.log("Failed to fix utf8mb4: " + e);
});
};
function fixCustomEmbeds(cb) {
var CustomEmbedFilter = require("../customembed").filter;
Q.nfcall(db.query, "SELECT * FROM `channel_libraries` WHERE type='cu'")
.then(function (rows) {
var all = [];
rows.forEach(function (row) {
if (row.id.indexOf("cu:") === 0) return;
all.push(Q.nfcall(db.query, "DELETE FROM `channel_libraries` WHERE `id`=? AND `channel`=?",
[row.id, row.channel]));
try {
var media = CustomEmbedFilter(row.id);
all.push(Q.nfcall(db.channels.addToLibrary, row.channel, media));
} catch(e) {
console.error("WARNING: Unable to convert " + row.id);
}
});
Q.allSettled(all).then(function () {
Logger.syslog.log("Converted custom embeds.");
cb();
});
});
}

View File

@ -7,7 +7,7 @@ var CustomEmbedFilter = require("./customembed").filter;
var Server = require("./server");
var Config = require("./config");
var ffmpeg = require("./ffmpeg");
require("cytube-mediaquery"); // Initialize sourcemaps
var mediaquery = require("cytube-mediaquery");
var YouTube = require("cytube-mediaquery/lib/provider/youtube");
/*
@ -62,6 +62,17 @@ var urlRetrieve = function (transport, options, callback) {
req.end();
};
var mediaTypeMap = {
"youtube": "yt",
"googledrive": "gd",
"google+": "gp"
};
function convertMedia(media) {
return new Media(media.id, media.title, media.duration, mediaTypeMap[media.type],
media.meta);
}
var Getters = {
/* youtube.com */
yt: function (id, callback) {
@ -502,103 +513,47 @@ var Getters = {
/* custom embed */
cu: function (id, callback) {
id = CustomEmbedFilter(id);
var media = new Media(id, "Custom Media", "--:--", "cu");
var media;
try {
media = CustomEmbedFilter(id);
} catch (e) {
if (/invalid embed/i.test(e.message)) {
return callback(e.message);
} else {
Logger.errlog.log(e.stack);
return callback("Unknown error processing embed");
}
}
callback(false, media);
},
/* google docs */
gd: function (id, callback) {
/* WARNING: hacks inbound */
var options = {
host: "docs.google.com",
path: "/file/d/" + id + "/get_video_info?sle=true",
port: 443
var data = {
type: "googledrive",
kind: "single",
id: id
};
urlRetrieve(https, options, function (status, res) {
switch (status) {
case 200:
break; /* Request is OK, skip to handling data */
case 400:
return callback("Invalid request", null);
case 403:
return callback("Private video", null);
case 404:
return callback("Video not found", null);
case 500:
case 503:
return callback("Service unavailable", null);
default:
return callback("HTTP " + status, null);
}
mediaquery.lookup(data).then(function (video) {
callback(null, convertMedia(video));
}).catch(function (err) {
callback(err.message || err);
});
},
try {
/* Google+ videos */
gp: function (id, callback) {
var data = {
type: "google+",
kind: "single",
id: id
};
var data = {};
res.split("&").forEach(function (urlparam) {
var pair = urlparam.split("=").map(decodeURIComponent).map(
function (s) { return s.replace(/\+/g, ' '); });
data[pair[0]] = pair[1];
});
if (data.hasOwnProperty("reason")) {
var reason = data.reason;
if (reason.indexOf("Unable to play this video at this time.") === 0) {
reason = "There is currently a bug with Google Drive which prevents playback " +
"of videos 1 hour long or longer.";
} else if (reason.indexOf(
"You must be signed in to access this video") >= 0) {
reason = "This video is not shared properly";
}
return callback(reason);
}
if (!data.hasOwnProperty("title")) {
return callback("Returned HTML is missing the video title. Are you " +
"sure the video is done processing?");
}
if (!data.hasOwnProperty("length_seconds")) {
return callback("Returned HTML is missing the video duration. Are you " +
"sure the video is done processing?");
}
var title = data.title;
var seconds = parseInt(data.length_seconds);
var videos = {};
data.fmt_stream_map.split(",").forEach(function (stream) {
var parts = stream.split("|");
videos[parts[0]] = parts[1];
});
var direct = {};
for (var key in GOOGLE_PREFERENCE) {
for (var i = 0; i < GOOGLE_PREFERENCE[key].length; i++) {
var format = GOOGLE_PREFERENCE[key][i];
if (format in videos) {
direct[key] = {
url: videos[format],
contentType: CONTENT_TYPES[format]
};
break;
}
}
}
if (Object.keys(direct).length === 0) {
return callback("No valid links could be extracted", null);
}
callback(null, new Media(id, title, seconds, "gd", { gpdirect: direct }));
} catch (e) {
return callback("Failed to parse Google Docs output", null);
}
mediaquery.lookup(data).then(function (video) {
callback(null, convertMedia(video));
}).catch(function (err) {
callback(err.message || err);
});
},
@ -617,109 +572,6 @@ var Getters = {
});
},
/*
* Google+ videos
*
* Also known as Picasa Web Albums.
*
*/
gp: function (id, cb) {
var idparts = id.split("_");
if (idparts.length !== 3) {
return cb("Invalid Google+ video ID");
}
var options = {
host: "picasaweb.google.com",
path: '/data/feed/api/user/'+idparts[0]+'/albumid/'+idparts[1]+'/photoid/'+idparts[2]+'?kind=tag',
port: 443
};
urlRetrieve(https, options, function (status, res) {
switch (status) {
case 200:
break; /* Request is OK, skip to handling data */
case 400:
return cb("Invalid request", null);
case 403:
return cb("Private video", null);
case 404:
return cb("Video not found", null);
case 500:
case 503:
return cb("Service unavailable", null);
default:
return cb("HTTP " + status, null);
}
try {
var $ = cheerio.load(res, { xmlMode: true });
switch ($("gphoto\\:videostatus").text()) {
case "final":
break; /* Incoming Fun. */
case "pending":
return cb("The video is still being processed.", null);
case "failed":
return cb("A processing error has occured and the video should be deleted.", null);
case "ready":
return cb("The video has been processed but still needs a thumbnail.", null);
}
var duration = parseInt($("gphoto\\:originalvideo").attr("duration"),10);
var title = $("media\\:title").text();
var videos = {};
$('media\\:content[medium="video"]').each(function(index, element){
var url = $(this).attr("url");
var match = url.match(/itag=(\d+)/)
if (!match) {
match = url.match(/googleusercontent.*=m(\d+)$/);
}
if (match && match[1]) {
var type = match[1];
videos[type] = {
format: type,
link: url
};
}
});
$ = null;
var direct = {};
for (var key in GOOGLE_PREFERENCE) {
for (var i = 0; i < GOOGLE_PREFERENCE[key].length; i++) {
var format = GOOGLE_PREFERENCE[key][i];
if (format in videos) {
direct[key] = {
url: videos[format].link,
contentType: CONTENT_TYPES[format]
};
break;
}
}
}
if (Object.keys(direct).length === 0) {
return cb("Unable to retrieve video data from Google+. The videos " +
"may have not finished processing yet.");
} else if (!title) {
return cb("Unable to retrieve title from Google+. Check that " +
"the album exists and is shared publicly.");
} else if (!duration) {
return cb("Unable to retreive duration from Google+. This might be " +
"because the video is still processing.");
}
var media = new Media(id, title, duration, "gp", { gpdirect: direct });
cb(null, media);
} catch (e) {
cb("Unknown error");
Logger.errlog.log("Unknown error for Google+ ID " + id + ": " + e.stack);
}
});
},
/* hitbox.tv */
hb: function (id, callback) {
var m = id.match(/([\w-]+)/);
@ -735,98 +587,6 @@ var Getters = {
},
};
/**
* Function to workaround Vimeo being a dick and blocking my domain from embeds.
* Retrieves the player page and extracts the direct links to the MP4 encoded videos.
*/
function vimeoWorkaround(id, cb) {
if (typeof cb !== "function") {
return;
}
var failcount = 0;
var inner = function () {
var options = {
host: "player.vimeo.com",
port: 443,
path: "/video/" + id,
headers: {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0",
"Referrer": "player.vimeo.com"
}
};
var parse = function (data) {
var i = data.indexOf("{\"cdn_url\"");
if (i === -1) {
setImmediate(function () {
cb({});
});
return;
}
var j = data.indexOf("};", i);
var json = data.substring(i, j+1);
try {
json = JSON.parse(json);
if (!json.request.files) {
setImmediate(function () {
cb({});
});
return;
}
var codec = json.request.files.codecs[0];
var files = json.request.files[codec];
setImmediate(function () {
cb(files);
});
} catch (e) {
// This shouldn't happen due to the user-agent, but just in case
if (data.indexOf("crawler") !== -1) {
Logger.syslog.log("Warning: vimdeoWorkaround got crawler response");
failcount++;
if (failcount > 4) {
Logger.errlog.log("vimeoWorkaround got bad response 5 times!"+
" Giving up.");
setImmediate(function () {
cb({});
});
} else {
setImmediate(function () {
inner();
});
}
return;
} else if (data.indexOf("This video does not exist.") !== -1) {
cb({});
return;
} else if (data.indexOf("Because of its privacy settings, this video cannot be played here.") !== -1) {
cb({});
}
Logger.errlog.log("Vimeo workaround error: ");
Logger.errlog.log(e);
Logger.errlog.log("http://vimeo.com/" + id);
setImmediate(function () {
cb({});
});
}
};
urlRetrieve(https, options, function (status, buffer) {
if (status !== 200) {
setImmediate(function () {
cb({});
});
return;
}
parse(buffer);
});
};
inner();
}
module.exports = {
Getters: Getters,
getMedia: function (id, type, callback) {
@ -835,7 +595,5 @@ module.exports = {
} else {
callback("Unknown media type '" + type + "'", null);
}
},
vimeoWorkaround: vimeoWorkaround
}
};

View File

@ -33,11 +33,11 @@ Media.prototype = {
type: this.type,
meta: {
direct: this.meta.direct,
gpdirect: this.meta.gpdirect,
restricted: this.meta.restricted,
codec: this.meta.codec,
bitrate: this.meta.bitrate,
scuri: this.meta.scuri
scuri: this.meta.scuri,
embed: this.meta.embed
}
};
},

View File

@ -1,4 +1,4 @@
const VERSION = "3.7.1";
const VERSION = require("../package.json").version;
var singleton = null;
var Config = require("./config");

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.7.1",
"version": "3.8.0",
"repository": {
"url": "http://github.com/calzoneman/sync"
},
@ -30,5 +30,11 @@
"serve-static": "^1.8.1",
"socket.io": "^1.3.5",
"yamljs": "^0.1.6"
},
"scripts": {
"build-player": "$npm_node_execpath build-player.js"
},
"devDependencies": {
"coffee-script": "^1.9.2"
}
}

34
player/base.coffee Normal file
View File

@ -0,0 +1,34 @@
window.Player = class Player
constructor: (data) ->
if not (this instanceof Player)
return new Player(data)
@setMediaProperties(data)
@paused = false
load: (data) ->
@setMediaProperties(data)
setMediaProperties: (data) ->
@mediaId = data.id
@mediaType = data.type
@mediaLength = data.seconds
play: ->
@paused = false
pause: ->
@paused = true
seekTo: (time) ->
setVolume: (volume) ->
getTime: (cb) ->
cb(0)
isPaused: (cb) ->
cb(@paused)
getVolume: (cb) ->
cb(VOLUME)

View File

@ -0,0 +1,28 @@
CUSTOM_EMBED_WARNING = 'This channel is embedding custom content from %link%.
Since this content is not trusted, you must click "Embed" below to allow
the content to be embedded.<hr>'
window.CustomEmbedPlayer = class CustomEmbedPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof CustomEmbedPlayer)
return new CustomEmbedPlayer(data)
@load(data)
load: (data) ->
if not data.meta.embed?
console.error('CustomEmbedPlayer::load(): missing meta.embed')
return
embedSrc = data.meta.embed.src
link = "<a href=\"#{embedSrc}\" target=\"_blank\"><strong>#{embedSrc}</strong></a>"
alert = makeAlert('Untrusted Content', CUSTOM_EMBED_WARNING.replace('%link%', link),
'alert-warning')
.removeClass('col-md-12')
$('<button/>').addClass('btn btn-default')
.text('Embed')
.click(=>
super(data)
)
.appendTo(alert.find('.alert'))
removeOld(alert)

105
player/dailymotion.coffee Normal file
View File

@ -0,0 +1,105 @@
window.DailymotionPlayer = class DailymotionPlayer extends Player
constructor: (data) ->
if not (this instanceof DailymotionPlayer)
return new DailymotionPlayer(data)
@setMediaProperties(data)
@initialVolumeSet = false
waitUntilDefined(window, 'DM', =>
removeOld()
params =
autoplay: 1
wmode: if USEROPTS.wmode_transparent then 'transparent' else 'opaque'
logo: 0
quality = @mapQuality(USEROPTS.default_quality)
if quality != 'auto'
params.quality = quality
@dm = DM.player('ytapiplayer',
video: data.id
width: parseInt(VWIDTH, 10)
height: parseInt(VHEIGHT, 10)
params: params
)
@dm.addEventListener('apiready', =>
@dm.ready = true
@dm.addEventListener('ended', ->
if CLIENT.leader
socket.emit('playNext')
)
@dm.addEventListener('pause', =>
@paused = true
if CLIENT.leader
sendVideoUpdate()
)
@dm.addEventListener('playing', =>
@paused = false
if CLIENT.leader
sendVideoUpdate()
if not @initialVolumeSet
@setVolume(VOLUME)
@initialVolumeSet = true
)
)
)
load: (data) ->
@setMediaProperties(data)
if @dm and @dm.ready
@dm.load(data.id)
@dm.seek(data.currentTime)
else
console.error('WTF? DailymotionPlayer::load() called but dm is not ready')
pause: ->
if @dm and @dm.ready
@paused = true
@dm.pause()
play: ->
if @dm and @dm.ready
@paused = false
@dm.play()
seekTo: (time) ->
if @dm and @dm.ready
@dm.seek(time)
setVolume: (volume) ->
if @dm and @dm.ready
@dm.setVolume(volume)
getTime: (cb) ->
if @dm and @dm.ready
cb(@dm.currentTime)
else
cb(0)
getVolume: (cb) ->
if @dm and @dm.ready
if @dm.muted
cb(0)
else
volume = @dm.volume
# There was once a bug in Dailymotion where it sometimes gave back
# volumes in the wrong range. Not sure if this is still a necessary
# check.
if volume > 1
volume /= 100
cb(volume)
else
cb(VOLUME)
mapQuality: (quality) ->
switch String(quality)
when '240', '480', '720', '1080' then String(quality)
when '360' then '380'
when 'best' then '1080'
else 'auto'

65
player/embed.coffee Normal file
View File

@ -0,0 +1,65 @@
DEFAULT_ERROR = 'You are currently connected via HTTPS but the embedded content
uses non-secure plain HTTP. Your browser therefore blocks it from
loading due to mixed content policy. To fix this, embed the video using a
secure link if available (https://...), or load this page over plain HTTP by
replacing "https://" with "http://" in the address bar (your websocket will
still be secured using HTTPS, but this will permit non-secure content to load).'
genParam = (name, value) ->
$('<param/>').attr(
name: name
value: value
)
window.EmbedPlayer = class EmbedPlayer extends Player
constructor: (data) ->
if not (this instanceof EmbedPlayer)
return new EmbedPlayer(data)
@load(data)
load: (data) ->
@setMediaProperties(data)
embed = data.meta.embed
if not embed?
console.error('EmbedPlayer::load(): missing meta.embed')
return
if embed.tag == 'object'
@player = @loadObject(embed)
else
@player = @loadIframe(embed)
removeOld(@player)
loadObject: (embed) ->
object = $('<object/>').attr(
type: 'application/x-shockwave-flash'
data: embed.src
)
genParam('allowfullscreen', 'true').appendTo(object)
genParam('allowscriptaccess', 'always').appendTo(object)
for key, value of embed.params
genParam(key, value).appendTo(object)
return object
loadIframe: (embed) ->
if embed.src.indexOf('http:') == 0 and location.protocol == 'https:'
if @__proto__.mixedContentError?
error = @__proto__.mixedContentError
else
error = DEFAULT_ERROR
alert = makeAlert('Mixed Content Error', error, 'alert-danger')
.removeClass('col-md-12')
alert.find('.close').remove()
return alert
else
iframe = $('<iframe/>').attr(
src: embed.src
frameborder: '0'
)
return iframe

21
player/hitbox.coffee Normal file
View File

@ -0,0 +1,21 @@
HITBOX_ERROR = 'Hitbox.tv only serves its content over plain HTTP, but you are
viewing this page over secure HTTPS. Your browser therefore blocks the
hitbox embed due to mixed content policy. In order to view hitbox, you must
view this page over plain HTTP (change "https://" to "http://" in the address
bar)-- your websocket will still be connected using secure HTTPS. This is
something I have asked Hitbox to fix but they have not done so yet.'
window.HitboxPlayer = class HitboxPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof HitboxPlayer)
return new HitboxPlayer(data)
@load(data)
load: (data) ->
data.meta.embed =
src: "http://hitbox.tv/embed/#{data.id}"
tag: 'iframe'
super(data)
mixedContentError: HITBOX_ERROR

12
player/imgur.coffee Normal file
View File

@ -0,0 +1,12 @@
window.ImgurPlayer = class ImgurPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof ImgurPlayer)
return new ImgurPlayer(data)
@load(data)
load: (data) ->
data.meta.embed =
tag: 'iframe'
src: "https://imgur.com/a/#{data.id}/embed"
super(data)

View File

@ -0,0 +1,23 @@
window.LivestreamPlayer = class LivestreamPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof LivestreamPlayer)
return new LivestreamPlayer(data)
@load(data)
load: (data) ->
if LIVESTREAM_CHROMELESS
data.meta.embed =
src: 'https://cdn.livestream.com/chromelessPlayer/v20/playerapi.swf'
tag: 'object'
params:
flashvars: "channel=#{data.id}"
else
data.meta.embed =
src: "https://cdn.livestream.com/embed/#{data.id}?\
layout=4&\
color=0x000000&\
iconColorOver=0xe7e7e7&\
iconColor=0xcccccc"
tag: 'iframe'
super(data)

29
player/raw-file.coffee Normal file
View File

@ -0,0 +1,29 @@
codecToMimeType = (codec) ->
switch codec
when 'mov/h264' then 'video/mp4'
when 'flv/h264' then 'video/flv'
when 'matroska/vp8', 'matroska/vp9' then 'video/webm'
when 'ogg/theora' then 'video/ogg'
when 'mp3' then 'audio/mp3'
when 'vorbis' then 'audio/vorbis'
else 'video/flv'
window.FilePlayer = class FilePlayer extends VideoJSPlayer
constructor: (data) ->
if not (this instanceof FilePlayer)
return new FilePlayer(data)
data.meta.direct =
480: [{
contentType: codecToMimeType(data.meta.codec)
link: data.id
}]
super(data)
load: (data) ->
data.meta.direct =
480: [{
contentType: codecToMimeType(data.meta.codec)
link: data.id
}]
super(data)

26
player/rtmp.coffee Normal file
View File

@ -0,0 +1,26 @@
window.rtmpEventHandler = (id, event, data) ->
if event == 'volumechange'
PLAYER.volume = if data.muted then 0 else data.volume
window.RTMPPlayer = class RTMPPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof RTMPPlayer)
return new RTMPPlayer(data)
@volume = VOLUME
@load(data)
load: (data) ->
data.meta.embed =
tag: 'object'
src: 'https://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf'
params:
flashvars: "src=#{data.id}&\
streamType=live&\
javascriptCallbackFunction=rtmpEventHandler&\
autoPlay=true&\
volume=#{VOLUME}"
super(data)
getVolume: (cb) ->
cb(@volume)

105
player/soundcloud.coffee Normal file
View File

@ -0,0 +1,105 @@
window.SoundCloudPlayer = class SoundCloudPlayer extends Player
constructor: (data) ->
if not (this instanceof SoundCloudPlayer)
return new SoundCloudPlayer(data)
@setMediaProperties(data)
waitUntilDefined(window, 'SC', =>
removeOld()
# For tracks that are private, but embeddable, the API returns a
# special URL to load into the player.
# TODO: rename scuri?
if data.meta.scuri
soundUrl = data.meta.scuri
else
soundUrl = data.id
widget = $('<iframe/>').appendTo($('#ytapiplayer'))
widget.attr(
id: 'scplayer'
src: "https://w.soundcloud.com/player/?url=#{soundUrl}"
)
# Soundcloud embed widget doesn't have a volume control.
volumeSlider = $('<div/>').attr('id', 'widget-volume')
.css('top', '170px')
.insertAfter(widget)
.slider(
range: 'min'
value: VOLUME * 100
stop: (event, ui) =>
@setVolume(ui.value / 100)
)
@soundcloud = SC.Widget(widget[0])
@soundcloud.bind(SC.Widget.Events.READY, =>
@soundcloud.ready = true
@setVolume(VOLUME)
@play()
@soundcloud.bind(SC.Widget.Events.PAUSE, =>
@paused = true
if CLIENT.leader
sendVideoUpdate()
)
@soundcloud.bind(SC.Widget.Events.PLAY, =>
@paused = false
if CLIENT.leader
sendVideoUpdate()
)
@soundcloud.bind(SC.Widget.Events.FINISH, =>
if CLIENT.leader
socket.emit('playNext')
)
)
)
load: (data) ->
@setMediaProperties(data)
if @soundcloud and @soundcloud.ready
if data.meta.scuri
soundUrl = data.meta.scuri
else
soundUrl = data.id
@soundcloud.load(soundUrl, auto_play: true)
else
console.error('SoundCloudPlayer::load() called but soundcloud is not ready')
play: ->
@paused = false
if @soundcloud and @soundcloud.ready
@soundcloud.play()
pause: ->
@paused = true
if @soundcloud and @soundcloud.ready
@soundcloud.pause()
seekTo: (time) ->
if @soundcloud and @soundcloud.ready
# SoundCloud measures time in milliseconds while CyTube uses seconds.
@soundcloud.seekTo(time * 1000)
setVolume: (volume) ->
# NOTE: SoundCloud's documentation claims that setVolume() accepts
# volumes in the range [0, 100], however it *actually* accepts volumes
# in the range [0, 1] (anything larger than 1 is treated as 1). I
# emailed them about this 2 years ago and they still haven't fixed
# their documentation.
if @soundcloud and @soundcloud.ready
@soundcloud.setVolume(volume)
getTime: (cb) ->
if @soundcloud and @soundcloud.ready
# Returned time is in milliseconds; CyTube expects seconds
@soundcloud.getPosition((time) -> cb(time / 1000))
else
cb(0)
getVolume: (cb) ->
if @soundcloud and @soundcloud.ready
@soundcloud.getVolume(cb)
else
cb(VOLUME)

29
player/twitch.coffee Normal file
View File

@ -0,0 +1,29 @@
window.twitchEventCallback = (events) ->
if not (PLAYER instanceof TwitchPlayer)
return false
events.forEach((event) ->
if event.event == 'playerInit'
PLAYER.twitch.unmute()
PLAYER.twitch.ready = true
)
window.TwitchPlayer = class TwitchPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof TwitchPlayer)
return new TwitchPlayer(data)
@load(data)
load: (data) ->
data.meta.embed =
src: '//www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf'
tag: 'object'
params:
flashvars: "embed=1&\
hostname=localhost&\
channel=#{data.id}&
eventsCallback=twitchEventCallback&\
auto_play=true&\
start_volume=#{Math.floor(VOLUME * 100)}"
super(data)

105
player/update.coffee Normal file
View File

@ -0,0 +1,105 @@
TYPE_MAP =
yt: YouTubePlayer
vi: VimeoPlayer
dm: DailymotionPlayer
gd: VideoJSPlayer
gp: VideoJSPlayer
fi: FilePlayer
jw: FilePlayer
sc: SoundCloudPlayer
li: LivestreamPlayer
tw: TwitchPlayer
cu: CustomEmbedPlayer
rt: RTMPPlayer
hb: HitboxPlayer
us: UstreamPlayer
im: ImgurPlayer
window.loadMediaPlayer = (data) ->
if data.meta.direct
try
window.PLAYER = new VideoJSPlayer(data)
catch e
console.error e
else if data.type of TYPE_MAP
try
window.PLAYER = TYPE_MAP[data.type](data)
catch e
console.error e
window.handleMediaUpdate = (data) ->
PLAYER = window.PLAYER
# Do not update if the current time is past the end of the video, unless
# the video has length 0 (which is a special case for livestreams)
if typeof PLAYER.mediaLength is 'number' and
PLAYER.mediaLength > 0 and
data.currentTime > PLAYER.mediaLength
return
# Negative currentTime indicates a lead-in for clients to load the video,
# but not play it yet (helps with initial buffering)
waiting = data.currentTime < 0
# Load a new video in the same player if the ID changed
if data.id and data.id != PLAYER.mediaId
if data.currentTime < 0
data.currentTime = 0
PLAYER.load(data)
PLAYER.play()
if waiting
PLAYER.seekTo(0)
# YouTube player has a race condition that crashes the player if
# play(), seek(0), and pause() are called quickly without waiting
# for events to fire. Setting a flag variable that is checked in the
# event handler mitigates this.
if PLAYER instanceof YouTubePlayer
PLAYER.pauseSeekRaceCondition = true
else
PLAYER.pause()
return
else if PLAYER instanceof YouTubePlayer
PLAYER.pauseSeekRaceCondition = false
if CLIENT.leader or not USEROPTS.synch
return
if data.paused and not PLAYER.paused
PLAYER.seekTo(data.currentTime)
PLAYER.pause()
else if PLAYER.paused and not data.paused
PLAYER.play()
PLAYER.getTime((seconds) ->
time = data.currentTime
diff = (time - seconds) or time
accuracy = USEROPTS.sync_accuracy
# Dailymotion can't seek very accurately in Flash due to keyframe
# placement. Accuracy should not be set lower than 5 or the video
# may be very choppy.
if PLAYER instanceof DailymotionPlayer
accuracy = Math.max(accuracy, 5)
if diff > accuracy
# The player is behind the correct time
PLAYER.seekTo(time)
else if diff < -accuracy
# The player is ahead of the correct time
# Don't seek all the way back, to account for possible buffering.
# However, do seek all the way back for Dailymotion due to the
# keyframe issue mentioned above.
if not (PLAYER instanceof DailymotionPlayer)
time += 1
PLAYER.seekTo(time)
)
window.removeOld = (replace) ->
$('#sc_volume').remove()
replace ?= $('<div/>').addClass('embed-responsive-item')
old = $('#ytapiplayer')
replace.insertBefore(old)
old.remove()
replace.attr('id', 'ytapiplayer')
return replace

12
player/ustream.coffee Normal file
View File

@ -0,0 +1,12 @@
window.UstreamPlayer = class UstreamPlayer extends EmbedPlayer
constructor: (data) ->
if not (this instanceof UstreamPlayer)
return new UstreamPlayer(data)
@load(data)
load: (data) ->
data.meta.embed =
tag: 'iframe'
src: "https://www.ustream.tv/embed/#{data.id}?v=3&wmode=direct&autoplay=1"
super(data)

136
player/videojs.coffee Normal file
View File

@ -0,0 +1,136 @@
sortSources = (sources) ->
if not sources
console.error('sortSources() called with null source list')
return []
qualities = ['1080', '720', '480', '360', '240']
pref = String(USEROPTS.default_quality)
idx = qualities.indexOf(pref)
if idx < 0
pref = '480'
qualityOrder = qualities.slice(idx).concat(qualities.slice(0, idx))
sourceOrder = []
flvOrder = []
for quality in qualityOrder
if quality of sources
flv = []
nonflv = []
sources[quality].forEach((source) ->
source.quality = quality
if source.contentType == 'video/flv'
flv.push(source)
else
nonflv.push(source)
)
sourceOrder = sourceOrder.concat(nonflv)
flvOrder = flvOrder.concat(flv)
return sourceOrder.concat(flvOrder).map((source) ->
type: source.contentType
src: source.link
quality: source.quality
)
waitUntilDefined(window, 'videojs', =>
videojs.options.flash.swf = '/video-js.swf'
)
window.VideoJSPlayer = class VideoJSPlayer extends Player
constructor: (data) ->
if not (this instanceof VideoJSPlayer)
return new VideoJSPlayer(data)
@setMediaProperties(data)
@loadPlayer(data)
loadPlayer: (data) ->
waitUntilDefined(window, 'videojs', =>
video = $('<video/>')
.addClass('video-js vjs-default-skin embed-responsive-item')
.attr(width: '100%', height: '100%')
removeOld(video)
sources = sortSources(data.meta.direct)
if sources.length == 0
console.error('VideoJSPlayer::constructor(): data.meta.direct
has no sources!')
@mediaType = null
return
sources.forEach((source) ->
$('<source/>').attr(
src: source.src
type: source.type
'data-quality': source.quality
).appendTo(video)
)
@player = videojs(video[0], autoplay: true, controls: true)
@player.ready(=>
@setVolume(VOLUME)
@player.on('ended', ->
if CLIENT.leader
socket.emit('playNext')
)
@player.on('pause', =>
@paused = true
if CLIENT.leader
sendVideoUpdate()
)
@player.on('play', =>
@paused = false
if CLIENT.leader
sendVideoUpdate()
)
# Workaround for IE-- even after seeking completes, the loading
# spinner remains.
@player.on('seeked', =>
$('.vjs-waiting').removeClass('vjs-waiting')
)
)
)
load: (data) ->
@setMediaProperties(data)
# Note: VideoJS does have facilities for loading new videos into the
# existing player object, however it appears to be pretty glitchy when
# a video can't be played (either previous or next video). It's safer
# to just reset the entire thing.
@loadPlayer(data)
play: ->
@paused = false
if @player and @player.readyState() > 0
@player.play()
pause: ->
@paused = true
if @player and @player.readyState() > 0
@player.pause()
seekTo: (time) ->
if @player and @player.readyState() > 0
@player.currentTime(time)
setVolume: (volume) ->
if @player
@player.volume(volume)
getTime: (cb) ->
if @player and @player.readyState() > 0
cb(@player.currentTime())
else
cb(0)
getVolume: (cb) ->
if @player and @player.readyState() > 0
if @player.muted()
cb(0)
else
cb(@player.volume())
else
cb(VOLUME)

80
player/vimeo.coffee Normal file
View File

@ -0,0 +1,80 @@
window.VimeoPlayer = class VimeoPlayer extends Player
constructor: (data) ->
if not (this instanceof VimeoPlayer)
return new VimeoPlayer(data)
@load(data)
load: (data) ->
@setMediaProperties(data)
waitUntilDefined(window, '$f', =>
video = $('<iframe/>')
removeOld(video)
video.attr(
src: "https://player.vimeo.com/video/#{data.id}?api=1&player_id=ytapiplayer"
webkitallowfullscreen: true
mozallowfullscreen: true
allowfullscreen: true
)
if USEROPTS.wmode_transparent
video.attr('wmode', 'transparent')
$f(video[0]).addEvent('ready', =>
@vimeo = $f(video[0])
@play()
@vimeo.addEvent('finish', =>
if CLIENT.leader
socket.emit('playNext')
)
@vimeo.addEvent('pause', =>
@paused = true
if CLIENT.leader
sendVideoUpdate()
)
@vimeo.addEvent('play', =>
@paused = false
if CLIENT.leader
sendVideoUpdate()
)
@setVolume(VOLUME)
)
)
play: ->
@paused = false
if @vimeo
@vimeo.api('play')
pause: ->
@paused = true
if @vimeo
@vimeo.api('pause')
seekTo: (time) ->
if @vimeo
@vimeo.api('seekTo', time)
setVolume: (volume) ->
if @vimeo
@vimeo.api('setVolume', volume)
getTime: (cb) ->
if @vimeo
@vimeo.api('getCurrentTime', (time) ->
# I will never understand why Vimeo returns current time as a string
cb(parseFloat(time))
)
else
cb(0)
getVolume: (cb) ->
if @vimeo
@vimeo.api('getVolume', cb)
else
cb(VOLUME)

121
player/youtube.coffee Normal file
View File

@ -0,0 +1,121 @@
window.YouTubePlayer = class YouTubePlayer extends Player
constructor: (data) ->
if not (this instanceof YouTubePlayer)
return new YouTubePlayer(data)
@setMediaProperties(data)
@qualityRaceCondition = true
@pauseSeekRaceCondition = false
waitUntilDefined(window, 'YT', =>
# Even after window.YT is defined, YT.Player may not be, which causes a
# 'YT.Player is not a constructor' error occasionally
waitUntilDefined(YT, 'Player', =>
removeOld()
wmode = if USEROPTS.wmode_transparent then 'transparent' else 'opaque'
@yt = new YT.Player('ytapiplayer',
videoId: data.id
playerVars:
autohide: 1
autoplay: 1
controls: 1
iv_load_policy: 3 # iv_load_policy 3 indicates no annotations
rel: 0
wmode: wmode
events:
onReady: @onReady.bind(this)
onStateChange: @onStateChange.bind(this)
)
)
)
load: (data) ->
@setMediaProperties(data)
if @yt and @yt.ready
@yt.loadVideoById(data.id, data.currentTime)
@qualityRaceCondition = true
if USEROPTS.default_quality
@setQuality(USEROPTS.default_quality)
else
console.error('WTF? YouTubePlayer::load() called but yt is not ready')
onReady: ->
@yt.ready = true
@setVolume(VOLUME)
onStateChange: (ev) ->
# For some reason setting the quality doesn't work
# until the first event has fired.
if @qualityRaceCondition
@qualityRaceCondition = false
if USEROPTS.default_quality
@setQuality(USEROPTS.default_quality)
# Similar to above, if you pause the video before the first PLAYING
# event is emitted, weird things happen.
if ev.data == YT.PlayerState.PLAYING and @pauseSeekRaceCondition
@pause()
@pauseSeekRaceCondition = false
if (ev.data == YT.PlayerState.PAUSED and not @paused) or
(ev.data == YT.PlayerState.PLAYING and @paused)
@paused = (ev.data == YT.PlayerState.PAUSED)
if CLIENT.leader
sendVideoUpdate()
if ev.data == YT.PlayerState.ENDED and CLIENT.leader
socket.emit('playNext')
play: ->
@paused = false
if @yt and @yt.ready
@yt.playVideo()
pause: ->
@paused = true
if @yt and @yt.ready
@yt.pauseVideo()
seekTo: (time) ->
if @yt and @yt.ready
@yt.seekTo(time, true)
setVolume: (volume) ->
if @yt and @yt.ready
if volume > 0
# If the player is muted, even if the volume is set,
# the player remains muted
@yt.unMute()
@yt.setVolume(volume * 100)
setQuality: (quality) ->
if not @yt or not @yt.ready
return
ytQuality = switch String(quality)
when '240' then 'small'
when '360' then 'medium'
when '480' then 'large'
when '720' then 'hd720'
when '1080' then 'hd1080'
when 'best' then 'highres'
else 'auto'
if ytQuality != 'auto'
@yt.setPlaybackQuality(ytQuality)
getTime: (cb) ->
if @yt and @yt.ready
cb(@yt.getCurrentTime())
else
cb(0)
getVolume: (cb) ->
if @yt and @yt.ready
if @yt.isMuted()
cb(0)
else
cb(@yt.getVolume() / 100)
else
cb(VOLUME)

View File

@ -4,6 +4,7 @@ html(lang="en")
include head
mixin head()
link(href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css", rel="stylesheet")
link(rel="stylesheet", href="/css/video-js.css")
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
@ -242,8 +243,7 @@ html(lang="en")
script(src="/js/ui.js")
script(src="/js/callbacks.js")
script(defer, src="https://www.youtube.com/iframe_api")
script(defer, src="//api.dmcdn.net/all.js")
script(defer, src="//jwpsrv.com/library/QouFCLBMEeKC+CIACpYGxA.js")
script(defer, src="https://api.dmcdn.net/all.js")
script(defer, src="/js/sc.js")
script(defer, src="/js/froogaloop.min.js")
script(defer, src="/js/swf.js")
script(defer, src="/js/video.js")

View File

@ -83,12 +83,12 @@ mixin us-playback
.col-sm-8
select#us-default-quality.form-control
option(value="auto") Auto
option(value="small") 240p
option(value="medium") 360p
option(value="large") 480p
option(value="hd720") 720p
option(value="hd1080") 1080p
option(value="highres") Highest Available
option(value="240") 240p
option(value="360") 360p
option(value="480") 480p
option(value="720") 720p
option(value="1080") 1080p
option(value="best") Highest Available
mixin us-chat
#us-chat.tab-pane

BIN
www/css/font/vjs.eot Normal file

Binary file not shown.

28
www/css/font/vjs.svg Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe000;" d="M1024 960v-416l-160 160-192-192-96 96 192 192-160 160zM448 288l-192-192 160-160h-416v416l160-160 192 192z" />
<glyph unicode="&#xe001;" d="M192 832l640-384-640-384z" />
<glyph unicode="&#xe002;" d="M128 832h320v-768h-320zM576 832h320v-768h-320z" />
<glyph unicode="&#xe003;" d="M401.332 881.332c25.668 25.668 46.668 16.968 46.668-19.332v-828c0-36.3-21-44.998-46.668-19.33l-241.332 241.33h-160v384h160l241.332 241.332z" />
<glyph unicode="&#xe004;" d="M549.020 218.98c-12.286 0-24.568 4.686-33.942 14.058-18.746 18.746-18.746 49.136 0 67.882 81.1 81.1 81.1 213.058 0 294.156-18.746 18.746-18.746 49.138 0 67.882 18.746 18.744 49.136 18.744 67.882 0 118.53-118.53 118.53-311.392 0-429.922-9.372-9.37-21.656-14.056-33.94-14.056zM401.332 881.332c25.668 25.668 46.668 16.968 46.668-19.332v-828c0-36.3-21-44.998-46.668-19.33l-241.332 241.33h-160v384h160l241.332 241.332z" />
<glyph unicode="&#xe005;" d="M719.53 128.47c-12.286 0-24.568 4.686-33.942 14.058-18.744 18.744-18.744 49.136 0 67.882 131.006 131.006 131.006 344.17 0 475.176-18.744 18.746-18.744 49.138 0 67.882 18.744 18.742 49.138 18.744 67.882 0 81.594-81.592 126.53-190.076 126.53-305.468 0-115.39-44.936-223.876-126.53-305.47-9.372-9.374-21.656-14.060-33.94-14.060zM549.020 218.98c-12.286 0-24.568 4.686-33.942 14.058-18.746 18.746-18.746 49.136 0 67.882 81.1 81.1 81.1 213.058 0 294.156-18.746 18.746-18.746 49.138 0 67.882 18.746 18.744 49.136 18.744 67.882 0 118.53-118.53 118.53-311.392 0-429.922-9.372-9.37-21.656-14.056-33.94-14.056zM401.332 881.332c25.668 25.668 46.668 16.968 46.668-19.332v-828c0-36.3-21-44.998-46.668-19.33l-241.332 241.33h-160v384h160l241.332 241.332z" />
<glyph unicode="&#xe006;" d="M890.040 37.96c-12.286 0-24.568 4.686-33.942 14.058-18.744 18.746-18.744 49.136 0 67.882 87.638 87.642 135.904 204.16 135.904 328.1 0 123.938-48.266 240.458-135.904 328.098-18.744 18.746-18.744 49.138 0 67.882 18.744 18.744 49.138 18.744 67.882 0 105.77-105.772 164.022-246.4 164.022-395.98 0-149.582-58.252-290.208-164.022-395.98-9.372-9.374-21.656-14.060-33.94-14.060zM719.53 128.47c-12.286 0-24.568 4.686-33.942 14.058-18.744 18.744-18.744 49.136 0 67.882 131.006 131.006 131.006 344.17 0 475.176-18.744 18.746-18.744 49.138 0 67.882 18.744 18.742 49.138 18.744 67.882 0 81.594-81.592 126.53-190.076 126.53-305.468 0-115.39-44.936-223.876-126.53-305.47-9.372-9.374-21.656-14.060-33.94-14.060zM549.020 218.98c-12.286 0-24.568 4.686-33.942 14.058-18.746 18.746-18.746 49.136 0 67.882 81.1 81.1 81.1 213.058 0 294.156-18.746 18.746-18.746 49.138 0 67.882 18.746 18.744 49.136 18.744 67.882 0 118.53-118.53 118.53-311.392 0-429.922-9.372-9.37-21.656-14.056-33.94-14.056zM401.332 881.332c25.668 25.668 46.668 16.968 46.668-19.332v-828c0-36.3-21-44.998-46.668-19.33l-241.332 241.33h-160v384h160l241.332 241.332z" horiz-adv-x="1088" />
<glyph unicode="&#xe007;" d="M512 960l-320-512 320-512 320 512z" />
<glyph unicode="&#xe008;" d="M0 960h1374.316v-1030.414h-1374.316v1030.414zM1245.462 449.276c-1.706 180.052-8.542 258.568-51.2 314.036-7.68 11.946-22.186 18.772-34.132 27.296-41.814 30.73-238.944 41.814-467.636 41.814-228.702 0-435.21-11.084-476.17-41.814-12.8-8.524-27.316-15.35-35.84-27.296-41.822-55.468-47.786-133.984-50.346-314.036 2.56-180.062 8.524-258.57 50.346-314.036 8.524-12.8 23.040-18.774 35.84-27.306 40.96-31.574 247.468-41.814 476.17-43.52 228.692 1.706 425.822 11.946 467.636 43.52 11.946 8.532 26.452 14.506 34.132 27.306 42.658 55.466 49.494 133.974 51.2 314.036zM662.358 495.904c-11.58 140.898-86.51 223.906-220.556 223.906-122.458 0-218.722-110.432-218.722-287.88 0-178.212 87.73-289.396 232.734-289.396 115.766 0 196.798 85.298 209.588 226.95h-138.302c-5.48-52.548-27.414-92.914-73.72-92.914-73.108 0-86.51 72.354-86.51 149.27 0 105.868 30.46 159.932 81.032 159.932 45.082 0 73.718-32.75 77.976-89.868h136.48zM1140.026 495.904c-11.57 140.898-86.51 223.906-220.546 223.906-122.466 0-218.722-110.432-218.722-287.88 0-178.212 87.73-289.396 232.734-289.396 115.758 0 196.788 85.298 209.58 226.95h-138.304c-5.47-52.548-27.404-92.914-73.71-92.914-73.116 0-86.518 72.354-86.518 149.27 0 105.868 30.468 159.932 81.030 159.932 45.084 0 73.728-32.75 77.986-89.868h136.47z" horiz-adv-x="1374" />
<glyph unicode="&#xe009;" d="M128 832h768v-768h-768z" />
<glyph unicode="&#xe00a;" d="M384 832c0-70.692 57.308-128 128-128s128 57.308 128 128c0 70.692-57.308 128-128 128-70.692 0-128-57.308-128-128zM655.53 719.53c0-70.692 57.308-128 128-128s128 57.308 128 128c0 70.692-57.308 128-128 128-70.692 0-128-57.308-128-128zM832 448c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64zM719.53 176.47c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64zM448.002 64c0 0 0 0 0 0 0-35.346 28.654-64 64-64 35.346 0 64 28.654 64 64 0 0 0 0 0 0 0 0 0 0 0 0 0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64s0 0 0 0zM176.472 176.47c0 0 0 0 0 0 0-35.346 28.654-64 64-64 35.346 0 64 28.654 64 64 0 0 0 0 0 0 0 0 0 0 0 0 0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64s0 0 0 0zM144.472 719.53c0 0 0 0 0 0 0-53.019 42.981-96 96-96 53.019 0 96 42.981 96 96 0 0 0 0 0 0 0 0 0 0 0 0 0 53.019-42.981 96-96 96-53.019 0-96-42.981-96-96s0 0 0 0zM56 448c0-39.765 32.235-72 72-72s72 32.235 72 72c0 39.765-32.235 72-72 72-39.765 0-72-32.235-72-72z" />
<glyph unicode="&#xe00b;" d="M448 384v-416l-160 160-192-192-96 96 192 192-160 160zM1024 864l-192-192 160-160h-416v416l160-160 192 192z" />
<glyph unicode="&#xe00c;" d="M512 896c282.77 0 512-186.25 512-416 0-229.752-229.23-416-512-416-27.156 0-53.81 1.734-79.824 5.044-109.978-109.978-241.25-129.7-368.176-132.596v26.916c68.536 33.578 128 94.74 128 164.636 0 9.754-0.758 19.33-2.164 28.696-115.796 76.264-189.836 192.754-189.836 323.304 0 229.75 229.23 416 512 416z" />
<glyph unicode="&#xe00d;" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 704c141.384 0 256-114.616 256-256s-114.616-256-256-256-256 114.616-256 256 114.616 256 256 256zM817.47 142.53c-81.594-81.594-190.080-126.53-305.47-126.53-115.392 0-223.876 44.936-305.47 126.53-81.594 81.594-126.53 190.078-126.53 305.47 0 115.39 44.936 223.876 126.53 305.47l67.882-67.882c0 0 0 0 0 0-131.006-131.006-131.006-344.17 0-475.176 63.462-63.462 147.838-98.412 237.588-98.412 89.748 0 174.124 34.95 237.588 98.412 131.006 131.006 131.006 344.168 0 475.176l67.882 67.882c81.594-81.594 126.53-190.080 126.53-305.47 0-115.392-44.936-223.876-126.53-305.47z" />
<glyph unicode="&#xe00e;" d="M864 256c-45.16 0-85.92-18.738-115.012-48.83l-431.004 215.502c1.314 8.252 2.016 16.706 2.016 25.328s-0.702 17.076-2.016 25.326l431.004 215.502c29.092-30.090 69.852-48.828 115.012-48.828 88.366 0 160 71.634 160 160s-71.634 160-160 160-160-71.634-160-160c0-8.622 0.704-17.076 2.016-25.326l-431.004-215.504c-29.092 30.090-69.852 48.83-115.012 48.83-88.366 0-160-71.636-160-160 0-88.368 71.634-160 160-160 45.16 0 85.92 18.738 115.012 48.828l431.004-215.502c-1.312-8.25-2.016-16.704-2.016-25.326 0-88.368 71.634-160 160-160s160 71.632 160 160c0 88.364-71.634 160-160 160z" />
<glyph unicode="&#xe01e;" d="M1024 448c-1.278 66.862-15.784 133.516-42.576 194.462-26.704 61-65.462 116.258-113.042 161.92-47.552 45.696-103.944 81.82-164.984 105.652-61.004 23.924-126.596 35.352-191.398 33.966-64.81-1.282-129.332-15.374-188.334-41.356-59.048-25.896-112.542-63.47-156.734-109.576-44.224-46.082-79.16-100.708-102.186-159.798-23.114-59.062-34.128-122.52-32.746-185.27 1.286-62.76 14.964-125.148 40.134-182.206 25.088-57.1 61.476-108.828 106.11-151.548 44.61-42.754 97.472-76.504 154.614-98.72 57.118-22.304 118.446-32.902 179.142-31.526 60.708 1.29 120.962 14.554 176.076 38.914 55.15 24.282 105.116 59.48 146.366 102.644 41.282 43.14 73.844 94.236 95.254 149.43 13.034 33.458 21.88 68.4 26.542 103.798 1.246-0.072 2.498-0.12 3.762-0.12 35.346 0 64 28.652 64 64 0 1.796-0.094 3.572-0.238 5.332h0.238zM922.306 278.052c-23.472-53.202-57.484-101.4-99.178-141.18-41.67-39.81-91-71.186-144.244-91.79-53.228-20.678-110.29-30.452-166.884-29.082-56.604 1.298-112.596 13.736-163.82 36.474-51.25 22.666-97.684 55.49-135.994 95.712-38.338 40.198-68.528 87.764-88.322 139.058-19.87 51.284-29.228 106.214-27.864 160.756 1.302 54.552 13.328 108.412 35.254 157.69 21.858 49.3 53.498 93.97 92.246 130.81 38.73 36.868 84.53 65.87 133.874 84.856 49.338 19.060 102.136 28.006 154.626 26.644 52.5-1.306 104.228-12.918 151.562-34.034 47.352-21.050 90.256-51.502 125.624-88.782 35.396-37.258 63.21-81.294 81.39-128.688 18.248-47.392 26.782-98.058 25.424-148.496h0.238c-0.144-1.76-0.238-3.536-0.238-5.332 0-33.012 24.992-60.174 57.086-63.624-6.224-34.822-16.53-68.818-30.78-100.992z" />
<glyph unicode="&#xe01f;" d="M512 960c-278.748 0-505.458-222.762-511.848-499.974 5.92 241.864 189.832 435.974 415.848 435.974 229.75 0 416-200.576 416-448 0-53.020 42.98-96 96-96 53.020 0 96 42.98 96 96 0 282.77-229.23 512-512 512zM512-64c278.748 0 505.458 222.762 511.848 499.974-5.92-241.864-189.832-435.974-415.848-435.974-229.75 0-416 200.576-416 448 0 53.020-42.98 96-96 96-53.020 0-96-42.98-96-96 0-282.77 229.23-512 512-512z" />
<glyph unicode="&#xe600;" d="M1024 351.906v192.188l-146.774 24.462c-5.958 18.132-13.222 35.668-21.694 52.5l86.454 121.034-135.896 135.898-120.826-86.304c-16.91 8.554-34.538 15.888-52.768 21.902l-24.402 146.414h-192.188l-24.402-146.416c-18.23-6.014-35.858-13.348-52.766-21.902l-120.828 86.304-135.898-135.898 86.454-121.036c-8.47-16.83-15.734-34.366-21.692-52.498l-146.774-24.46v-192.188l147.118-24.52c5.96-17.968 13.21-35.348 21.642-52.030l-86.748-121.448 135.898-135.896 121.654 86.894c16.602-8.35 33.89-15.528 51.764-21.434l24.578-147.472h192.188l24.578 147.474c17.874 5.906 35.162 13.084 51.766 21.432l121.652-86.892 135.896 135.896-86.744 121.446c8.432 16.682 15.678 34.062 21.64 52.032l147.118 24.518zM512 320c-70.692 0-128 57.306-128 128 0 70.692 57.308 128 128 128 70.694 0 128-57.308 128-128 0-70.694-57.306-128-128-128z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 10 KiB

BIN
www/css/font/vjs.ttf Normal file

Binary file not shown.

BIN
www/css/font/vjs.woff Normal file

Binary file not shown.

1011
www/css/video-js.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -826,64 +826,51 @@ Callbacks = {
return;
}
/* Failsafe */
// Failsafe
if (isNaN(VOLUME) || VOLUME > 1 || VOLUME < 0) {
VOLUME = 1;
}
var shouldResize = $("#ytapiplayer").html() === "";
function loadNext() {
if (!PLAYER || data.type !== PLAYER.mediaType) {
loadMediaPlayer(data);
}
handleMediaUpdate(data);
}
// Persist the user's volume preference from the the player, if possible
if (PLAYER && typeof PLAYER.getVolume === "function") {
PLAYER.getVolume(function (v) {
if (typeof v === "number") {
if (v < 0 || v > 1) {
// Dailymotion's API was wrong once and caused a huge
// headache. This alert is here to make debugging easier.
alert("Something went wrong with retrieving the volume. " +
"Please tell calzoneman the following: " +
JSON.stringify({ v: v, t: PLAYER.type, i: PLAYER.videoId }));
JSON.stringify({
v: v,
t: PLAYER.mediaType,
i: PLAYER.mediaId
}));
} else {
VOLUME = v;
setOpt("volume", VOLUME);
}
}
loadNext();
});
} else {
loadNext();
}
if (CHANNEL.opts.allow_voteskip)
// Reset voteskip since the video changed
if (CHANNEL.opts.allow_voteskip) {
$("#voteskip").attr("disabled", false);
}
$("#currenttitle").text("Currently Playing: " + data.title);
if (data.type != "sc" && PLAYER.type == "sc")
// [](/goddamnitmango)
fixSoundcloudShit();
if (data.type != "jw" && PLAYER.type == "jw") {
// Is it so hard to not mess up my DOM?
$("<div/>").attr("id", "ytapiplayer")
.insertBefore($("#ytapiplayer_wrapper"));
$("#ytapiplayer_wrapper").remove();
}
if (data.type === "fi") {
data.url = data.id;
}
if (NO_VIMEO && data.type === "vi" && data.meta.direct) {
data = vimeoSimulator2014(data);
}
/*
* Google Docs now uses the same simulator as Google+
*/
if (data.type === "gp" || data.type === "gd") {
data = googlePlusSimulator2014(data);
}
if (data.type != PLAYER.type) {
loadMediaPlayer(data);
}
handleMediaUpdate(data);
},
mediaUpdate: function(data) {
@ -891,7 +878,9 @@ Callbacks = {
return;
}
handleMediaUpdate(data);
if (PLAYER) {
handleMediaUpdate(data);
}
},
setPlaylistLocked: function (locked) {
@ -1100,7 +1089,7 @@ setupCallbacks = function() {
Callbacks[key](data);
} catch (e) {
if (SOCKET_DEBUG) {
console.log("EXCEPTION: " + e.stack);
console.log("EXCEPTION: " + e + "\n" + e.stack);
}
}
});

View File

@ -25,6 +25,7 @@ var CHANNEL = {
};
var PLAYER = false;
var LIVESTREAM_CHROMELESS = false;
var FLUIDLAYOUT = false;
var VWIDTH;
var VHEIGHT;
@ -112,7 +113,7 @@ var USEROPTS = {
ignore_channeljs : getOrDefault("ignore_channeljs", false),
sort_rank : getOrDefault("sort_rank", true),
sort_afk : getOrDefault("sort_afk", false),
default_quality : getOrDefault("default_quality", ""),
default_quality : getOrDefault("default_quality", "auto"),
boop : getOrDefault("boop", "never"),
secure_connection : getOrDefault("secure_connection", false),
show_shadowchat : getOrDefault("show_shadowchat", false),
@ -140,6 +141,22 @@ if (["never", "onlyping", "always"].indexOf(USEROPTS.boop) === -1) {
USEROPTS.boop = "onlyping";
}
// As of 3.8, preferred quality names are different
(function () {
var fix = {
small: "240",
medium: "360",
large: "480",
hd720: "720",
hd1080: "1080",
highres: "best"
};
if (fix.hasOwnProperty(USEROPTS.default_quality)) {
USEROPTS.default_quality = fix[USEROPTS.default_quality];
}
})();
var VOLUME = parseFloat(getOrDefault("volume", 1));
var NO_WEBSOCKETS = USEROPTS.altsocket;

View File

@ -1,96 +0,0 @@
"undefined"==typeof jwplayer&&(jwplayer=function(d){if(jwplayer.api)return jwplayer.api.selectPlayer(d)},jwplayer.version="6.6.3896",jwplayer.vid=document.createElement("video"),jwplayer.audio=document.createElement("audio"),jwplayer.source=document.createElement("source"),function(d){function a(b){return function(){return c(b)}}function k(b){return function(){b("Error loading file")}}function f(m,a,e,g){return function(){try{var c=m.responseXML;if(c&&c.firstChild)return e(m)}catch(j){}(c=b.parseXML(m.responseText))&&
c.firstChild?(m=b.extend({},m,{responseXML:c}),e(m)):g&&g(m.responseText?"Invalid XML":a)}}var h=document,e=window,j=navigator,b=d.utils=function(){};b.exists=function(b){switch(typeof b){case "string":return 0<b.length;case "object":return null!==b;case "undefined":return!1}return!0};b.styleDimension=function(b){return b+(0<b.toString().indexOf("%")?"":"px")};b.getAbsolutePath=function(a,e){b.exists(e)||(e=h.location.href);if(b.exists(a)){var c;if(b.exists(a)){c=a.indexOf("://");var g=a.indexOf("?");
c=0<c&&(0>g||g>c)}else c=void 0;if(c)return a;c=e.substring(0,e.indexOf("://")+3);var g=e.substring(c.length,e.indexOf("/",c.length+1)),j;0===a.indexOf("/")?j=a.split("/"):(j=e.split("?")[0],j=j.substring(c.length+g.length+1,j.lastIndexOf("/")),j=j.split("/").concat(a.split("/")));for(var f=[],t=0;t<j.length;t++)j[t]&&(b.exists(j[t])&&"."!=j[t])&&(".."==j[t]?f.pop():f.push(j[t]));return c+g+"/"+f.join("/")}};b.extend=function(){var a=b.extend.arguments;if(1<a.length){for(var e=1;e<a.length;e++)b.foreach(a[e],
function(e,g){try{b.exists(g)&&(a[0][e]=g)}catch(c){}});return a[0]}return null};b.log=function(b,a){"undefined"!=typeof console&&"undefined"!=typeof console.log&&(a?console.log(b,a):console.log(b))};var c=b.userAgentMatch=function(b){return null!==j.userAgent.toLowerCase().match(b)};b.isIE=a(/msie/i);b.isFF=a(/firefox/i);b.isChrome=a(/chrome/i);b.isIOS=a(/iP(hone|ad|od)/i);b.isIPod=a(/iP(hone|od)/i);b.isIPad=a(/iPad/i);b.isSafari602=a(/Macintosh.*Mac OS X 10_8.*6\.0\.\d* Safari/i);b.isSafari=function(){return c(/safari/i)&&
!c(/chrome/i)&&!c(/chromium/i)&&!c(/android/i)};b.isAndroid=function(b){return b?c(RegExp("android.*"+b,"i")):c(/android/i)};b.isMobile=function(){return b.isIOS()||b.isAndroid()};b.saveCookie=function(b,a){h.cookie="jwplayer."+b+"\x3d"+a+"; path\x3d/"};b.getCookies=function(){for(var b={},a=h.cookie.split("; "),e=0;e<a.length;e++){var g=a[e].split("\x3d");0==g[0].indexOf("jwplayer.")&&(b[g[0].substring(9,g[0].length)]=g[1])}return b};b.typeOf=function(b){var a=typeof b;return"object"===a?!b?"null":
b instanceof Array?"array":a:a};b.translateEventResponse=function(a,e){var c=b.extend({},e);a==d.events.JWPLAYER_FULLSCREEN&&!c.fullscreen?(c.fullscreen="true"==c.message?!0:!1,delete c.message):"object"==typeof c.data?(c=b.extend(c,c.data),delete c.data):"object"==typeof c.metadata&&b.deepReplaceKeyName(c.metadata,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]);b.foreach(["position","duration","offset"],function(b,a){c[a]&&(c[a]=Math.round(1E3*c[a])/1E3)});return c};b.flashVersion=
function(){if(b.isAndroid())return 0;var a=j.plugins,c;try{if("undefined"!==a&&(c=a["Shockwave Flash"]))return parseInt(c.description.replace(/\D+(\d+)\..*/,"$1"))}catch(f){}if("undefined"!=typeof e.ActiveXObject)try{if(c=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseInt(c.GetVariable("$version").split(" ")[1].split(",")[0])}catch(g){}return 0};b.getScriptPath=function(b){for(var a=h.getElementsByTagName("script"),c=0;c<a.length;c++){var g=a[c].src;if(g&&0<=g.indexOf(b))return g.substr(0,
g.indexOf(b))}return""};b.deepReplaceKeyName=function(a,c,e){switch(d.utils.typeOf(a)){case "array":for(var g=0;g<a.length;g++)a[g]=d.utils.deepReplaceKeyName(a[g],c,e);break;case "object":b.foreach(a,function(b,g){var j;if(c instanceof Array&&e instanceof Array){if(c.length!=e.length)return;j=c}else j=[c];for(var f=b,h=0;h<j.length;h++)f=f.replace(RegExp(c[h],"g"),e[h]);a[f]=d.utils.deepReplaceKeyName(g,c,e);b!=f&&delete a[b]})}return a};var n=b.pluginPathType={ABSOLUTE:0,RELATIVE:1,CDN:2};b.getPluginPathType=
function(a){if("string"==typeof a){a=a.split("?")[0];var c=a.indexOf("://");if(0<c)return n.ABSOLUTE;var e=a.indexOf("/");a=b.extension(a);return 0>c&&0>e&&(!a||!isNaN(a))?n.CDN:n.RELATIVE}};b.getPluginName=function(b){return b.replace(/^(.*\/)?([^-]*)-?.*\.(swf|js)$/,"$2")};b.getPluginVersion=function(b){return b.replace(/[^-]*-?([^\.]*).*$/,"$1")};b.isYouTube=function(b){return/^(http|\/\/).*(youtube\.com|youtu\.be)\/.+/.test(b)};b.youTubeID=function(b){try{return/v[=\/]([^?&]*)|youtu\.be\/([^?]*)|^([\w-]*)$/i.exec(b).slice(1).join("").replace("?",
"")}catch(a){return""}};b.isRtmp=function(b,a){return 0==b.indexOf("rtmp")||"rtmp"==a};b.foreach=function(a,c){var e,g;for(e in a)"function"==b.typeOf(a.hasOwnProperty)?a.hasOwnProperty(e)&&(g=a[e],c(e,g)):(g=a[e],c(e,g))};b.isHTTPS=function(){return 0==e.location.href.indexOf("https")};b.repo=function(){var a="http://p.jwpcdn.com/"+d.version.split(/\W/).splice(0,2).join("/")+"/";try{b.isHTTPS()&&(a=a.replace("http://","https://ssl."))}catch(c){}return a};b.ajax=function(a,c,j){var g;0<a.indexOf("#")&&
(a=a.replace(/#.*$/,""));var h;h=(h=a)&&0<=h.indexOf("://")&&h.split("/")[2]!=e.location.href.split("/")[2]?!0:!1;if(h&&b.exists(e.XDomainRequest))g=new XDomainRequest,g.onload=f(g,a,c,j),g.onerror=k(j,a,g);else if(b.exists(e.XMLHttpRequest)){var d=g=new XMLHttpRequest,t=a;g.onreadystatechange=function(){if(4===d.readyState)switch(d.status){case 200:f(d,t,c,j)();break;case 404:j("File not found")}};g.onerror=k(j,a)}else j&&j();try{g.open("GET",a,!0),g.send(null)}catch(n){j&&j(a)}return g};b.parseXML=
function(b){try{var a;if(e.DOMParser){a=(new DOMParser).parseFromString(b,"text/xml");try{if("parsererror"==a.childNodes[0].firstChild.nodeName)return}catch(c){}}else a=new ActiveXObject("Microsoft.XMLDOM"),a.async="false",a.loadXML(b);return a}catch(g){}};b.filterPlaylist=function(a,c){for(var e=[],g=0;g<a.length;g++){var j=b.extend({},a[g]);j.sources=b.filterSources(j.sources);if(0<j.sources.length){for(var f=0;f<j.sources.length;f++){var h=j.sources[f];h.label||(h.label=f.toString())}e.push(j)}}if(c&&
0==e.length)for(g=0;g<a.length;g++)if(j=b.extend({},a[g]),j.sources=b.filterSources(j.sources,!0),0<j.sources.length){for(f=0;f<j.sources.length;f++)h=j.sources[f],h.label||(h.label=f.toString());e.push(j)}return e};b.filterSources=function(a,c){var e,g,j=b.extensionmap;if(a){g=[];for(var f=0;f<a.length;f++){var h=a[f].type,n=a[f].file;n&&n.trim&&(n=n.trim());h||(h=j.extType(b.extension(n)),a[f].type=h);c?d.embed.flashCanPlay(n,h)&&(e||(e=h),h==e&&g.push(b.extend({},a[f]))):b.canPlayHTML5(h)&&(e||
(e=h),h==e&&g.push(b.extend({},a[f])))}}return g};b.canPlayHTML5=function(a){if(b.isAndroid()&&("hls"==a||"m3u"==a||"m3u8"==a))return!1;a=b.extensionmap.types[a];return!!a&&!!d.vid.canPlayType&&d.vid.canPlayType(a)};b.seconds=function(a){a=a.replace(",",".");var b=a.split(":"),c=0;"s"==a.substr(-1)?c=Number(a.substr(0,a.length-1)):"m"==a.substr(-1)?c=60*Number(a.substr(0,a.length-1)):"h"==a.substr(-1)?c=3600*Number(a.substr(0,a.length-1)):1<b.length?(c=Number(b[b.length-1]),c+=60*Number(b[b.length-
2]),3==b.length&&(c+=3600*Number(b[b.length-3]))):c=Number(a);return c};b.serialize=function(a){return null==a?null:"true"==a.toString().toLowerCase()?!0:"false"==a.toString().toLowerCase()?!1:isNaN(Number(a))||5<a.length||0==a.length?a:Number(a)}}(jwplayer),function(d){var a="video/",k=d.foreach,f={mp4:a+"mp4",vorbis:"audio/ogg",ogg:a+"ogg",webm:a+"webm",aac:"audio/mp4",mp3:"audio/mpeg",hls:"application/vnd.apple.mpegurl"},h={mp4:f.mp4,f4v:f.mp4,m4v:f.mp4,mov:f.mp4,m4a:f.aac,f4a:f.aac,aac:f.aac,
mp3:f.mp3,ogv:f.ogg,ogg:f.vorbis,oga:f.vorbis,webm:f.webm,m3u8:f.hls,hls:f.hls},a="video",a={flv:a,f4v:a,mov:a,m4a:a,m4v:a,mp4:a,aac:a,f4a:a,mp3:"sound",smil:"rtmp",m3u8:"hls",hls:"hls"},e=d.extensionmap={};k(h,function(a,b){e[a]={html5:b}});k(a,function(a,b){e[a]||(e[a]={});e[a].flash=b});e.types=f;e.mimeType=function(a){var b;k(f,function(c,e){!b&&e==a&&(b=c)});return b};e.extType=function(a){return e.mimeType(h[a])}}(jwplayer.utils),function(d){var a=d.loaderstatus={NEW:0,LOADING:1,ERROR:2,COMPLETE:3},
k=document;d.scriptloader=function(f){function h(){j=a.ERROR;c.sendEvent(b.ERROR)}function e(){j=a.COMPLETE;c.sendEvent(b.COMPLETE)}var j=a.NEW,b=jwplayer.events,c=new b.eventdispatcher;d.extend(this,c);this.load=function(){var c=d.scriptloader.loaders[f];if(c&&(c.getStatus()==a.NEW||c.getStatus()==a.LOADING))c.addEventListener(b.ERROR,h),c.addEventListener(b.COMPLETE,e);else if(d.scriptloader.loaders[f]=this,j==a.NEW){j=a.LOADING;var m=k.createElement("script");m.addEventListener?(m.onload=e,m.onerror=
h):m.readyState&&(m.onreadystatechange=function(){("loaded"==m.readyState||"complete"==m.readyState)&&e()});k.getElementsByTagName("head")[0].appendChild(m);m.src=f}};this.getStatus=function(){return j}};d.scriptloader.loaders={}}(jwplayer.utils),function(d){d.trim=function(a){return a.replace(/^\s*/,"").replace(/\s*$/,"")};d.pad=function(a,d,f){for(f||(f="0");a.length<d;)a=f+a;return a};d.xmlAttribute=function(a,d){for(var f=0;f<a.attributes.length;f++)if(a.attributes[f].name&&a.attributes[f].name.toLowerCase()==
d.toLowerCase())return a.attributes[f].value.toString();return""};d.extension=function(a){if(!a||"rtmp"==a.substr(0,4))return"";a=a.substring(a.lastIndexOf("/")+1,a.length).split("?")[0].split("#")[0];if(-1<a.lastIndexOf("."))return a.substr(a.lastIndexOf(".")+1,a.length).toLowerCase()};d.stringToColor=function(a){a=a.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2));return parseInt(a,16)}}(jwplayer.utils),function(d){var a=
"touchmove",k="touchstart";d.touch=function(f){function h(b){b.type==k?(c=!0,m=j(l.DRAG_START,b)):b.type==a?c&&(p||(e(l.DRAG_START,b,m),p=!0),e(l.DRAG,b)):(c&&(p?e(l.DRAG_END,b):(b.cancelBubble=!0,e(l.TAP,b))),c=p=!1,m=null)}function e(a,b,c){if(n[a]&&(b.preventManipulation&&b.preventManipulation(),b.preventDefault&&b.preventDefault(),b=c?c:j(a,b)))n[a](b)}function j(a,c){var e=null;c.touches&&c.touches.length?e=c.touches[0]:c.changedTouches&&c.changedTouches.length&&(e=c.changedTouches[0]);if(!e)return null;
var j=b.getBoundingClientRect(),e={type:a,target:b,x:e.pageX-window.pageXOffset-j.left,y:e.pageY,deltaX:0,deltaY:0};a!=l.TAP&&m&&(e.deltaX=e.x-m.x,e.deltaY=e.y-m.y);return e}var b=f,c=!1,n={},m=null,p=!1,l=d.touchEvents;document.addEventListener(a,h);document.addEventListener("touchend",function(a){c&&p&&e(l.DRAG_END,a);c=p=!1;m=null});document.addEventListener("touchcancel",h);f.addEventListener(k,h);f.addEventListener("touchend",h);this.addEventListener=function(a,b){n[a]=b};this.removeEventListener=
function(a){delete n[a]};return this}}(jwplayer.utils),function(d){d.touchEvents={DRAG:"jwplayerDrag",DRAG_START:"jwplayerDragStart",DRAG_END:"jwplayerDragEnd",TAP:"jwplayerTap"}}(jwplayer.utils),function(d){d.key=function(a){var k,f,h;this.edition=function(){return h&&h.getTime()<(new Date).getTime()?"invalid":k};this.token=function(){return f};d.exists(a)||(a="");try{a=d.tea.decrypt(a,"36QXq4W@GSBV^teR");var e=a.split("/");(k=e[0])?/^(free|pro|premium|ads)$/i.test(k)?(f=e[1],e[2]&&0<parseInt(e[2])&&
(h=new Date,h.setTime(String(e[2])))):k="invalid":k="free"}catch(j){k="invalid"}}}(jwplayer.utils),function(d){var a=d.tea={};a.encrypt=function(h,e){if(0==h.length)return"";var j=a.strToLongs(f.encode(h));1>=j.length&&(j[1]=0);for(var b=a.strToLongs(f.encode(e).slice(0,16)),c=j.length,d=j[c-1],m=j[0],p,l=Math.floor(6+52/c),g=0;0<l--;){g+=2654435769;p=g>>>2&3;for(var q=0;q<c;q++)m=j[(q+1)%c],d=(d>>>5^m<<2)+(m>>>3^d<<4)^(g^m)+(b[q&3^p]^d),d=j[q]+=d}j=a.longsToStr(j);return k.encode(j)};a.decrypt=function(h,
e){if(0==h.length)return"";for(var j=a.strToLongs(k.decode(h)),b=a.strToLongs(f.encode(e).slice(0,16)),c=j.length,d=j[c-1],m=j[0],p,l=2654435769*Math.floor(6+52/c);0!=l;){p=l>>>2&3;for(var g=c-1;0<=g;g--)d=j[0<g?g-1:c-1],d=(d>>>5^m<<2)+(m>>>3^d<<4)^(l^m)+(b[g&3^p]^d),m=j[g]-=d;l-=2654435769}j=a.longsToStr(j);j=j.replace(/\0+$/,"");return f.decode(j)};a.strToLongs=function(a){for(var e=Array(Math.ceil(a.length/4)),j=0;j<e.length;j++)e[j]=a.charCodeAt(4*j)+(a.charCodeAt(4*j+1)<<8)+(a.charCodeAt(4*j+
2)<<16)+(a.charCodeAt(4*j+3)<<24);return e};a.longsToStr=function(a){for(var e=Array(a.length),j=0;j<a.length;j++)e[j]=String.fromCharCode(a[j]&255,a[j]>>>8&255,a[j]>>>16&255,a[j]>>>24&255);return e.join("")};var k={code:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d",encode:function(a,e){var j,b,c,d,m=[],p="",l,g,q=k.code;g=("undefined"==typeof e?0:e)?f.encode(a):a;l=g.length%3;if(0<l)for(;3>l++;)p+="\x3d",g+="\x00";for(l=0;l<g.length;l+=3)j=g.charCodeAt(l),b=g.charCodeAt(l+
1),c=g.charCodeAt(l+2),d=j<<16|b<<8|c,j=d>>18&63,b=d>>12&63,c=d>>6&63,d&=63,m[l/3]=q.charAt(j)+q.charAt(b)+q.charAt(c)+q.charAt(d);m=m.join("");return m=m.slice(0,m.length-p.length)+p},decode:function(a,e){e="undefined"==typeof e?!1:e;var j,b,c,d,m,p=[],l,g=k.code;l=e?f.decode(a):a;for(var q=0;q<l.length;q+=4)j=g.indexOf(l.charAt(q)),b=g.indexOf(l.charAt(q+1)),d=g.indexOf(l.charAt(q+2)),m=g.indexOf(l.charAt(q+3)),c=j<<18|b<<12|d<<6|m,j=c>>>16&255,b=c>>>8&255,c&=255,p[q/4]=String.fromCharCode(j,b,
c),64==m&&(p[q/4]=String.fromCharCode(j,b)),64==d&&(p[q/4]=String.fromCharCode(j));d=p.join("");return e?f.decode(d):d}},f={encode:function(a){a=a.replace(/[\u0080-\u07ff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(192|a>>6,128|a&63)});return a=a.replace(/[\u0800-\uffff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(224|a>>12,128|a>>6&63,128|a&63)})},decode:function(a){a=a.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&15)<<12|
(a.charCodeAt(1)&63)<<6|a.charCodeAt(2)&63;return String.fromCharCode(a)});return a=a.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&31)<<6|a.charCodeAt(1)&63;return String.fromCharCode(a)})}}}(jwplayer.utils),function(d){d.events={COMPLETE:"COMPLETE",ERROR:"ERROR",API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_SETUP_ERROR:"jwplayerSetupError",JWPLAYER_MEDIA_BEFOREPLAY:"jwplayerMediaBeforePlay",
JWPLAYER_MEDIA_BEFORECOMPLETE:"jwplayerMediaBeforeComplete",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",
JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_MEDIA_LEVELS:"jwplayerMediaLevels",JWPLAYER_MEDIA_LEVEL_CHANGED:"jwplayerMediaLevelChanged",JWPLAYER_CAPTIONS_CHANGED:"jwplayerCaptionsChanged",JWPLAYER_CAPTIONS_LIST:"jwplayerCaptionsList",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",state:{BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"},JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem",JWPLAYER_PLAYLIST_COMPLETE:"jwplayerPlaylistComplete",
JWPLAYER_DISPLAY_CLICK:"jwplayerViewClick",JWPLAYER_CONTROLS:"jwplayerViewControls",JWPLAYER_USER_ACTION:"jwplayerUserAction",JWPLAYER_INSTREAM_CLICK:"jwplayerInstreamClicked",JWPLAYER_INSTREAM_DESTROYED:"jwplayerInstreamDestroyed",JWPLAYER_AD_TIME:"jwplayerAdTime",JWPLAYER_AD_ERROR:"jwplayerAdError",JWPLAYER_AD_CLICK:"jwplayerAdClicked",JWPLAYER_AD_COMPLETE:"jwplayerAdComplete",JWPLAYER_AD_IMPRESSION:"jwplayerAdImpression",JWPLAYER_AD_COMPANIONS:"jwplayerAdCompanions"}}(jwplayer),function(d){var a=
jwplayer.utils;d.eventdispatcher=function(d,f){var h,e;this.resetEventListeners=function(){h={};e=[]};this.resetEventListeners();this.addEventListener=function(e,b,c){try{a.exists(h[e])||(h[e]=[]),"string"==a.typeOf(b)&&(b=(new Function("return "+b))()),h[e].push({listener:b,count:c})}catch(f){a.log("error",f)}return!1};this.removeEventListener=function(e,b){if(h[e]){try{for(var c=0;c<h[e].length;c++)if(h[e][c].listener.toString()==b.toString()){h[e].splice(c,1);break}}catch(f){a.log("error",f)}return!1}};
this.addGlobalListener=function(f,b){try{"string"==a.typeOf(f)&&(f=(new Function("return "+f))()),e.push({listener:f,count:b})}catch(c){a.log("error",c)}return!1};this.removeGlobalListener=function(f){if(f){try{for(var b=0;b<e.length;b++)if(e[b].listener.toString()==f.toString()){e.splice(b,1);break}}catch(c){a.log("error",c)}return!1}};this.sendEvent=function(j,b){a.exists(b)||(b={});a.extend(b,{id:d,version:jwplayer.version,type:j});f&&a.log(j,b);if("undefined"!=a.typeOf(h[j]))for(var c=0;c<h[j].length;c++){try{h[j][c].listener(b)}catch(n){a.log("There was an error while handling a listener: "+
n.toString(),h[j][c].listener)}h[j][c]&&(1===h[j][c].count?delete h[j][c]:0<h[j][c].count&&(h[j][c].count-=1))}for(c=0;c<e.length;c++){try{e[c].listener(b)}catch(m){a.log("There was an error while handling a listener: "+m.toString(),e[c].listener)}e[c]&&(1===e[c].count?delete e[c]:0<e[c].count&&(e[c].count-=1))}}}}(jwplayer.events),function(d){var a={},k={};d.plugins=function(){};d.plugins.loadPlugins=function(f,h){k[f]=new d.plugins.pluginloader(new d.plugins.model(a),h);return k[f]};d.plugins.registerPlugin=
function(f,h,e,j){var b=d.utils.getPluginName(f);a[b]||(a[b]=new d.plugins.plugin(f));a[b].registerPlugin(f,h,e,j)}}(jwplayer),function(d){d.plugins.model=function(a){this.addPlugin=function(k){var f=d.utils.getPluginName(k);a[f]||(a[f]=new d.plugins.plugin(k));return a[f]};this.getPlugins=function(){return a}}}(jwplayer),function(d){var a=jwplayer.utils,k=jwplayer.events;d.pluginmodes={FLASH:0,JAVASCRIPT:1,HYBRID:2};d.plugin=function(f){function h(){switch(a.getPluginPathType(f)){case a.pluginPathType.ABSOLUTE:return f;
case a.pluginPathType.RELATIVE:return a.getAbsolutePath(f,window.location.href)}}function e(){p=setTimeout(function(){b=a.loaderstatus.COMPLETE;l.sendEvent(k.COMPLETE)},1E3)}function j(){b=a.loaderstatus.ERROR;l.sendEvent(k.ERROR)}var b=a.loaderstatus.NEW,c,n,m,p,l=new k.eventdispatcher;a.extend(this,l);this.load=function(){if(b==a.loaderstatus.NEW)if(0<f.lastIndexOf(".swf"))c=f,b=a.loaderstatus.COMPLETE,l.sendEvent(k.COMPLETE);else if(a.getPluginPathType(f)==a.pluginPathType.CDN)b=a.loaderstatus.COMPLETE,
l.sendEvent(k.COMPLETE);else{b=a.loaderstatus.LOADING;var g=new a.scriptloader(h());g.addEventListener(k.COMPLETE,e);g.addEventListener(k.ERROR,j);g.load()}};this.registerPlugin=function(e,f,j,d){p&&(clearTimeout(p),p=void 0);m=f;j&&d?(c=d,n=j):"string"==typeof j?c=j:"function"==typeof j?n=j:!j&&!d&&(c=e);b=a.loaderstatus.COMPLETE;l.sendEvent(k.COMPLETE)};this.getStatus=function(){return b};this.getPluginName=function(){return a.getPluginName(f)};this.getFlashPath=function(){if(c)switch(a.getPluginPathType(c)){case a.pluginPathType.ABSOLUTE:return c;
case a.pluginPathType.RELATIVE:return 0<f.lastIndexOf(".swf")?a.getAbsolutePath(c,window.location.href):a.getAbsolutePath(c,h())}return null};this.getJS=function(){return n};this.getTarget=function(){return m};this.getPluginmode=function(){if("undefined"!=typeof c&&"undefined"!=typeof n)return d.pluginmodes.HYBRID;if("undefined"!=typeof c)return d.pluginmodes.FLASH;if("undefined"!=typeof n)return d.pluginmodes.JAVASCRIPT};this.getNewInstance=function(a,b,c){return new n(a,b,c)};this.getURL=function(){return f}}}(jwplayer.plugins),
function(d){var a=d.utils,k=d.events,f=a.foreach;d.plugins.pluginloader=function(h,e){function j(){m?g.sendEvent(k.ERROR,{message:p}):n||(n=!0,c=a.loaderstatus.COMPLETE,g.sendEvent(k.COMPLETE))}function b(){l||j();if(!n&&!m){var b=0,c=h.getPlugins();a.foreach(l,function(e){e=a.getPluginName(e);var f=c[e];e=f.getJS();var g=f.getTarget(),f=f.getStatus();if(f==a.loaderstatus.LOADING||f==a.loaderstatus.NEW)b++;else if(e&&(!g||parseFloat(g)>parseFloat(d.version)))m=!0,p="Incompatible player version",j()});
0==b&&j()}}var c=a.loaderstatus.NEW,n=!1,m=!1,p,l=e,g=new k.eventdispatcher;a.extend(this,g);this.setupPlugins=function(b,c,e){var g={length:0,plugins:{}},j=0,d={},n=h.getPlugins();f(c.plugins,function(f,h){var k=a.getPluginName(f),l=n[k],m=l.getFlashPath(),q=l.getJS(),p=l.getURL();m&&(g.plugins[m]=a.extend({},h),g.plugins[m].pluginmode=l.getPluginmode(),g.length++);try{if(q&&c.plugins&&c.plugins[p]){var A=document.createElement("div");A.id=b.id+"_"+k;A.style.position="absolute";A.style.top=0;A.style.zIndex=
j+10;d[k]=l.getNewInstance(b,a.extend({},c.plugins[p]),A);j++;b.onReady(e(d[k],A,!0));b.onResize(e(d[k],A))}}catch(D){a.log("ERROR: Failed to load "+k+".")}});b.plugins=d;return g};this.load=function(){if(!(a.exists(e)&&"object"!=a.typeOf(e))){c=a.loaderstatus.LOADING;f(e,function(c){a.exists(c)&&(c=h.addPlugin(c),c.addEventListener(k.COMPLETE,b),c.addEventListener(k.ERROR,q))});var g=h.getPlugins();f(g,function(a,b){b.load()})}b()};var q=this.pluginFailed=function(){m||(m=!0,p="File not found",j())};
this.getStatus=function(){return c}}}(jwplayer),function(){jwplayer.parsers={localName:function(d){return d?d.localName?d.localName:d.baseName?d.baseName:"":""},textContent:function(d){return d?d.textContent?d.textContent:d.text?d.text:"":""},getChildNode:function(d,a){return d.childNodes[a]},numChildren:function(d){return d.childNodes?d.childNodes.length:0}}}(jwplayer),function(d){var a=d.parsers;(a.jwparser=function(){}).parseEntry=function(k,f){for(var h=[],e=[],j=d.utils.xmlAttribute,b=0;b<k.childNodes.length;b++){var c=
k.childNodes[b];if("jwplayer"==c.prefix){var n=a.localName(c);"source"==n?(delete f.sources,h.push({file:j(c,"file"),"default":j(c,"default"),label:j(c,"label"),type:j(c,"type")})):"track"==n?(delete f.tracks,e.push({file:j(c,"file"),"default":j(c,"default"),kind:j(c,"kind"),label:j(c,"label")})):(f[n]=d.utils.serialize(a.textContent(c)),"file"==n&&f.sources&&delete f.sources)}f.file||(f.file=f.link)}if(h.length){f.sources=[];for(b=0;b<h.length;b++)0<h[b].file.length&&(h[b]["default"]="true"==h[b]["default"]?
!0:!1,h[b].label.length||delete h[b].label,f.sources.push(h[b]))}if(e.length){f.tracks=[];for(b=0;b<e.length;b++)0<e[b].file.length&&(e[b]["default"]="true"==e[b]["default"]?!0:!1,e[b].kind=!e[b].kind.length?"captions":e[b].kind,e[b].label.length||delete e[b].label,f.tracks.push(e[b]))}return f}}(jwplayer),function(d){var a=jwplayer.utils,k=a.xmlAttribute,f=d.localName,h=d.textContent,e=d.numChildren,j=d.mediaparser=function(){};j.parseGroup=function(b,c){var d,m,p=[];for(m=0;m<e(b);m++)if(d=b.childNodes[m],
"media"==d.prefix&&f(d))switch(f(d).toLowerCase()){case "content":k(d,"duration")&&(c.duration=a.seconds(k(d,"duration")));0<e(d)&&(c=j.parseGroup(d,c));k(d,"url")&&(c.sources||(c.sources=[]),c.sources.push({file:k(d,"url"),type:k(d,"type"),width:k(d,"width"),label:k(d,"label")}));break;case "title":c.title=h(d);break;case "description":c.description=h(d);break;case "guid":c.mediaid=h(d);break;case "thumbnail":c.image||(c.image=k(d,"url"));break;case "group":j.parseGroup(d,c);break;case "subtitle":var l=
{};l.file=k(d,"url");l.kind="captions";if(0<k(d,"lang").length){var g=l;d=k(d,"lang");var q={zh:"Chinese",nl:"Dutch",en:"English",fr:"French",de:"German",it:"Italian",ja:"Japanese",pt:"Portuguese",ru:"Russian",es:"Spanish"};d=q[d]?q[d]:d;g.label=d}p.push(l)}c.hasOwnProperty("tracks")||(c.tracks=[]);for(m=0;m<p.length;m++)c.tracks.push(p[m]);return c}}(jwplayer.parsers),function(d){function a(a){for(var c={},e=0;e<a.childNodes.length;e++){var h=a.childNodes[e],p=j(h);if(p)switch(p.toLowerCase()){case "enclosure":c.file=
k.xmlAttribute(h,"url");break;case "title":c.title=f(h);break;case "guid":c.mediaid=f(h);break;case "pubdate":c.date=f(h);break;case "description":c.description=f(h);break;case "link":c.link=f(h);break;case "category":c.tags=c.tags?c.tags+f(h):f(h)}}c=d.mediaparser.parseGroup(a,c);c=d.jwparser.parseEntry(a,c);return new jwplayer.playlist.item(c)}var k=jwplayer.utils,f=d.textContent,h=d.getChildNode,e=d.numChildren,j=d.localName;d.rssparser={};d.rssparser.parse=function(b){for(var c=[],f=0;f<e(b);f++){var d=
h(b,f);if("channel"==j(d).toLowerCase())for(var k=0;k<e(d);k++){var l=h(d,k);"item"==j(l).toLowerCase()&&c.push(a(l))}}return c}}(jwplayer.parsers),function(d){d.playlist=function(a){var k=[];if("array"==d.utils.typeOf(a))for(var f=0;f<a.length;f++)k.push(new d.playlist.item(a[f]));else k.push(new d.playlist.item(a));return k}}(jwplayer),function(d){var a=d.item=function(k){var f=jwplayer.utils,h=f.extend({},a.defaults,k);h.tracks=k&&f.exists(k.tracks)?k.tracks:[];0==h.sources.length&&(h.sources=
[new d.source(h)]);for(var e=0;e<h.sources.length;e++){var j=h.sources[e]["default"];h.sources[e]["default"]=j?"true"==j.toString():!1;h.sources[e]=new d.source(h.sources[e])}if(h.captions&&!f.exists(k.tracks)){for(k=0;k<h.captions.length;k++)h.tracks.push(h.captions[k]);delete h.captions}for(e=0;e<h.tracks.length;e++)h.tracks[e]=new d.track(h.tracks[e]);return h};a.defaults={description:"",image:"",mediaid:"",title:"",sources:[],tracks:[]}}(jwplayer.playlist),function(d){var a=jwplayer,k=a.utils,
f=a.events,h=a.parsers;d.loader=function(){function a(e){try{var j=e.responseXML.childNodes;e="";for(var k=0;k<j.length&&!(e=j[k],8!=e.nodeType);k++);"xml"==h.localName(e)&&(e=e.nextSibling);if("rss"!=h.localName(e))b("Not a valid RSS feed");else{var l=new d(h.rssparser.parse(e));c.sendEvent(f.JWPLAYER_PLAYLIST_LOADED,{playlist:l})}}catch(g){b()}}function j(a){b(a.match(/invalid/i)?"Not a valid RSS feed":"")}function b(a){c.sendEvent(f.JWPLAYER_ERROR,{message:a?a:"Error loading file"})}var c=new f.eventdispatcher;
k.extend(this,c);this.load=function(b){k.ajax(b,a,j)}}}(jwplayer.playlist),function(d){var a=jwplayer.utils,k={file:void 0,label:void 0,type:void 0,"default":void 0};d.source=function(f){var d=a.extend({},k);a.foreach(k,function(e){a.exists(f[e])&&(d[e]=f[e],delete f[e])});d.type&&0<d.type.indexOf("/")&&(d.type=a.extensionmap.mimeType(d.type));"m3u8"==d.type&&(d.type="hls");"smil"==d.type&&(d.type="rtmp");return d}}(jwplayer.playlist),function(d){var a=jwplayer.utils,k={file:void 0,label:void 0,kind:"captions",
"default":!1};d.track=function(d){var h=a.extend({},k);d||(d={});a.foreach(k,function(e){a.exists(d[e])&&(h[e]=d[e],delete d[e])});return h}}(jwplayer.playlist),function(d){var a=d.utils,k=d.events,f=!0,h=!1,e=document,j=d.embed=function(b){function c(b,c){a.foreach(c,function(a,c){"function"==typeof b[a]&&b[a].call(b,c)})}function n(){if(r.sitecatalyst)try{null!=s&&s.hasOwnProperty("Media")||l()}catch(e){l();return}if("array"==a.typeOf(r.playlist)&&2>r.playlist.length&&(0==r.playlist.length||!r.playlist[0].sources||
0==r.playlist[0].sources.length))p();else if(!z)if("string"==a.typeOf(r.playlist)){var g=new d.playlist.loader;g.addEventListener(k.JWPLAYER_PLAYLIST_LOADED,function(a){r.playlist=a.playlist;z=h;n()});g.addEventListener(k.JWPLAYER_ERROR,function(a){z=h;p(a)});z=f;g.load(r.playlist)}else if(y.getStatus()==a.loaderstatus.COMPLETE){for(g=0;g<r.modes.length;g++)if(r.modes[g].type&&j[r.modes[g].type]){var u=a.extend({},r),w=new j[r.modes[g].type](t,r.modes[g],u,y,b);if(w.supportsConfig())return w.addEventListener(k.ERROR,
m),w.embed(),c(b,u.events),b}if(r.fallback){var x="No suitable players found and fallback enabled";C=setTimeout(function(){q(x,f)},10);a.log(x);new j.download(t,r,p)}else x="No suitable players found and fallback disabled",q(x,h),a.log(x),t.parentNode.replaceChild(v,t)}}function m(a){g(t,B+a.message)}function p(a){a&&a.message?g(t,"Error loading playlist: "+a.message):g(t,B+"No playable sources found")}function l(){g(t,"Adobe SiteCatalyst Error: Could not find Media Module")}function g(b,c){if(r.fallback){var e=
b.style;e.backgroundColor="#000";e.color="#FFF";e.width=a.styleDimension(r.width);e.height=a.styleDimension(r.height);e.display="table";e.opacity=1;var e=document.createElement("p"),d=e.style;d.verticalAlign="middle";d.textAlign="center";d.display="table-cell";d.font="15px/20px Arial, Helvetica, sans-serif";e.innerHTML=c.replace(":",":\x3cbr\x3e");b.innerHTML="";b.appendChild(e);q(c,f)}else q(c,h)}function q(a,c){C&&(clearTimeout(C),C=null);b.dispatchEvent(k.JWPLAYER_SETUP_ERROR,{message:a,fallback:c})}
var r=new j.config(b.config),t,u,v,w=r.width,x=r.height,B="Error loading player: ",y=d.plugins.loadPlugins(b.id,r.plugins),z=h,C=null;r.fallbackDiv&&(v=r.fallbackDiv,delete r.fallbackDiv);r.id=b.id;u=e.getElementById(b.id);r.aspectratio?b.config.aspectratio=r.aspectratio:delete b.config.aspectratio;t=e.createElement("div");t.id=u.id;t.style.width=0<w.toString().indexOf("%")?w:w+"px";t.style.height=0<x.toString().indexOf("%")?x:x+"px";u.parentNode.replaceChild(t,u);d.embed.errorScreen=g;y.addEventListener(k.COMPLETE,
n);y.addEventListener(k.ERROR,function(a){g(t,"Could not load plugins: "+a.message)});y.load();return b}}(jwplayer),function(d){function a(a){if(a.playlist)for(var d=0;d<a.playlist.length;d++)a.playlist[d]=new h(a.playlist[d]);else{var b={};f.foreach(h.defaults,function(c){k(a,b,c)});b.sources||(a.levels?(b.sources=a.levels,delete a.levels):(d={},k(a,d,"file"),k(a,d,"type"),b.sources=d.file?[d]:[]));a.playlist=[new h(b)]}}function k(a,d,b){f.exists(a[b])&&(d[b]=a[b],delete a[b])}var f=d.utils,h=d.playlist.item;
(d.embed.config=function(e){var j={fallback:!0,height:270,primary:"html5",width:480,base:e.base?e.base:f.getScriptPath("jwplayer.js"),aspectratio:""};e=f.extend(j,d.defaults,e);var j={type:"html5",src:e.base+"jwplayer.html5.js"},b={type:"flash",src:e.base+"jwplayer.flash.swf"};e.modes="flash"==e.primary?[b,j]:[j,b];e.listbar&&(e.playlistsize=e.listbar.size,e.playlistposition=e.listbar.position,e.playlistlayout=e.listbar.layout);e.flashplayer&&(b.src=e.flashplayer);e.html5player&&(j.src=e.html5player);
a(e);b=e.aspectratio;if("string"!=typeof b||!f.exists(b))j=0;else{var c=b.indexOf(":");-1==c?j=0:(j=parseFloat(b.substr(0,c)),b=parseFloat(b.substr(c+1)),j=0>=j||0>=b?0:100*(b/j)+"%")}-1==e.width.toString().indexOf("%")?delete e.aspectratio:j?e.aspectratio=j:delete e.aspectratio;return e}).addConfig=function(e,d){a(d);return f.extend(e,d)}}(jwplayer),function(d){var a=d.utils,k=document;d.embed.download=function(d,h,e){function j(b,c){for(var e=k.querySelectorAll(b),d=0;d<e.length;d++)a.foreach(c,
function(a,b){e[d].style[a]=b})}function b(a,b,c){a=k.createElement(a);b&&(a.className="jwdownload"+b);c&&c.appendChild(a);return a}var c=a.extend({},h),n=c.width?c.width:480,m=c.height?c.height:320,p;h=h.logo?h.logo:{prefix:a.repo(),file:"logo.png",margin:10};var l,g,q,c=c.playlist,r,t=["mp4","aac","mp3"];if(c&&c.length){r=c[0];p=r.sources;for(c=0;c<p.length;c++){var u=p[c],v=u.type?u.type:a.extensionmap.extType(a.extension(u.file));u.file&&a.foreach(t,function(b){v==t[b]?(l=u.file,g=r.image):a.isYouTube(u.file)&&
(q=u.file)})}l?(p=l,e=g,d&&(c=b("a","display",d),b("div","icon",c),b("div","logo",c),p&&c.setAttribute("href",a.getAbsolutePath(p))),c="#"+d.id+" .jwdownload",d.style.width="",d.style.height="",j(c+"display",{width:a.styleDimension(Math.max(320,n)),height:a.styleDimension(Math.max(180,m)),background:"black center no-repeat "+(e?"url("+e+")":""),backgroundSize:"contain",position:"relative",border:"none",display:"block"}),j(c+"display div",{position:"absolute",width:"100%",height:"100%"}),j(c+"logo",
{top:h.margin+"px",right:h.margin+"px",background:"top right no-repeat url("+h.prefix+h.file+")"}),j(c+"icon",{background:"center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgNJREFUeNrs28lqwkAYB/CZqNVDDj2r6FN41QeIy8Fe+gj6BL275Q08u9FbT8ZdwVfotSBYEPUkxFOoks4EKiJdaDuTjMn3wWBO0V/+sySR8SNSqVRKIR8qaXHkzlqS9jCfzzWcTCYp9hF5o+59sVjsiRzcegSckFzcjT+ruN80TeSlAjCAAXzdJSGPFXRpAAMYwACGZQkSdhG4WCzehMNhqV6vG6vVSrirKVEw66YoSqDb7cqlUilE8JjHd/y1MQefVzqdDmiaJpfLZWHgXMHn8F6vJ1cqlVAkEsGuAn83J4gAd2RZymQygX6/L1erVQt+9ZPWb+CDwcCC2zXGJaewl/DhcHhK3DVj+KfKZrMWvFarcYNLomAv4aPRSFZVlTlcSPA5fDweW/BoNIqFnKV53JvncjkLns/n/cLdS+92O7RYLLgsKfv9/t8XlDn4eDyiw+HA9Jyz2eyt0+kY2+3WFC5hluej0Ha7zQQq9PPwdDq1Et1sNsx/nFBgCqWJ8oAK1aUptNVqcYWewE4nahfU0YQnk4ntUEfGMIU2m01HoLaCKbTRaDgKtaVLk9tBYaBcE/6Artdr4RZ5TB6/dC+9iIe/WgAMYADDpAUJAxjAAAYwgGFZgoS/AtNNTF7Z2bL0BYPBV3Jw5xFwwWcYxgtBP5OkE8i9G7aWGOOCruvauwADALMLMEbKf4SdAAAAAElFTkSuQmCC)"})):
q?(h=q,d=b("embed","",d),d.src="http://www.youtube.com/v/"+a.youTubeID(h),d.type="application/x-shockwave-flash",d.width=n,d.height=m):e()}}}(jwplayer),function(d){var a=d.utils,k=d.events,f={};(d.embed.flash=function(e,j,b,c,n){function m(a,b,c){var e=document.createElement("param");e.setAttribute("name",b);e.setAttribute("value",c);a.appendChild(e)}function p(a,b,c){return function(){try{c&&document.getElementById(n.id+"_wrapper").appendChild(b);var e=document.getElementById(n.id).getPluginConfig("display");
"function"==typeof a.resize&&a.resize(e.width,e.height);b.style.left=e.x;b.style.top=e.h}catch(d){}}}function l(b){if(!b)return{};var c={},e=[];a.foreach(b,function(b,d){var g=a.getPluginName(b);e.push(b);a.foreach(d,function(a,b){c[g+"."+a]=b})});c.plugins=e.join(",");return c}var g=new d.events.eventdispatcher,q=a.flashVersion();a.extend(this,g);this.embed=function(){b.id=n.id;if(10>q)return g.sendEvent(k.ERROR,{message:"Flash version must be 10.0 or greater"}),!1;var d,h,u=n.config.listbar,v=a.extend({},
b);if(e.id+"_wrapper"==e.parentNode.id)d=document.getElementById(e.id+"_wrapper");else{d=document.createElement("div");h=document.createElement("div");h.style.display="none";h.id=e.id+"_aspect";d.id=e.id+"_wrapper";d.style.position="relative";d.style.display="block";d.style.width=a.styleDimension(v.width);d.style.height=a.styleDimension(v.height);if(n.config.aspectratio){var w=parseFloat(n.config.aspectratio);h.style.display="block";h.style.marginTop=n.config.aspectratio;d.style.height="auto";d.style.display=
"inline-block";u&&("bottom"==u.position?h.style.paddingBottom=u.size+"px":"right"==u.position&&(h.style.marginBottom=-1*u.size*(w/100)+"px"))}e.parentNode.replaceChild(d,e);d.appendChild(e);d.appendChild(h)}d=c.setupPlugins(n,v,p);0<d.length?a.extend(v,l(d.plugins)):delete v.plugins;"undefined"!=typeof v["dock.position"]&&"false"==v["dock.position"].toString().toLowerCase()&&(v.dock=v["dock.position"],delete v["dock.position"]);d=v.wmode?v.wmode:v.height&&40>=v.height?"transparent":"opaque";h="height width modes events primary base fallback volume".split(" ");
for(u=0;u<h.length;u++)delete v[h[u]];h=a.getCookies();a.foreach(h,function(a,b){"undefined"==typeof v[a]&&(v[a]=b)});h=window.location.href.split("/");h.splice(h.length-1,1);h=h.join("/");v.base=h+"/";f[e.id]=v;a.isIE()?(h='\x3cobject classid\x3d"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" " width\x3d"100%" height\x3d"100%"id\x3d"'+e.id+'" name\x3d"'+e.id+'" tabindex\x3d0""\x3e',h+='\x3cparam name\x3d"movie" value\x3d"'+j.src+'"\x3e',h+='\x3cparam name\x3d"allowfullscreen" value\x3d"true"\x3e\x3cparam name\x3d"allowscriptaccess" value\x3d"always"\x3e',
h+='\x3cparam name\x3d"seamlesstabbing" value\x3d"true"\x3e',h+='\x3cparam name\x3d"wmode" value\x3d"'+d+'"\x3e',h+='\x3cparam name\x3d"bgcolor" value\x3d"#000000"\x3e',h+="\x3c/object\x3e",e.outerHTML=h,d=document.getElementById(e.id)):(h=document.createElement("object"),h.setAttribute("type","application/x-shockwave-flash"),h.setAttribute("data",j.src),h.setAttribute("width","100%"),h.setAttribute("height","100%"),h.setAttribute("bgcolor","#000000"),h.setAttribute("id",e.id),h.setAttribute("name",
e.id),h.setAttribute("tabindex",0),m(h,"allowfullscreen","true"),m(h,"allowscriptaccess","always"),m(h,"seamlesstabbing","true"),m(h,"wmode",d),e.parentNode.replaceChild(h,e),d=h);n.config.aspectratio&&(d.style.position="absolute");n.container=d;n.setPlayer(d,"flash")};this.supportsConfig=function(){if(q)if(b){if("string"==a.typeOf(b.playlist))return!0;try{var c=b.playlist[0].sources;if("undefined"==typeof c)return!0;for(var d=0;d<c.length;d++)if(c[d].file&&h(c[d].file,c[d].type))return!0}catch(e){}}else return!0;
return!1}}).getVars=function(a){return f[a]};var h=d.embed.flashCanPlay=function(d,f){if(a.isYouTube(d)||a.isRtmp(d,f)||"hls"==f)return!0;var b=a.extensionmap[f?f:a.extension(d)];return!b?!1:!!b.flash}}(jwplayer),function(d){var a=d.utils,k=a.extensionmap,f=d.events;d.embed.html5=function(h,e,j,b,c){function n(a,b,c){return function(){try{var d=document.querySelector("#"+h.id+" .jwmain");c&&d.appendChild(b);"function"==typeof a.resize&&(a.resize(d.clientWidth,d.clientHeight),setTimeout(function(){a.resize(d.clientWidth,
d.clientHeight)},400));b.left=d.style.left;b.top=d.style.top}catch(e){}}}function m(a){p.sendEvent(a.type,{message:"HTML5 player not found"})}var p=this,l=new f.eventdispatcher;a.extend(p,l);p.embed=function(){if(d.html5){b.setupPlugins(c,j,n);h.innerHTML="";var g=d.utils.extend({},j);delete g.volume;g=new d.html5.player(g);c.container=document.getElementById(c.id);c.setPlayer(g,"html5")}else g=new a.scriptloader(e.src),g.addEventListener(f.ERROR,m),g.addEventListener(f.COMPLETE,p.embed),g.load()};
p.supportsConfig=function(){if(d.vid.canPlayType)try{if("string"==a.typeOf(j.playlist))return!0;for(var b=j.playlist[0].sources,c=0;c<b.length;c++){var e;var f=b[c].file,h=b[c].type;if(null!==navigator.userAgent.match(/BlackBerry/i)||a.isAndroid()&&("m3u"==a.extension(f)||"m3u8"==a.extension(f))||a.isRtmp(f,h))e=!1;else{var l=k[h?h:a.extension(f)],m;if(!l||l.flash&&!l.html5)m=!1;else{var n=l.html5,p=d.vid;if(n)try{m=p.canPlayType(n)?!0:!1}catch(y){m=!1}else m=!0}e=m}if(e)return!0}}catch(z){}return!1}}}(jwplayer),
function(d){var a=d.embed,k=d.utils,f=k.extend(function(f){var e=k.repo(),j=k.extend({},d.defaults),b=k.extend({},j,f.config),c=f.config,n=b.plugins,m=b.analytics,p=e+"jwpsrv.js",l=e+"sharing.js",g=e+"related.js",q=e+"gapro.js",j=d.key?d.key:j.key,r=(new d.utils.key(j)).edition(),n=n?n:{};"ads"==r&&b.advertising&&(b.advertising.client.match(".js$|.swf$")?n[b.advertising.client]=b.advertising:n[e+b.advertising.client+".js"]=b.advertising);delete c.advertising;c.key=j;b.analytics&&(b.analytics.client&&
b.analytics.client.match(".js$|.swf$"))&&(p=b.analytics.client);delete c.analytics;if("free"==r||!m||!1!==m.enabled)n[p]=m?m:{};delete n.sharing;delete n.related;switch(r){case "premium":case "ads":b.related&&(b.related.client&&b.related.client.match(".js$|.swf$")&&(g=b.related.client),n[g]=b.related),b.ga&&(b.ga.client&&b.ga.client.match(".js$|.swf$")&&(q=b.ga.client),n[q]=b.ga),c.sitecatalyst&&new d.embed.sitecatalyst(f);case "pro":b.sharing&&(b.sharing.client&&b.sharing.client.match(".js$|.swf$")&&
(l=b.sharing.client),n[l]=b.sharing),b.skin&&(c.skin=b.skin.replace(/^(beelden|bekle|five|glow|modieus|roundster|stormtrooper|vapor)$/i,k.repo()+"skins/$1.xml"))}c.plugins=n;return new a(f)},a);d.embed=f}(jwplayer),function(d){var a=jwplayer.utils;d.sitecatalyst=function(d){function f(b){c.debug&&a.log(b)}function h(a){a=a.split("/");a=a[a.length-1];a=a.split("?");return a[0]}function e(){if(!g){g=!0;var a=b.getPosition();f("stop: "+m+" : "+a);s.Media.stop(m,a)}}function j(){q||(e(),q=!0,f("close: "+
m),s.Media.close(m),r=!0,l=0)}var b=d,c=a.extend({},b.config.sitecatalyst),n={onPlay:function(){if(!r){var a=b.getPosition();g=!1;f("play: "+m+" : "+a);s.Media.play(m,a)}},onPause:e,onBuffer:e,onIdle:j,onPlaylistItem:function(d){try{r=!0;j();l=0;var e;if(c.mediaName)e=c.mediaName;else{var g=b.getPlaylistItem(d.index);e=g.title?g.title:g.file?h(g.file):g.sources&&g.sources.length?h(g.sources[0].file):""}m=e;p=c.playerName?c.playerName:b.id}catch(f){a.log(f)}},onTime:function(){if(r){var a=b.getDuration();
if(-1==a)return;q=g=r=!1;f("open: "+m+" : "+a+" : "+p);s.Media.open(m,a,p);f("play: "+m+" : 0");s.Media.play(m,0)}a=b.getPosition();if(3<=Math.abs(a-l)){var c=l;f("seek: "+c+" to "+a);f("stop: "+m+" : "+c);s.Media.stop(m,c);f("play: "+m+" : "+a);s.Media.play(m,a)}l=a},onComplete:j},m,p,l,g=!0,q=!0,r;a.foreach(n,function(a){b[a](n[a])})}}(jwplayer.embed),function(d){var a=[],k=d.utils,f=d.events,h=f.state,e=document,j=d.api=function(a){function c(a,b){return function(c){return b(a,c)}}function n(a,
b){r[a]||(r[a]=[],p(f.JWPLAYER_PLAYER_STATE,function(b){var c=b.newstate;b=b.oldstate;if(c==a){var d=r[c];if(d)for(var e=0;e<d.length;e++)"function"==typeof d[e]&&d[e].call(this,{oldstate:b,newstate:c})}}));r[a].push(b);return g}function m(a,b){try{a.jwAddEventListener(b,'function(dat) { jwplayer("'+g.id+'").dispatchEvent("'+b+'", dat); }')}catch(c){k.log("Could not add internal listener")}}function p(a,b){q[a]||(q[a]=[],t&&u&&m(t,a));q[a].push(b);return g}function l(){if(u){for(var a=arguments[0],
b=[],c=1;c<arguments.length;c++)b.push(arguments[c]);if("undefined"!=typeof t&&"function"==typeof t[a])switch(b.length){case 4:return t[a](b[0],b[1],b[2],b[3]);case 3:return t[a](b[0],b[1],b[2]);case 2:return t[a](b[0],b[1]);case 1:return t[a](b[0]);default:return t[a]()}return null}v.push(arguments)}var g=this,q={},r={},t=void 0,u=!1,v=[],w=void 0,x={},B={};g.container=a;g.id=a.id;g.getBuffer=function(){return l("jwGetBuffer")};g.getContainer=function(){return g.container};g.addButton=function(a,
b,c,d){try{B[d]=c,l("jwDockAddButton",a,b,"jwplayer('"+g.id+"').callback('"+d+"')",d)}catch(e){k.log("Could not add dock button"+e.message)}};g.removeButton=function(a){l("jwDockRemoveButton",a)};g.callback=function(a){if(B[a])B[a]()};g.forceState=function(a){l("jwForceState",a);return g};g.releaseState=function(){return l("jwReleaseState")};g.getDuration=function(){return l("jwGetDuration")};g.getFullscreen=function(){return l("jwGetFullscreen")};g.getHeight=function(){return l("jwGetHeight")};g.getLockState=
function(){return l("jwGetLockState")};g.getMeta=function(){return g.getItemMeta()};g.getMute=function(){return l("jwGetMute")};g.getPlaylist=function(){var a=l("jwGetPlaylist");"flash"==g.renderingMode&&k.deepReplaceKeyName(a,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]);return a};g.getPlaylistItem=function(a){k.exists(a)||(a=g.getPlaylistIndex());return g.getPlaylist()[a]};g.getPlaylistIndex=function(){return l("jwGetPlaylistIndex")};g.getPosition=function(){return l("jwGetPosition")};
g.getRenderingMode=function(){return g.renderingMode};g.getState=function(){return l("jwGetState")};g.getVolume=function(){return l("jwGetVolume")};g.getWidth=function(){return l("jwGetWidth")};g.setFullscreen=function(a){k.exists(a)?l("jwSetFullscreen",a):l("jwSetFullscreen",!l("jwGetFullscreen"));return g};g.setMute=function(a){k.exists(a)?l("jwSetMute",a):l("jwSetMute",!l("jwGetMute"));return g};g.lock=function(){return g};g.unlock=function(){return g};g.load=function(a){l("jwLoad",a);return g};
g.playlistItem=function(a){l("jwPlaylistItem",parseInt(a));return g};g.playlistPrev=function(){l("jwPlaylistPrev");return g};g.playlistNext=function(){l("jwPlaylistNext");return g};g.resize=function(a,b){if("flash"!=g.renderingMode){var c=document.getElementById(g.id);c.className=c.className.replace(/\s+aspectMode/,"");c.style.display="block";l("jwResize",a,b)}else{var c=e.getElementById(g.id+"_wrapper"),d=e.getElementById(g.id+"_aspect");d&&(d.style.display="none");c&&(c.style.display="block",c.style.width=
k.styleDimension(a),c.style.height=k.styleDimension(b))}return g};g.play=function(a){"undefined"==typeof a?(a=g.getState(),a==h.PLAYING||a==h.BUFFERING?l("jwPause"):l("jwPlay")):l("jwPlay",a);return g};g.pause=function(a){"undefined"==typeof a?(a=g.getState(),a==h.PLAYING||a==h.BUFFERING?l("jwPause"):l("jwPlay")):l("jwPause",a);return g};g.stop=function(){l("jwStop");return g};g.seek=function(a){l("jwSeek",a);return g};g.setVolume=function(a){l("jwSetVolume",a);return g};g.loadInstream=function(a,
b){return w=new j.instream(this,t,a,b)};g.getQualityLevels=function(){return l("jwGetQualityLevels")};g.getCurrentQuality=function(){return l("jwGetCurrentQuality")};g.setCurrentQuality=function(a){l("jwSetCurrentQuality",a)};g.getCaptionsList=function(){return l("jwGetCaptionsList")};g.getCurrentCaptions=function(){return l("jwGetCurrentCaptions")};g.setCurrentCaptions=function(a){l("jwSetCurrentCaptions",a)};g.getControls=function(){return l("jwGetControls")};g.getSafeRegion=function(){return l("jwGetSafeRegion")};
g.setControls=function(a){l("jwSetControls",a)};g.destroyPlayer=function(){l("jwPlayerDestroy")};g.playAd=function(a){l("jwPlayAd",a)};var y={onBufferChange:f.JWPLAYER_MEDIA_BUFFER,onBufferFull:f.JWPLAYER_MEDIA_BUFFER_FULL,onError:f.JWPLAYER_ERROR,onSetupError:f.JWPLAYER_SETUP_ERROR,onFullscreen:f.JWPLAYER_FULLSCREEN,onMeta:f.JWPLAYER_MEDIA_META,onMute:f.JWPLAYER_MEDIA_MUTE,onPlaylist:f.JWPLAYER_PLAYLIST_LOADED,onPlaylistItem:f.JWPLAYER_PLAYLIST_ITEM,onPlaylistComplete:f.JWPLAYER_PLAYLIST_COMPLETE,
onReady:f.API_READY,onResize:f.JWPLAYER_RESIZE,onComplete:f.JWPLAYER_MEDIA_COMPLETE,onSeek:f.JWPLAYER_MEDIA_SEEK,onTime:f.JWPLAYER_MEDIA_TIME,onVolume:f.JWPLAYER_MEDIA_VOLUME,onBeforePlay:f.JWPLAYER_MEDIA_BEFOREPLAY,onBeforeComplete:f.JWPLAYER_MEDIA_BEFORECOMPLETE,onDisplayClick:f.JWPLAYER_DISPLAY_CLICK,onControls:f.JWPLAYER_CONTROLS,onQualityLevels:f.JWPLAYER_MEDIA_LEVELS,onQualityChange:f.JWPLAYER_MEDIA_LEVEL_CHANGED,onCaptionsList:f.JWPLAYER_CAPTIONS_LIST,onCaptionsChange:f.JWPLAYER_CAPTIONS_CHANGED,
onAdError:f.JWPLAYER_AD_ERROR,onAdClick:f.JWPLAYER_AD_CLICK,onAdImpression:f.JWPLAYER_AD_IMPRESSION,onAdTime:f.JWPLAYER_AD_TIME,onAdComplete:f.JWPLAYER_AD_COMPLETE,onAdCompanions:f.JWPLAYER_AD_COMPANIONS};k.foreach(y,function(a){g[a]=c(y[a],p)});var z={onBuffer:h.BUFFERING,onPause:h.PAUSED,onPlay:h.PLAYING,onIdle:h.IDLE};k.foreach(z,function(a){g[a]=c(z[a],n)});g.remove=function(){if(!u)throw"Cannot call remove() before player is ready";v=[];j.destroyPlayer(this.id)};g.setup=function(a){if(d.embed){var b=
e.getElementById(g.id);b&&(a.fallbackDiv=b);b=g;v=[];j.destroyPlayer(b.id);b=d(g.id);b.config=a;return new d.embed(b)}return g};g.registerPlugin=function(a,b,c,e){d.plugins.registerPlugin(a,b,c,e)};g.setPlayer=function(a,b){t=a;g.renderingMode=b};g.detachMedia=function(){if("html5"==g.renderingMode)return l("jwDetachMedia")};g.attachMedia=function(a){if("html5"==g.renderingMode)return l("jwAttachMedia",a)};g.dispatchEvent=function(a,b){if(q[a])for(var c=k.translateEventResponse(a,b),d=0;d<q[a].length;d++)if("function"==
typeof q[a][d])try{a==f.JWPLAYER_PLAYLIST_LOADED&&k.deepReplaceKeyName(c.playlist,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]),q[a][d].call(this,c)}catch(e){k.log("There was an error calling back an event handler")}};g.dispatchInstreamEvent=function(a){w&&w.dispatchEvent(a,arguments)};g.callInternal=l;g.playerReady=function(a){u=!0;t||g.setPlayer(e.getElementById(a.id));g.container=e.getElementById(g.id);k.foreach(q,function(a){m(t,a)});p(f.JWPLAYER_PLAYLIST_ITEM,function(){x=
{}});p(f.JWPLAYER_MEDIA_META,function(a){k.extend(x,a.metadata)});for(g.dispatchEvent(f.API_READY);0<v.length;)l.apply(this,v.shift())};g.getItemMeta=function(){return x};g.isBeforePlay=function(){return t.jwIsBeforePlay()};g.isBeforeComplete=function(){return t.jwIsBeforeComplete()};return g};j.selectPlayer=function(b){var c;k.exists(b)||(b=0);b.nodeType?c=b:"string"==typeof b&&(c=e.getElementById(b));return c?(b=j.playerById(c.id))?b:j.addPlayer(new j(c)):"number"==typeof b?a[b]:null};j.playerById=
function(b){for(var c=0;c<a.length;c++)if(a[c].id==b)return a[c];return null};j.addPlayer=function(b){for(var c=0;c<a.length;c++)if(a[c]==b)return b;a.push(b);return b};j.destroyPlayer=function(b){for(var c=-1,d,f=0;f<a.length;f++)a[f].id==b&&(c=f,d=a[f]);0<=c&&(b=d.id,f=e.getElementById(b+("flash"==d.renderingMode?"_wrapper":"")),k.clearCss&&k.clearCss("#"+b),f&&("html5"==d.renderingMode&&d.destroyPlayer(),d=e.createElement("div"),d.id=b,f.parentNode.replaceChild(d,f)),a.splice(c,1));return null};
d.playerReady=function(a){var c=d.api.playerById(a.id);c?c.playerReady(a):d.api.selectPlayer(a.id).playerReady(a)}}(jwplayer),function(d){var a=d.events,k=d.utils,f=a.state;d.api.instream=function(d,e,j,b){function c(a,b){l[a]||(l[a]=[],p.jwInstreamAddEventListener(a,'function(dat) { jwplayer("'+m.id+'").dispatchInstreamEvent("'+a+'", dat); }'));l[a].push(b);return this}function n(b,d){g[b]||(g[b]=[],c(a.JWPLAYER_PLAYER_STATE,function(a){var c=a.newstate,d=a.oldstate;if(c==b){var e=g[c];if(e)for(var f=
0;f<e.length;f++)"function"==typeof e[f]&&e[f].call(this,{oldstate:d,newstate:c,type:a.type})}}));g[b].push(d);return this}var m=d,p=e,l={},g={};this.dispatchEvent=function(a,b){if(l[a])for(var c=k.translateEventResponse(a,b[1]),d=0;d<l[a].length;d++)"function"==typeof l[a][d]&&l[a][d].call(this,c)};this.onError=function(b){return c(a.JWPLAYER_ERROR,b)};this.onFullscreen=function(b){return c(a.JWPLAYER_FULLSCREEN,b)};this.onMeta=function(b){return c(a.JWPLAYER_MEDIA_META,b)};this.onMute=function(b){return c(a.JWPLAYER_MEDIA_MUTE,
b)};this.onComplete=function(b){return c(a.JWPLAYER_MEDIA_COMPLETE,b)};this.onTime=function(b){return c(a.JWPLAYER_MEDIA_TIME,b)};this.onBuffer=function(a){return n(f.BUFFERING,a)};this.onPause=function(a){return n(f.PAUSED,a)};this.onPlay=function(a){return n(f.PLAYING,a)};this.onIdle=function(a){return n(f.IDLE,a)};this.onClick=function(b){return c(a.JWPLAYER_INSTREAM_CLICK,b)};this.onInstreamDestroyed=function(b){return c(a.JWPLAYER_INSTREAM_DESTROYED,b)};this.play=function(a){p.jwInstreamPlay(a)};
this.pause=function(a){p.jwInstreamPause(a)};this.destroy=function(){p.jwInstreamDestroy()};this.setText=function(a){p.jwInstreamSetText(a?a:"")};m.callInternal("jwLoadInstream",j,b?b:{})}}(jwplayer),function(d){var a=d.api,k=a.selectPlayer;a.selectPlayer=function(a){return(a=k(a))?a:{registerPlugin:function(a,e,f){d.plugins.registerPlugin(a,e,f)}}}}(jwplayer));jwplayer.key = '6SWbjFZruJXBFP7fiNzuhGtoET+Kj6BAt+nhuQ==';if (location.protocol === 'https:') {jwplayer.defaults = {flashplayer: 'https://ssl.p.jwpcdn.com/6/6/jwplayer.flash.swf', html5player: 'https://ssl.p.jwpcdn.com/6/6/jwplayer.html5.js', ph: 1}} else {jwplayer.defaults = {flashplayer: 'http://p.jwpcdn.com/6/6/jwplayer.flash.swf', html5player: 'http://p.jwpcdn.com/6/6/jwplayer.html5.js', ph: 1}}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,777 +0,0 @@
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject = function() {
var UNDEF = "undefined",
OBJECT = "object",
SHOCKWAVE_FLASH = "Shockwave Flash",
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
FLASH_MIME_TYPE = "application/x-shockwave-flash",
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
ON_READY_STATE_CHANGE = "onreadystatechange",
win = window,
doc = document,
nav = navigator,
plugin = false,
domLoadFnArr = [main],
regObjArr = [],
objIdArr = [],
listenersArr = [],
storedAltContent,
storedAltContentId,
storedCallbackFn,
storedCallbackObj,
isDomLoaded = false,
isExpressInstallActive = false,
dynamicStylesheet,
dynamicStylesheetMedia,
autoHideShow = true,
/* Centralized function for browser feature detection
- User agent string detection is only used when no good alternative is possible
- Is executed directly for optimal performance
*/
ua = function() {
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
u = nav.userAgent.toLowerCase(),
p = nav.platform.toLowerCase(),
windows = p ? /win/.test(p) : /win/.test(u),
mac = p ? /mac/.test(p) : /mac/.test(u),
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
playerVersion = [0,0,0],
d = null;
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
d = nav.plugins[SHOCKWAVE_FLASH].description;
if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
plugin = true;
ie = false; // cascaded feature detection for Internet Explorer
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
}
}
else if (typeof win.ActiveXObject != UNDEF) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if (a) { // a will return null when ActiveX is disabled
d = a.GetVariable("$version");
if (d) {
ie = true; // cascaded feature detection for Internet Explorer
d = d.split(" ")[1].split(",");
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
}(),
/* Cross-browser onDomLoad
- Will fire an event as soon as the DOM of a web page is loaded
- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
- Regular onload serves as fallback
*/
onDomLoad = function() {
if (!ua.w3) { return; }
if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
callDomLoadFunctions();
}
if (!isDomLoaded) {
if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
}
if (ua.ie && ua.win) {
doc.attachEvent(ON_READY_STATE_CHANGE, function() {
if (doc.readyState == "complete") {
doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
callDomLoadFunctions();
}
});
if (win == top) { // if not inside an iframe
(function(){
if (isDomLoaded) { return; }
try {
doc.documentElement.doScroll("left");
}
catch(e) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
}
if (ua.wk) {
(function(){
if (isDomLoaded) { return; }
if (!/loaded|complete/.test(doc.readyState)) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
addLoadEvent(callDomLoadFunctions);
}
}();
function callDomLoadFunctions() {
if (isDomLoaded) { return; }
try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
t.parentNode.removeChild(t);
}
catch (e) { return; }
isDomLoaded = true;
var dl = domLoadFnArr.length;
for (var i = 0; i < dl; i++) {
domLoadFnArr[i]();
}
}
function addDomLoadEvent(fn) {
if (isDomLoaded) {
fn();
}
else {
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
}
}
/* Cross-browser onload
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
- Will fire an event as soon as a web page including all of its assets are loaded
*/
function addLoadEvent(fn) {
if (typeof win.addEventListener != UNDEF) {
win.addEventListener("load", fn, false);
}
else if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("load", fn, false);
}
else if (typeof win.attachEvent != UNDEF) {
addListener(win, "onload", fn);
}
else if (typeof win.onload == "function") {
var fnOld = win.onload;
win.onload = function() {
fnOld();
fn();
};
}
else {
win.onload = fn;
}
}
/* Main function
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
*/
function main() {
if (plugin) {
testPlayerVersion();
}
else {
matchVersions();
}
}
/* Detect the Flash Player version for non-Internet Explorer browsers
- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
a. Both release and build numbers can be detected
b. Avoid wrong descriptions by corrupt installers provided by Adobe
c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
*/
function testPlayerVersion() {
var b = doc.getElementsByTagName("body")[0];
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
var t = b.appendChild(o);
if (t) {
var counter = 0;
(function(){
if (typeof t.GetVariable != UNDEF) {
var d = t.GetVariable("$version");
if (d) {
d = d.split(" ")[1].split(",");
ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
else if (counter < 10) {
counter++;
setTimeout(arguments.callee, 10);
return;
}
b.removeChild(o);
t = null;
matchVersions();
})();
}
else {
matchVersions();
}
}
/* Perform Flash Player and SWF version matching; static publishing only
*/
function matchVersions() {
var rl = regObjArr.length;
if (rl > 0) {
for (var i = 0; i < rl; i++) { // for each registered object element
var id = regObjArr[i].id;
var cb = regObjArr[i].callbackFn;
var cbObj = {success:false, id:id};
if (ua.pv[0] > 0) {
var obj = getElementById(id);
if (obj) {
if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
setVisibility(id, true);
if (cb) {
cbObj.success = true;
cbObj.ref = getObjectById(id);
cb(cbObj);
}
}
else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
var att = {};
att.data = regObjArr[i].expressInstall;
att.width = obj.getAttribute("width") || "0";
att.height = obj.getAttribute("height") || "0";
if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
// parse HTML object param element's name-value pairs
var par = {};
var p = obj.getElementsByTagName("param");
var pl = p.length;
for (var j = 0; j < pl; j++) {
if (p[j].getAttribute("name").toLowerCase() != "movie") {
par[p[j].getAttribute("name")] = p[j].getAttribute("value");
}
}
showExpressInstall(att, par, id, cb);
}
else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
displayAltContent(obj);
if (cb) { cb(cbObj); }
}
}
}
else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
setVisibility(id, true);
if (cb) {
var o = getObjectById(id); // test whether there is an HTML object element or not
if (o && typeof o.SetVariable != UNDEF) {
cbObj.success = true;
cbObj.ref = o;
}
cb(cbObj);
}
}
}
}
}
function getObjectById(objectIdStr) {
var r = null;
var o = getElementById(objectIdStr);
if (o && o.nodeName == "OBJECT") {
if (typeof o.SetVariable != UNDEF) {
r = o;
}
else {
var n = o.getElementsByTagName(OBJECT)[0];
if (n) {
r = n;
}
}
}
return r;
}
/* Requirements for Adobe Express Install
- only one instance can be active at a time
- fp 6.0.65 or higher
- Win/Mac OS only
- no Webkit engines older than version 312
*/
function canExpressInstall() {
return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
}
/* Show the Adobe Express Install dialog
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
*/
function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
isExpressInstallActive = true;
storedCallbackFn = callbackFn || null;
storedCallbackObj = {success:false, id:replaceElemIdStr};
var obj = getElementById(replaceElemIdStr);
if (obj) {
if (obj.nodeName == "OBJECT") { // static publishing
storedAltContent = abstractAltContent(obj);
storedAltContentId = null;
}
else { // dynamic publishing
storedAltContent = obj;
storedAltContentId = replaceElemIdStr;
}
att.id = EXPRESS_INSTALL_ID;
if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + fv;
}
else {
par.flashvars = fv;
}
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
if (ua.ie && ua.win && obj.readyState != 4) {
var newObj = createElement("div");
replaceElemIdStr += "SWFObjectNew";
newObj.setAttribute("id", replaceElemIdStr);
obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
createSWF(att, par, replaceElemIdStr);
}
}
/* Functions to abstract and display alternative content
*/
function displayAltContent(obj) {
if (ua.ie && ua.win && obj.readyState != 4) {
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
var el = createElement("div");
obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
el.parentNode.replaceChild(abstractAltContent(obj), el);
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
}
}
function abstractAltContent(obj) {
var ac = createElement("div");
if (ua.win && ua.ie) {
ac.innerHTML = obj.innerHTML;
}
else {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
ac.appendChild(c[i].cloneNode(true));
}
}
}
}
}
return ac;
}
/* Cross-browser dynamic SWF creation
*/
function createSWF(attObj, parObj, id) {
var r, el = getElementById(id);
if (ua.wk && ua.wk < 312) { return r; }
if (el) {
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
attObj.id = id;
}
if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
var att = "";
for (var i in attObj) {
if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
if (i.toLowerCase() == "data") {
parObj.movie = attObj[i];
}
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
att += ' class="' + attObj[i] + '"';
}
else if (i.toLowerCase() != "classid") {
att += ' ' + i + '="' + attObj[i] + '"';
}
}
}
var par = "";
for (var j in parObj) {
if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
}
}
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
r = getElementById(attObj.id);
}
else { // well-behaving browsers
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
for (var m in attObj) {
if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
o.setAttribute("class", attObj[m]);
}
else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
o.setAttribute(m, attObj[m]);
}
}
}
for (var n in parObj) {
if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
createObjParam(o, n, parObj[n]);
}
}
el.parentNode.replaceChild(o, el);
r = o;
}
}
return r;
}
function createObjParam(el, pName, pValue) {
var p = createElement("param");
p.setAttribute("name", pName);
p.setAttribute("value", pValue);
el.appendChild(p);
}
/* Cross-browser SWF removal
- Especially needed to safely and completely remove a SWF in Internet Explorer
*/
function removeSWF(id) {
var obj = getElementById(id);
if (obj && obj.nodeName == "OBJECT") {
if (ua.ie && ua.win) {
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
removeObjectInIE(id);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.removeChild(obj);
}
}
}
function removeObjectInIE(id) {
var obj = getElementById(id);
if (obj) {
for (var i in obj) {
if (typeof obj[i] == "function") {
obj[i] = null;
}
}
obj.parentNode.removeChild(obj);
}
}
/* Functions to optimize JavaScript compression
*/
function getElementById(id) {
var el = null;
try {
el = doc.getElementById(id);
}
catch (e) {}
return el;
}
function createElement(el) {
return doc.createElement(el);
}
/* Updated attachEvent function for Internet Explorer
- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
*/
function addListener(target, eventType, fn) {
target.attachEvent(eventType, fn);
listenersArr[listenersArr.length] = [target, eventType, fn];
}
/* Flash Player and SWF content version matching
*/
function hasPlayerVersion(rv) {
var pv = ua.pv, v = rv.split(".");
v[0] = parseInt(v[0], 10);
v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
v[2] = parseInt(v[2], 10) || 0;
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
}
/* Cross-browser dynamic CSS creation
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
*/
function createCSS(sel, decl, media, newStyle) {
if (ua.ie && ua.mac) { return; }
var h = doc.getElementsByTagName("head")[0];
if (!h) { return; } // to also support badly authored HTML pages that lack a head element
var m = (media && typeof media == "string") ? media : "screen";
if (newStyle) {
dynamicStylesheet = null;
dynamicStylesheetMedia = null;
}
if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
// create dynamic stylesheet + get a global reference to it
var s = createElement("style");
s.setAttribute("type", "text/css");
s.setAttribute("media", m);
dynamicStylesheet = h.appendChild(s);
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
}
dynamicStylesheetMedia = m;
}
// add style rule
if (ua.ie && ua.win) {
if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
dynamicStylesheet.addRule(sel, decl);
}
}
else {
if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
}
}
}
function setVisibility(id, isVisible) {
if (!autoHideShow) { return; }
var v = isVisible ? "visible" : "hidden";
if (isDomLoaded && getElementById(id)) {
getElementById(id).style.visibility = v;
}
else {
createCSS("#" + id, "visibility:" + v);
}
}
/* Filter to avoid XSS attacks
*/
function urlEncodeIfNecessary(s) {
var regex = /[\\\"<>\.;]/;
var hasBadChars = regex.exec(s) != null;
return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
}
/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
*/
var cleanup = function() {
if (ua.ie && ua.win) {
window.attachEvent("onunload", function() {
// remove listeners to avoid memory leaks
var ll = listenersArr.length;
for (var i = 0; i < ll; i++) {
listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
}
// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
var il = objIdArr.length;
for (var j = 0; j < il; j++) {
removeSWF(objIdArr[j]);
}
// cleanup library's main closures to avoid memory leaks
for (var k in ua) {
ua[k] = null;
}
ua = null;
for (var l in swfobject) {
swfobject[l] = null;
}
swfobject = null;
});
}
}();
return {
/* Public API
- Reference: http://code.google.com/p/swfobject/wiki/documentation
*/
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
if (ua.w3 && objectIdStr && swfVersionStr) {
var regObj = {};
regObj.id = objectIdStr;
regObj.swfVersion = swfVersionStr;
regObj.expressInstall = xiSwfUrlStr;
regObj.callbackFn = callbackFn;
regObjArr[regObjArr.length] = regObj;
setVisibility(objectIdStr, false);
}
else if (callbackFn) {
callbackFn({success:false, id:objectIdStr});
}
},
getObjectById: function(objectIdStr) {
if (ua.w3) {
return getObjectById(objectIdStr);
}
},
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
var callbackObj = {success:false, id:replaceElemIdStr};
if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
setVisibility(replaceElemIdStr, false);
addDomLoadEvent(function() {
widthStr += ""; // auto-convert to string
heightStr += "";
var att = {};
if (attObj && typeof attObj === OBJECT) {
for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
att[i] = attObj[i];
}
}
att.data = swfUrlStr;
att.width = widthStr;
att.height = heightStr;
var par = {};
if (parObj && typeof parObj === OBJECT) {
for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
par[j] = parObj[j];
}
}
if (flashvarsObj && typeof flashvarsObj === OBJECT) {
for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + k + "=" + flashvarsObj[k];
}
else {
par.flashvars = k + "=" + flashvarsObj[k];
}
}
}
if (hasPlayerVersion(swfVersionStr)) { // create SWF
var obj = createSWF(att, par, replaceElemIdStr);
if (att.id == replaceElemIdStr) {
setVisibility(replaceElemIdStr, true);
}
callbackObj.success = true;
callbackObj.ref = obj;
}
else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
att.data = xiSwfUrlStr;
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
return;
}
else { // show alternative content
setVisibility(replaceElemIdStr, true);
}
if (callbackFn) { callbackFn(callbackObj); }
});
}
else if (callbackFn) { callbackFn(callbackObj); }
},
switchOffAutoHideShow: function() {
autoHideShow = false;
},
ua: ua,
getFlashPlayerVersion: function() {
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
},
hasFlashPlayerVersion: hasPlayerVersion,
createSWF: function(attObj, parObj, replaceElemIdStr) {
if (ua.w3) {
return createSWF(attObj, parObj, replaceElemIdStr);
}
else {
return undefined;
}
},
showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
if (ua.w3 && canExpressInstall()) {
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
}
},
removeSWF: function(objElemIdStr) {
if (ua.w3) {
removeSWF(objElemIdStr);
}
},
createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
if (ua.w3) {
createCSS(selStr, declStr, mediaStr, newStyleBoolean);
}
},
addDomLoadEvent: addDomLoadEvent,
addLoadEvent: addLoadEvent,
getQueryParamValue: function(param) {
var q = doc.location.search || doc.location.hash;
if (q) {
if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
if (param == null) {
return urlEncodeIfNecessary(q);
}
var pairs = q.split("&");
for (var i = 0; i < pairs.length; i++) {
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
}
}
}
return "";
},
// For internal usage only
expressInstallCallback: function() {
if (isExpressInstallActive) {
var obj = getElementById(EXPRESS_INSTALL_ID);
if (obj && storedAltContent) {
obj.parentNode.replaceChild(storedAltContent, obj);
if (storedAltContentId) {
setVisibility(storedAltContentId, true);
if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
}
if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
}
isExpressInstallActive = false;
}
}
};
}();

View File

@ -296,8 +296,8 @@ $("#userpl_save").click(function() {
/* video controls */
$("#mediarefresh").click(function() {
PLAYER.type = "";
PLAYER.id = "";
PLAYER.mediaType = "";
PLAYER.mediaId = "";
// playerReady triggers the server to send a changeMedia.
// the changeMedia handler then reloads the player
socket.emit("playerReady");

View File

@ -1376,10 +1376,10 @@ function sendVideoUpdate() {
}
PLAYER.getTime(function (seconds) {
socket.emit("mediaUpdate", {
id: PLAYER.videoId,
id: PLAYER.mediaId,
currentTime: seconds,
paused: PLAYER.paused,
type: PLAYER.type
type: PLAYER.mediaType
});
});
}

200
www/js/video.js Normal file

File diff suppressed because one or more lines are too long

BIN
www/video-js.swf Normal file

Binary file not shown.