/**
* Copyright 2013 Calvin 'calzoneman' Montgomery
*
* Licensed under Creative Commons Attribution-NonCommercial 3.0
* See http://creativecommons.org/licenses/by-nc/3.0/
*
*/
// Adds a user to the chatbox userlist
function addUser(name, rank, leader) {
var div = document.createElement('div');
$(div).attr("class", "userlist_item");
var span = document.createElement('span');
var flair = document.createElement('span');
span.innerHTML = name;
div.appendChild(flair);
div.appendChild(span);
fmtUserlistItem(div, rank, leader);
if(RANK >= Rank.Moderator)
addUserDropdown(div, name);
$('#userlist')[0].appendChild(div);
}
// Format a userlist entry based on a person's rank
function fmtUserlistItem(div, rank, leader) {
var span = div.children[1];
if(rank >= Rank.Siteadmin)
$(span).attr("class", "userlist_siteadmin");
else if(rank >= Rank.Owner)
$(span).attr("class", "userlist_owner");
else if(rank >= Rank.Moderator)
$(span).attr("class", "userlist_op");
var flair = div.children[0];
// denote current leader with [L]
if(leader) {
flair.innerHTML = "[L]";
}
else {
flair.innerHTML = "";
}
}
// Adds a dropdown with user actions (promote/demote/leader)
function addUserDropdown(entry, name) {
var div = $('
').addClass("dropdown").appendTo(entry);
var ul = $('').addClass("dropdown-menu").appendTo(div);
ul.attr("role", "menu");
ul.attr("aria-labelledby", "dropdownMenu");
var makeLeader = $('').appendTo(ul);
var a = $('').attr("tabindex", "-1").attr("href", "#").appendTo(makeLeader);
a.text("Make Leader");
a.click(function() {
socket.emit('assignLeader', {
name: name
});
});
var takeLeader = $('').appendTo(ul);
var a = $('').attr("tabindex", "-1").attr("href", "#").appendTo(takeLeader);
a.text("Take Leader");
a.click(function() {
socket.emit('assignLeader', {
name: ""
});
});
var kick = $('').appendTo(ul);
var a = $('').attr("tabindex", "-1").attr("href", "#").appendTo(kick);
a.text("Kick");
a.click(function() {
socket.emit('chatMsg', {
msg: "/kick " + name
});
});
$('').addClass("divider").appendTo(ul);
var promote = $('').appendTo(ul);
var a = $('').attr("tabindex", "-1").attr("href", "#").appendTo(promote);
a.text("Promote");
a.click(function() {
socket.emit('promote', {
name: name
});
});
var demote = $('').appendTo(ul);
var a = $('').attr("tabindex", "-1").attr("href", "#").appendTo(demote);
a.text("Demote");
a.click(function() {
socket.emit('demote', {
name: name
});
});
$(entry).click(function() {
if(ul.css("display") == "none") {
ul.css("display", "block");
}
else {
ul.css("display", "none");
}
});
return ul;
}
function formatChatMessage(data) {
var div = document.createElement('div');
if(data.msgclass == "action") {
var message = document.createElement('span');
$(message).addClass('action');
message.innerHTML = data.username + " " + data.msg;
div.appendChild(message);
}
else {
var name = document.createElement('span');
var message = document.createElement('span');
name.innerHTML = "<" + data.username + "> ";
$(message).addClass(data.msgclass);
message.innerHTML = data.msg;
div.appendChild(name);
div.appendChild(message);
}
return div;
}
// Creates and formats a queue entry
function makeQueueEntry(video) {
var li = $('');
li.attr("class", "well");
var title = $('').attr("class", "qe_title").appendTo(li);
title.text(video.title);
var time = $('').attr("class", "qe_time").appendTo(li);
time.text(video.duration);
var clear = $('').attr("class", "qe_clear").appendTo(li);
return li;
}
// Add buttons to a queue list entry
function addQueueButtons(li) {
var btnstrip = $('').attr("class", "btn-group qe_buttons").prependTo(li);
var btnRemove = $('').attr("class", "btn btn-danger qe_btn").appendTo(btnstrip);
$('').attr("class", "icon-remove").appendTo(btnRemove);
var btnUp = $('').attr("class", "btn qe_btn").appendTo(btnstrip);
$('').attr("class", "icon-arrow-up").appendTo(btnUp);
var btnDown = $('').attr("class", "btn qe_btn").appendTo(btnstrip);
$('').attr("class", "icon-arrow-down").appendTo(btnDown);
var btnNext = $('').attr("class", "btn qe_btn").appendTo(btnstrip);
//$('').attr("class", "icon-play").appendTo(btnNext);
btnNext.text('Next');
// Callback time
$(btnRemove).click(function() {
btnstrip.remove();
var idx = $('#queue').children().index(li);
socket.emit('unqueue', { pos: idx });
});
$(btnUp).click(function() {
var idx = $('#queue').children().index(li);
socket.emit('moveMedia', {
src: idx,
dest: idx-1
});
});
$(btnDown).click(function() {
var idx = $('#queue').children().index(li);
socket.emit('moveMedia', {
src: idx,
dest: idx+1
});
});
$(btnNext).click(function() {
var idx = $('#queue').children().index(li);
var dest = idx < POSITION ? POSITION : POSITION + 1;
socket.emit('moveMedia', {
src: idx,
dest: dest
});
});
}
// Add buttons to a list entry for the library search results
function addLibraryButtons(li, id) {
var btnstrip = $('').attr("class", "btn-group qe_buttons").prependTo(li);
var btnNext = $('').attr("class", "btn qe_btn").appendTo(btnstrip);
//$('').attr("class", "icon-play").appendTo(btnNext);
btnNext.text('Next');
var btnEnd = $('').attr("class", "btn qe_btn").appendTo(btnstrip);
//$('').attr("class", "icon-fast-forward").appendTo(btnEnd);
btnEnd.text('End');
// Callback time
$(btnNext).click(function() {
socket.emit('queue', {
id: id,
pos: "next"
});
});
$(btnEnd).click(function() {
socket.emit('queue', {
id: id,
pos: "end"
});
});
}
// Rearranges the queue
function moveVideo(src, dest) {
var li = $('#queue').children()[src];
var ul = $('#queue')[0];
$(li).hide('blind', function() {
ul.removeChild(li);
if(dest == ul.children.length) {
ul.appendChild(li);
}
else {
ul.insertBefore(li, ul.getElementsByTagName('li')[dest]);
}
$(li).show('blind');
});
if(src < POSITION && dest >= POSITION)
POSITION--;
if(src > POSITION && dest < POSITION)
POSITION++;
}
// YouTube Synchronization
function updateYT(data) {
if(MEDIATYPE != "yt") {
removeCurrentPlayer();
MEDIATYPE = "yt";
// Note to Soundcloud/Vimeo API designers:
// YouTube's API is actually nice to use
PLAYER = new YT.Player('ytapiplayer', {
height: '390',
width: '640',
videoId: '',
playerVars: {
'autoplay': 0,
'controls': 1,
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// Load new video
if(PLAYER.getVideoUrl && data.id != parseYTURL(PLAYER.getVideoUrl())) {
PLAYER.loadVideoById(data.id, data.currentTime, $('#quality').val());
if(data.paused)
PLAYER.pauseVideo();
}
// Sync playback time
else if(PLAYER.seekTo) {
if(Math.abs(PLAYER.getCurrentTime() - data.currentTime) > SYNC_THRESHOLD)
PLAYER.seekTo(data.currentTime, true);
if(!data.paused)
PLAYER.playVideo();
}
}
// Soundcloud synchronization
function updateSC(data) {
if(MEDIATYPE != "sc") {
var currentEmbed = $("#ytapiplayer");
var iframe = $("").insertBefore(currentEmbed);
currentEmbed.remove();
iframe.attr("id","ytapiplayer");
iframe.attr("src", "https://w.soundcloud.com/player/?url=");
iframe.css("width", "100%").attr("height", "166")
.attr("frameborder", "no");
PLAYER = SC.Widget('ytapiplayer');
MEDIATYPE = "sc";
}
// Server is on a different soundcloud track than client
if(PLAYER.mediaId != data.id) {
PLAYER.load(data.id, {
auto_play: true
});
// Keep track of current ID
PLAYER.mediaId = data.id;
}
// Soundcloud's API is async
// Query the playback position and compare that with the sync packet
PLAYER.getPosition(function(pos) {
if(Math.abs(pos / 1000 - data.currentTime) > SYNC_THRESHOLD) {
PLAYER.seekTo(data.currentTime * 1000);
}
});
}
// Vimeo synchronization
// URGH building a synchronizing tool is so frustrating when
// these APIs are designed to be async
function updateVI(data) {
if(MEDIATYPE != "vi") {
initVI(data);
}
// Either vimeo's API doesn't support loading a new video
// or their terrible documentation doesn't document it
else if(data.id != PLAYER.videoid) {
initVI(data);
}
PLAYER.api('getCurrentTime', function(time) {
if(Math.abs(time - data.currentTime) > SYNC_THRESHOLD) {
PLAYER.api('seekTo', data.currentTime);
}
});
}
// Loads up a Vimeo player
function initVI(data) {
var currentEmbed = $("#ytapiplayer");
var div = currentEmbed.parent();
currentEmbed.remove();
// Ugly but it's the only way I managed to get the API calls to work
div[0].innerHTML += '';
// $f() is defined by froogaloop, Vimeo's API wrapper
PLAYER = $f($('iframe')[0]);
// So we can retrieve the ID synchronously instead of waiting for
// getVideoId with a callback
PLAYER.videoid = data.id;
PLAYER.addEvent('ready', function() {
// Autoplay
PLAYER.api('play');
});
MEDIATYPE = "vi";
}
function loadTwitch(channel) {
MEDIATYPE = "tw";
removeCurrentPlayer();
var url = "http://www.twitch.tv/widgets/live_embed_player.swf?channel="+channel;
var params = {
allowFullScreen:"true",
allowScriptAccess:"always",
allowNetworking:"all",
movie:"http://www.twitch.tv/widgets/live_embed_player.swf",
id: "live_embed_player_flash",
flashvars:"hostname=www.twitch.tv&channel="+channel+"&auto_play=true&start_volume=100"
};
swfobject.embedSWF( url, "ytapiplayer", '640', '390', "8", null, null, params, {} );
}
function loadLivestream(channel) {
MEDIATYPE = "li";
removeCurrentPlayer();
flashvars = { channel: channel };
params = { AllowScriptAccess: 'always' };
swfobject.embedSWF("http://cdn.livestream.com/chromelessPlayer/v20/playerapi.swf", "ytapiplayer", "640", "390", "9.0.0", "expressInstall.swf", flashvars, params);
}
function removeCurrentPlayer(){
var currentEmbed = $("#ytapiplayer");
var placeholder = $("").insertBefore(currentEmbed);
currentEmbed.remove();
placeholder.attr("id","ytapiplayer");
}
function parseVideoURL(url){
if(typeof(url) != "string")
return null;
if(url.indexOf("youtu") != -1)
return [parseYTURL(url), "yt"];
else if(url.indexOf("twitch") != -1)
return [parseTwitch(url), "tw"];
else if(url.indexOf("livestream") != -1)
return [parseLivestream(url), "li"];
else if(url.indexOf("soundcloud") != -1)
return [url, "sc"];
else if(url.indexOf("vimeo") != -1)
return [parseVimeo(url), "vi"];
}
function parseYTURL(url) {
url = url.replace("feature=player_embedded&", "");
if(url.indexOf("&list=") != -1)
url = url.substring(0, url.indexOf("&list="));
var m = url.match(/youtube\.com\/watch\\?v=([^&]*)/);
if(m) {
// Extract ID
return m[1];
}
var m = url.match(/youtu\.be\/([^&]*)/);
if(m) {
// Extract ID
return m[1];
}
// Final try
var m = url.match(/v=([^&]*)/);
if(m) {
// Extract ID
return m[1];
}
return null;
}
function parseTwitch(url) {
var m = url.match(/twitch\.tv\/([a-zA-Z0-9]*)/);
if(m) {
// Extract channel name
return m[1];
}
return null;
}
function parseLivestream(url) {
var m = url.match(/livestream\.com\/([a-zA-Z0-9]*)/);
if(m) {
// Extract channel name
return m[1];
}
return null;
}
function parseVimeo(url) {
var m = url.match(/vimeo\.com\/([0-9]+)/);
if(m) {
// Extract video ID
return m[1];
}
return null;
}
function closePoll() {
if($('#pollcontainer .active').length != 0) {
var poll = $('#pollcontainer .active');
poll.removeClass("active").addClass("muted");
poll.find('.option button').each(function() {
$(this).attr('disabled', 'disabled');
});
poll.find('.btn-danger').each(function() {
$(this).remove()
});
}
}
function addPoll(data) {
closePoll();
var poll = $('').addClass('well active').prependTo($('#pollcontainer'));
$('').addClass('close pull-right').text('×')
.appendTo(poll)
.click(function() { poll.remove(); });
if(RANK >= Rank.Moderator) {
$('').addClass('btn btn-danger pull-right').text('Close Poll')
.appendTo(poll)
.click(function() {
socket.emit('closePoll')
});
}
$('').text(data.title).appendTo(poll);
for(var i = 0; i < data.options.length; i++) {
var callback = (function(i) { return function() {
console.log(i);
socket.emit('vote', {
option: i
});
poll.find('.option button').each(function() {
$(this).attr('disabled', 'disabled');
});
} })(i);
$('').addClass('btn').text(data.counts[i])
.prependTo($('').addClass('option').text(data.options[i])
.appendTo(poll))
.click(callback);
}
}
function updatePoll(data) {
var poll = $('#pollcontainer .active');
var i = 0;
poll.find('.option button').each(function() {
$(this).text(data.counts[i]);
i++;
});
}
function showChannelRegistration() {
var div = $('').addClass('alert alert-info').attr('id', 'chregnotice')
.insertAfter($('.row')[0]);
$('').addClass('close pull-right').text('×')
.appendTo(div)
.click(function() { div.remove(); });
$('').text("This channel isn't registered").appendTo(div);
$('').addClass('btn btn-primary').text('Register it')
.appendTo(div)
.click(function() {
socket.emit('registerChannel');
});
}