Here we go, merging v2.0

This commit is contained in:
calzoneman 2013-06-23 14:24:45 -04:00
commit f149f9a658
36 changed files with 5431 additions and 4510 deletions

145
acp.js Normal file
View File

@ -0,0 +1,145 @@
var Server = require("./server");
var Auth = require("./auth");
var Database = require("./database");
var ActionLog = require("./actionlog");
module.exports = {
init: function(user) {
ActionLog.record(user.ip, user.name, "acp-init");
user.socket.on("acp-announce", function(data) {
ActionLog.record(user.ip, user.name, "acp-announce", [data]);
Server.announcement = data;
Server.io.sockets.emit("announcement", data);
});
user.socket.on("acp-announce-clear", function() {
ActionLog.record(user.ip, user.name, "acp-announce-clear");
Server.announcement = null;
});
user.socket.on("acp-global-ban", function(data) {
ActionLog.record(user.ip, user.name, "acp-global-ban", [data.ip]);
Database.globalBanIP(data.ip, data.note);
user.socket.emit("acp-global-banlist", Database.refreshGlobalBans());
});
user.socket.on("acp-global-unban", function(ip) {
ActionLog.record(user.ip, user.name, "acp-global-unban", [data.ip]);
Database.globalUnbanIP(ip);
user.socket.emit("acp-global-banlist", Database.refreshGlobalBans());
});
user.socket.emit("acp-global-banlist", Database.refreshGlobalBans());
user.socket.on("acp-lookup-user", function(name) {
var db = Database.getConnection();
if(!db) {
return;
}
var query = Database.createQuery(
"SELECT id,uname,global_rank,profile_image,profile_text,email FROM registrations WHERE uname LIKE ?",
["%"+name+"%"]
);
var res = db.querySync(query);
if(!res)
return;
var rows = res.fetchAllSync();
user.socket.emit("acp-userdata", rows);
});
user.socket.on("acp-reset-password", function(data) {
if(Auth.getGlobalRank(data.name) >= user.global_rank)
return;
try {
var hash = Database.generatePasswordReset(user.ip, data.name, data.email);
ActionLog.record(user.ip, user.name, "acp-reset-password", [data.name]);
}
catch(e) {
user.socket.emit("acp-reset-password", {
success: false,
error: e
});
return;
}
if(hash) {
user.socket.emit("acp-reset-password", {
success: true,
hash: hash
});
}
else {
user.socket.emit("acp-reset-password", {
success: false,
error: "Reset failed"
});
}
});
user.socket.on("acp-set-rank", function(data) {
if(data.rank < 1 || data.rank >= user.global_rank)
return;
if(Auth.getGlobalRank(data.name) >= user.global_rank)
return;
var db = Database.getConnection();
if(!db)
return;
ActionLog.record(user.ip, user.name, "acp-set-rank", [data]);
var query = Database.createQuery(
"UPDATE registrations SET global_rank=? WHERE uname=?",
[data.name, data.rank]
);
var res = db.querySync(query);
if(!res)
return;
user.socket.emit("acp-set-rank", data);
});
user.socket.on("acp-list-loaded", function() {
var chans = [];
for(var c in Server.channels) {
var chan = Server.channels[c];
if(!chan)
continue;
chans.push({
name: c,
title: chan.opts.pagetitle,
usercount: chan.users.length,
mediatitle: chan.media ? chan.media.title : "-",
is_public: chan.opts.show_public,
registered: chan.registered
});
}
user.socket.emit("acp-list-loaded", chans);
});
user.socket.on("acp-channel-unload", function(data) {
if(data.name in Server.channels) {
var c = Server.channels[data.name];
if(!c)
return;
ActionLog.record(user.ip, user.name, "acp-channel-unload");
c.initialized = data.save;
c.users.forEach(function(u) {
c.kick(u, "Channel shutting down");
});
Server.unload(c);
}
});
user.socket.on("acp-actionlog-clear", function(data) {
ActionLog.clear(data);
ActionLog.record(user.ip, user.name, "acp-actionlog-clear", data);
});
}
}

65
actionlog.js Normal file
View File

@ -0,0 +1,65 @@
var fs = require("fs");
var Logger = require("./logger");
var buffer = [];
exports.record = function(ip, name, action, args) {
buffer.push(JSON.stringify({
ip: ip,
name: name,
action: action,
args: args ? args : [],
time: Date.now()
}));
}
exports.flush = function() {
if(buffer.length == 0)
return;
var text = buffer.join("\n") + "\n";
buffer = [];
fs.appendFile("action.log", text, function(err) {
if(err) {
errlog.log("Append to actionlog failed: ");
errlog.log(err);
}
});
}
exports.clear = function(actions) {
clearInterval(FLUSH_TMR);
var rs = fs.createReadStream("action.log");
var ws = fs.createWriteStream("action.log.tmp");
function handleLine(ln) {
try {
js = JSON.parse(ln);
if(actions.indexOf(js.action) == -1)
ws.write(ln + "\n");
}
catch(e) { }
}
var buffer = "";
rs.on("data", function(chunk) {
buffer += chunk;
if(buffer.indexOf("\n") != -1) {
var lines = buffer.split("\n");
buffer = lines[lines.length - 1];
lines.length = lines.length - 1;
lines.forEach(handleLine);
}
});
rs.on("end", function() {
handleLine(buffer);
ws.end();
});
try {
fs.renameSync("action.log.tmp", "action.log");
}
catch(e) {
Logger.errlog.log("Failed to move action.log.tmp => action.log");
Logger.errlog.log(e);
}
FLUSH_TMR = setInterval(exports.flush, 15000);
}
var FLUSH_TMR = setInterval(exports.flush, 15000);

126
api.js
View File

@ -15,6 +15,7 @@ var Logger = require("./logger.js");
var apilog = new Logger.Logger("api.log"); var apilog = new Logger.Logger("api.log");
var Database = require("./database.js"); var Database = require("./database.js");
var Config = require("./config.js"); var Config = require("./config.js");
var ActionLog = require("./actionlog.js");
var fs = require("fs"); var fs = require("fs");
var plainHandlers = { var plainHandlers = {
@ -32,11 +33,21 @@ var jsonHandlers = {
"setprofile" : handleProfileChange, "setprofile" : handleProfileChange,
"getprofile" : handleProfileGet, "getprofile" : handleProfileGet,
"setemail" : handleEmailChange, "setemail" : handleEmailChange,
"globalbans" : handleGlobalBans,
"admreports" : handleAdmReports, "admreports" : handleAdmReports,
"acppwreset" : handleAcpPasswordReset
}; };
function getClientIP(req) {
var ip;
var forward = req.header("x-forwarded-for");
if(forward) {
ip = forward.split(",")[0];
}
if(!ip) {
ip = req.connection.remoteAddress;
}
return ip;
}
function handle(path, req, res) { function handle(path, req, res) {
var parts = path.split("/"); var parts = path.split("/");
var last = parts[parts.length - 1]; var last = parts[parts.length - 1];
@ -193,12 +204,14 @@ function handleLogin(params, req, res) {
var row = Auth.login(name, pw, session); var row = Auth.login(name, pw, session);
if(row) { if(row) {
ActionLog.record(getClientIP(req), name, "login-success");
sendJSON(res, { sendJSON(res, {
success: true, success: true,
session: row.session_hash session: row.session_hash
}); });
} }
else { else {
ActionLog.record(getClientIP(req), name, "login-failure");
sendJSON(res, { sendJSON(res, {
error: "Invalid username/password", error: "Invalid username/password",
success: false success: false
@ -219,6 +232,7 @@ function handlePasswordChange(params, req, res) {
} }
var row = Auth.login(name, oldpw); var row = Auth.login(name, oldpw);
if(row) { if(row) {
ActionLog.record(getClientIP(req), name, "password-change");
var success = Auth.setUserPassword(name, newpw); var success = Auth.setUserPassword(name, newpw);
sendJSON(res, { sendJSON(res, {
success: success, success: success,
@ -237,11 +251,12 @@ function handlePasswordChange(params, req, res) {
function handlePasswordReset(params, req, res) { function handlePasswordReset(params, req, res) {
var name = params.name || ""; var name = params.name || "";
var email = unescape(params.email || ""); var email = unescape(params.email || "");
var ip = req.socket.address().address; var ip = getClientIP(req);
var hash = false; var hash = false;
try { try {
hash = Database.generatePasswordReset(ip, name, email); hash = Database.generatePasswordReset(ip, name, email);
ActionLog.record(ip, name, "password-reset-generate");
} }
catch(e) { catch(e) {
sendJSON(res, { sendJSON(res, {
@ -301,7 +316,7 @@ function handlePasswordReset(params, req, res) {
function handlePasswordRecover(params, req, res) { function handlePasswordRecover(params, req, res) {
var hash = params.hash || ""; var hash = params.hash || "";
var ip = req.socket.address().address; var ip = getClientIP(req);
try { try {
var info = Database.recoverPassword(hash); var info = Database.recoverPassword(hash);
@ -310,10 +325,12 @@ function handlePasswordRecover(params, req, res) {
name: info[0], name: info[0],
pw: info[1] pw: info[1]
}); });
ActionLog.record(ip, name, "password-recover-success");
Logger.syslog.log(ip + " recovered password for " + name); Logger.syslog.log(ip + " recovered password for " + name);
return; return;
} }
catch(e) { catch(e) {
ActionLog.record(ip, name, "password-recover-failure");
sendJSON(res, { sendJSON(res, {
success: false, success: false,
error: e error: e
@ -412,6 +429,7 @@ function handleEmailChange(params, req, res) {
var row = Auth.login(name, pw); var row = Auth.login(name, pw);
if(row) { if(row) {
var success = Database.setUserEmail(name, email); var success = Database.setUserEmail(name, email);
ActionLog.record(getClientIP(req), name, "email-update", [email]);
sendJSON(res, { sendJSON(res, {
success: success, success: success,
error: success ? "" : "Email update failed", error: success ? "" : "Email update failed",
@ -438,6 +456,7 @@ function handleRegister(params, req, res) {
return; return;
} }
else if(Auth.isRegistered(name)) { else if(Auth.isRegistered(name)) {
ActionLog.record(getClientIP(req), name, "register-failure");
sendJSON(res, { sendJSON(res, {
success: false, success: false,
error: "That username is already taken" error: "That username is already taken"
@ -445,6 +464,7 @@ function handleRegister(params, req, res) {
return false; return false;
} }
else if(!Auth.validateName(name)) { else if(!Auth.validateName(name)) {
ActionLog.record(getClientIP(req), name, "register-failure");
sendJSON(res, { sendJSON(res, {
success: false, success: false,
error: "Invalid username. Usernames must be 1-20 characters long and consist only of alphanumeric characters and underscores" error: "Invalid username. Usernames must be 1-20 characters long and consist only of alphanumeric characters and underscores"
@ -453,7 +473,8 @@ function handleRegister(params, req, res) {
else { else {
var session = Auth.register(name, pw); var session = Auth.register(name, pw);
if(session) { if(session) {
Logger.syslog.log(this.ip + " registered " + name); ActionLog.record(getClientIP(req), name, "register-success");
Logger.syslog.log(getClientIP(req) + " registered " + name);
sendJSON(res, { sendJSON(res, {
success: true, success: true,
session: session session: session
@ -468,103 +489,12 @@ function handleRegister(params, req, res) {
} }
} }
function handleGlobalBans(params, req, res) {
var name = params.name || "";
var pw = params.pw || "";
var session = params.session || "";
var row = Auth.login(name, pw, session);
if(!row || row.global_rank < 255) {
res.send(403);
return;
}
var action = params.action || "list";
if(action == "list") {
var gbans = Database.refreshGlobalBans();
sendJSON(res, gbans);
}
else if(action == "add") {
var ip = params.ip || "";
var reason = params.reason || "";
if(!ip.match(/\d+\.\d+\.(\d+\.(\d+)?)?/)) {
sendJSON(res, {
error: "Invalid IP address"
});
return;
}
var result = Database.globalBanIP(ip, reason);
sendJSON(res, {
success: result,
ip: ip,
reason: reason
});
}
else if(action == "remove") {
var ip = params.ip || "";
if(!ip.match(/\d+\.\d+\.(\d+\.(\d+)?)?/)) {
sendJSON(res, {
error: "Invalid IP address"
});
return;
}
var result = Database.globalUnbanIP(ip);
sendJSON(res, {
success: result,
ip: ip,
});
}
else {
sendJSON(res, {
error: "Invalid action: " + action
});
}
}
function handleAdmReports(params, req, res) { function handleAdmReports(params, req, res) {
sendJSON(res, { sendJSON(res, {
error: "Not implemented" error: "Not implemented"
}); });
} }
function handleAcpPasswordReset(params, req, res) {
var name = params.name || "";
var pw = params.pw || "";
var session = params.session || "";
var row = Auth.login(name, pw, session);
if(!row || row.global_rank < 255) {
res.send(403);
return;
}
var action = params.action || "";
if(action == "reset") {
var uname = params.reset_name;
if(Auth.getGlobalRank(uname) > row.global_rank) {
sendJSON(res, {
success: false
});
return;
}
var new_pw = Database.resetPassword(uname);
if(new_pw) {
sendJSON(res, {
success: true,
pw: new_pw
});
}
else {
sendJSON(res, {
success: false
});
}
}
else {
sendJSON(res, {
success: false
});
}
}
// Helper function // Helper function
function pipeLast(res, file, len) { function pipeLast(res, file, len) {
fs.stat(file, function(err, data) { fs.stat(file, function(err, data) {
@ -599,6 +529,10 @@ function handleReadLog(params, req, res) {
else if(type == "err") { else if(type == "err") {
pipeLast(res, "error.log", 1024*1024); pipeLast(res, "error.log", 1024*1024);
} }
else if(type == "action") {
ActionLog.flush();
pipeLast(res, "action.log", 1024*1024*100);
}
else if(type == "channel") { else if(type == "channel") {
var chan = params.channel || ""; var chan = params.channel || "";
fs.exists("chanlogs/" + chan + ".log", function(exists) { fs.exists("chanlogs/" + chan + ".log", function(exists) {

View File

@ -20,7 +20,7 @@ var Logger = require("./logger.js");
exports.isRegistered = function(name) { exports.isRegistered = function(name) {
var db = Database.getConnection(); var db = Database.getConnection();
if(!db) { if(!db) {
return true; throw "Database failure";
} }
var query = Database.createQuery( var query = Database.createQuery(
"SELECT * FROM `registrations` WHERE uname=?", "SELECT * FROM `registrations` WHERE uname=?",
@ -90,7 +90,7 @@ exports.login = function(name, pw, session) {
exports.loginPassword = function(name, pw) { exports.loginPassword = function(name, pw) {
var db = Database.getConnection(); var db = Database.getConnection();
if(!db) { if(!db) {
return false; throw "Database failure";
} }
var query = Database.createQuery( var query = Database.createQuery(
"SELECT * FROM `registrations` WHERE uname=?", "SELECT * FROM `registrations` WHERE uname=?",
@ -140,7 +140,7 @@ exports.createSession = function(name) {
var hash = hashlib.sha256(salt + name); var hash = hashlib.sha256(salt + name);
var db = Database.getConnection(); var db = Database.getConnection();
if(!db) { if(!db) {
return false; throw "Database failure";
} }
var query = Database.createQuery( var query = Database.createQuery(
["UPDATE `registrations` SET ", ["UPDATE `registrations` SET ",
@ -156,7 +156,7 @@ exports.createSession = function(name) {
exports.loginSession = function(name, hash) { exports.loginSession = function(name, hash) {
var db = Database.getConnection(); var db = Database.getConnection();
if(!db) { if(!db) {
return false; throw "Database failure";
} }
var query = Database.createQuery( var query = Database.createQuery(
"SELECT * FROM `registrations` WHERE `uname`=?", "SELECT * FROM `registrations` WHERE `uname`=?",

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,29 @@ function handle(chan, user, msg, data) {
chan.chainMessage(user, msg.substring(3), {modflair: user.rank}) chan.chainMessage(user, msg.substring(3), {modflair: user.rank})
} }
} }
else if(msg.indexOf("/a ") == 0) {
if(user.rank >= Rank.Siteadmin) {
var flair = {
superadminflair: {
labelclass: "label-important",
icon: "icon-globe"
}
};
var args = msg.substring(3).split(" ");
var cargs = [];
for(var i = 0; i < args.length; i++) {
var a = args[i];
if(a.indexOf("!icon-") == 0)
flair.superadminflair.icon = a.substring(1);
else if(a.indexOf("!label-") == 0)
flair.superadminflair.labelclass = a.substring(1);
else {
cargs.push(a);
}
}
chan.chainMessage(user, cargs.join(" "), flair);
}
}
else if(msg.indexOf("/kick ") == 0) { else if(msg.indexOf("/kick ") == 0) {
handleKick(chan, user, msg.substring(6).split(" ")); handleKick(chan, user, msg.substring(6).split(" "));
} }
@ -77,29 +100,13 @@ function handleKick(chan, user, args) {
} }
function handleIPBan(chan, user, args) { function handleIPBan(chan, user, args) {
if(chan.hasPermission(user, "ban") && args.length > 0) { chan.tryIPBan(user, args[0], args[1]);
args[0] = args[0].toLowerCase(); // Ban the name too for good measure
var kickee; chan.tryNameBan(user, args[0]);
for(var i = 0; i < chan.users.length; i++) {
if(chan.users[i].name.toLowerCase() == args[0]) {
kickee = chan.users[i];
break;
}
}
if(kickee) {
chan.tryIPBan(user, {
id: chan.hideIP(kickee.ip),
name: kickee.name
});
}
}
} }
function handleBan(chan, user, args) { function handleBan(chan, user, args) {
if(chan.hasPermission(user, "ban") && args.length > 0) { chan.tryNameBan(user, args[0]);
args[0] = args[0].toLowerCase();
chan.banName(user, args[0]);
}
} }
function handleUnban(chan, user, args) { function handleUnban(chan, user, args) {

View File

@ -38,7 +38,7 @@ function getConnection() {
db = mysql.createConnectionSync(); db = mysql.createConnectionSync();
db.connectSync(SERVER, USER, PASSWORD, DATABASE); db.connectSync(SERVER, USER, PASSWORD, DATABASE);
if(!db.connectedSync()) { if(!db.connectedSync()) {
//Logger.errlog.log("DB connection failed"); Logger.errlog.log("DB connection failed");
return false; return false;
} }
if(CONFIG.DEBUG) { if(CONFIG.DEBUG) {
@ -173,6 +173,19 @@ function init() {
if(!results) { if(!results) {
Logger.errlog.log("! Failed to create playlist table"); Logger.errlog.log("! Failed to create playlist table");
} }
// Create user aliases table
query = ["CREATE TABLE IF NOT EXISTS `aliases` (",
"`visit_id` INT NOT NULL AUTO_INCREMENT,",
"`ip` VARCHAR(15) NOT NULL,",
"`name` VARCHAR(20) NOT NULL,",
"`time` BIGINT NOT NULL,",
"PRIMARY KEY (`visit_id`), INDEX (`ip`))",
"ENGINE = MyISAM;"].join("");
results = db.querySync(query);
if(!results) {
Logger.errlog.log("! Failed to create aliases table");
}
} }
/* REGION Global Bans */ /* REGION Global Bans */
@ -672,6 +685,16 @@ function setUserEmail(name, email) {
return true; return true;
} }
function genSalt() {
var chars = "abcdefgihjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789!@#$%^&*_+=~";
var salt = [];
for(var i = 0; i < 32; i++) {
salt.push(chars[parseInt(Math.random()*chars.length)]);
}
return salt.join('');
}
function generatePasswordReset(ip, name, email) { function generatePasswordReset(ip, name, email) {
var db = getConnection(); var db = getConnection();
if(!db) { if(!db) {
@ -698,7 +721,7 @@ function generatePasswordReset(ip, name, email) {
} }
// Validation complete, now time to reset it // Validation complete, now time to reset it
var hash = hashlib.sha256(Date.now() + name); var hash = hashlib.sha256(genSalt() + name);
var exp = Date.now() + 24*60*60*1000; var exp = Date.now() + 24*60*60*1000;
query = createQuery( query = createQuery(
["INSERT INTO `password_reset` (", ["INSERT INTO `password_reset` (",
@ -885,6 +908,92 @@ function deleteUserPlaylist(user, name) {
return results; return results;
} }
/* User Aliases */
function recordVisit(ip, name) {
var db = getConnection();
if(!db) {
return false;
}
var time = Date.now();
db.querySync(createQuery(
"DELETE FROM aliases WHERE ip=? AND name=?",
[ip, name]
));
var query = createQuery(
"INSERT INTO aliases VALUES (NULL, ?, ?, ?)",
[ip, name, time]
);
var results = db.querySync(query);
if(!results) {
Logger.errlog.log("! Failed to record visit");
}
// Keep most recent 5 records per IP
results = db.querySync(createQuery(
["DELETE FROM aliases WHERE ip=? AND visit_id NOT IN (",
"SELECT visit_id FROM (",
"SELECT visit_id,time FROM aliases WHERE ip=? ORDER BY time DESC LIMIT 5",
") foo",
");"].join(""),
[ip, ip]
));
return results;
}
function getAliases(ip) {
var db = getConnection();
if(!db) {
return [];
}
var query = createQuery(
"SELECT name FROM aliases WHERE ip=?",
[ip]
);
var results = db.querySync(query);
if(!results) {
Logger.errlog.log("! Failed to retrieve aliases");
return [];
}
var names = [];
results.fetchAllSync().forEach(function(row) {
names.push(row.name);
});
return names;
}
function ipForName(name) {
var db = getConnection();
if(!db) {
return [];
}
var query = createQuery(
"SELECT ip FROM aliases WHERE name=?",
[name]
);
var results = db.querySync(query);
if(!results) {
Logger.errlog.log("! Failed to retrieve IP for name");
return [];
}
var ips = [];
results.fetchAllSync().forEach(function(row) {
ips.push(row.ip);
});
return ips;
}
exports.setup = setup; exports.setup = setup;
exports.getConnection = getConnection; exports.getConnection = getConnection;
exports.createQuery = createQuery; exports.createQuery = createQuery;
@ -914,3 +1023,6 @@ exports.getUserPlaylists = getUserPlaylists;
exports.loadUserPlaylist = loadUserPlaylist; exports.loadUserPlaylist = loadUserPlaylist;
exports.saveUserPlaylist = saveUserPlaylist; exports.saveUserPlaylist = saveUserPlaylist;
exports.deleteUserPlaylist = deleteUserPlaylist; exports.deleteUserPlaylist = deleteUserPlaylist;
exports.recordVisit = recordVisit;
exports.getAliases = getAliases;
exports.ipForName = ipForName;

View File

@ -16,6 +16,7 @@ var Filter = function(name, regex, flags, replace) {
this.regex = new RegExp(this.source, this.flags); this.regex = new RegExp(this.source, this.flags);
this.replace = replace; this.replace = replace;
this.active = true; this.active = true;
this.filterlinks = false;
} }
Filter.prototype.pack = function() { Filter.prototype.pack = function() {
@ -24,7 +25,8 @@ Filter.prototype.pack = function() {
source: this.source, source: this.source,
flags: this.flags, flags: this.flags,
replace: this.replace, replace: this.replace,
active: this.active active: this.active,
filterlinks: this.filterlinks
} }
} }

View File

@ -141,6 +141,7 @@ function newConnection(req, res) {
exports.newConnection = newConnection; exports.newConnection = newConnection;
function msgReceived(req, res) { function msgReceived(req, res) {
res.callback = req.query.callback;
var h = req.params.hash; var h = req.params.hash;
if(h in clients && clients[h] != null) { if(h in clients && clients[h] != null) {
var str = req.params.str; var str = req.params.str;

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery", "author": "Calvin Montgomery",
"name": "CyTube", "name": "CyTube",
"description": "Online media synchronizer and chat", "description": "Online media synchronizer and chat",
"version": "1.9.5", "version": "2.0.0",
"repository": { "repository": {
"url": "http://github.com/calzoneman/sync" "url": "http://github.com/calzoneman/sync"
}, },

View File

@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
const VERSION = "1.9.5"; const VERSION = "2.0.0";
var fs = require("fs"); var fs = require("fs");
var Logger = require("./logger.js"); var Logger = require("./logger.js");

259
user.js
View File

@ -17,6 +17,8 @@ var Server = require("./server.js");
var Database = require("./database.js"); var Database = require("./database.js");
var Logger = require("./logger.js"); var Logger = require("./logger.js");
var Config = require("./config.js"); var Config = require("./config.js");
var ACP = require("./acp");
var ActionLog = require("./actionlog");
// Represents a client connected via socket.io // Represents a client connected via socket.io
var User = function(socket, ip) { var User = function(socket, ip) {
@ -24,6 +26,7 @@ var User = function(socket, ip) {
this.socket = socket; this.socket = socket;
this.loggedIn = false; this.loggedIn = false;
this.rank = Rank.Anonymous; this.rank = Rank.Anonymous;
this.global_rank = Rank.Anonymous;
this.channel = null; this.channel = null;
this.name = ""; this.name = "";
this.meta = { this.meta = {
@ -206,7 +209,7 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("unqueue", function(data) { this.socket.on("delete", function(data) {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryDequeue(this, data); this.channel.tryDequeue(this, data);
} }
@ -236,21 +239,21 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("clearqueue", function() { this.socket.on("clearPlaylist", function() {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryClearqueue(this); this.channel.tryClearqueue(this);
} }
}.bind(this)); }.bind(this));
this.socket.on("shufflequeue", function() { this.socket.on("shufflePlaylist", function() {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryShufflequeue(this); this.channel.tryShufflequeue(this);
} }
}.bind(this)); }.bind(this));
this.socket.on("queueLock", function(data) { this.socket.on("togglePlaylistLock", function() {
if(this.channel != null) { if(this.channel != null) {
this.channel.trySetLock(this, data); this.channel.tryToggleLock(this);
} }
}.bind(this)); }.bind(this));
@ -260,18 +263,18 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("searchLibrary", function(data) { this.socket.on("searchMedia", function(data) {
if(this.channel != null) { if(this.channel != null) {
if(data.yt) { if(data.source == "yt") {
var callback = function(vids) { var callback = function(vids) {
this.socket.emit("librarySearchResults", { this.socket.emit("searchResults", {
results: vids results: vids
}); });
}.bind(this); }.bind(this);
this.channel.search(data.query, callback); this.channel.search(data.query, callback);
} }
else { else {
this.socket.emit("librarySearchResults", { this.socket.emit("searchResults", {
results: this.channel.search(data.query) results: this.channel.search(data.query)
}); });
} }
@ -331,12 +334,6 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("adm", function(data) {
if(Rank.hasPermission(this, "acp")) {
this.handleAdm(data);
}
}.bind(this));
this.socket.on("announce", function(data) { this.socket.on("announce", function(data) {
if(Rank.hasPermission(this, "announce")) { if(Rank.hasPermission(this, "announce")) {
if(data.clear) { if(data.clear) {
@ -349,7 +346,7 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("channelOpts", function(data) { this.socket.on("setOptions", function(data) {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryUpdateOptions(this, data); this.channel.tryUpdateOptions(this, data);
} }
@ -373,31 +370,53 @@ User.prototype.initCallbacks = function() {
} }
}.bind(this)); }.bind(this));
this.socket.on("chatFilter", function(data) { this.socket.on("updateFilter", function(data) {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryChangeFilter(this, data); this.channel.tryUpdateFilter(this, data);
} }
}.bind(this)); }.bind(this));
this.socket.on("updateMotd", function(data) { this.socket.on("removeFilter", function(data) {
if(this.channel != null) {
this.channel.tryRemoveFilter(this, data);
}
}.bind(this));
this.socket.on("moveFilter", function(data) {
if(this.channel != null) {
this.channel.tryMoveFilter(this, data);
}
}.bind(this));
this.socket.on("setMotd", function(data) {
if(this.channel != null) { if(this.channel != null) {
this.channel.tryUpdateMotd(this, data); this.channel.tryUpdateMotd(this, data);
} }
}.bind(this)); }.bind(this));
this.socket.on("requestAcl", function() { this.socket.on("requestLoginHistory", function() {
if(this.channel != null) { if(this.channel != null) {
this.channel.sendACL(this); this.channel.sendLoginHistory(this);
this.noflood("requestAcl", 0.25);
} }
}.bind(this)); }.bind(this));
this.socket.on("requestSeenlogins", function() { this.socket.on("requestBanlist", function() {
if(this.channel != null) { if(this.channel != null) {
if(this.noflood("requestSeenLogins", 0.25)) { this.channel.sendBanlist(this);
}
}.bind(this));
this.socket.on("requestChatFilters", function() {
if(this.channel != null) {
this.channel.sendChatFilters(this);
}
}.bind(this));
this.socket.on("requestChannelRanks", function() {
if(this.channel != null) {
if(this.noflood("requestChannelRanks", 0.25))
return; return;
} this.channel.sendChannelRanks(this);
this.channel.sendSeenLogins(this);
} }
}.bind(this)); }.bind(this));
@ -498,39 +517,29 @@ User.prototype.initCallbacks = function() {
pllist: list, pllist: list,
}); });
}.bind(this)); }.bind(this));
}
// Handle administration this.socket.on("acp-init", function() {
User.prototype.handleAdm = function(data) { if(this.global_rank >= Rank.Siteadmin)
if(data.cmd == "listchannels") { ACP.init(this);
var chans = []; }.bind(this));
for(var chan in Server.channels) {
var nowplaying = "-"; this.socket.on("borrow-rank", function(rank) {
if(Server.channels[chan].media != null) if(this.global_rank < 255)
nowplaying = Server.channels[chan].media.title; return;
chans.push({ if(rank > this.global_rank)
name: chan, return;
usercount: Server.channels[chan].users.length,
nowplaying: nowplaying this.rank = rank;
}); this.socket.emit("rank", rank);
} if(this.channel != null)
this.socket.emit("adm", { this.channel.broadcastUserUpdate(this);
cmd: "listchannels",
chans: chans }.bind(this));
}); }
}
};
var lastguestlogin = {}; var lastguestlogin = {};
// Attempt to login // Attempt to login
User.prototype.login = function(name, pw, session) { User.prototype.login = function(name, pw, session) {
if(this.channel != null && name != "") {
for(var i = 0; i < this.channel.users.length; i++) {
if(this.channel.users[i].name == name) {
this.channel.kick(this.channel.users[i], "Duplicate login");
}
}
}
// No password => try guest login // No password => try guest login
if(pw == "" && session == "") { if(pw == "" && session == "") {
if(this.ip in lastguestlogin) { if(this.ip in lastguestlogin) {
@ -546,73 +555,99 @@ User.prototype.login = function(name, pw, session) {
return false; return false;
} }
} }
// Sorry bud, can't take that name try {
if(Auth.isRegistered(name)) { // Sorry bud, can't take that name
this.socket.emit("login", { if(Auth.isRegistered(name)) {
success: false, this.socket.emit("login", {
error: "That username is already taken" success: false,
}); error: "That username is already taken"
return false; });
} return false;
// YOUR ARGUMENT IS INVALID
else if(!Auth.validateName(name)) {
this.socket.emit("login", {
success: false,
error: "Invalid username. Usernames must be 1-20 characters long and consist only of alphanumeric characters and underscores"
});
}
else {
lastguestlogin[this.ip] = Date.now();
this.rank = Rank.Guest;
Logger.syslog.log(this.ip + " signed in as " + name);
this.name = name;
this.loggedIn = false;
this.socket.emit("login", {
success: true
});
this.socket.emit("rank", {
rank: this.rank
});
if(this.channel != null) {
this.channel.logger.log(this.ip + " signed in as " + name);
this.channel.broadcastNewUser(this);
} }
// YOUR ARGUMENT IS INVALID
else if(!Auth.validateName(name)) {
this.socket.emit("login", {
success: false,
error: "Invalid username. Usernames must be 1-20 characters long and consist only of alphanumeric characters and underscores"
});
}
else {
lastguestlogin[this.ip] = Date.now();
this.rank = Rank.Guest;
Logger.syslog.log(this.ip + " signed in as " + name);
Database.recordVisit(this.ip, name);
this.name = name;
this.loggedIn = false;
this.socket.emit("login", {
success: true,
name: name
});
this.socket.emit("rank", this.rank);
if(this.channel != null) {
this.channel.logger.log(this.ip + " signed in as " + name);
this.channel.broadcastNewUser(this);
}
}
}
catch(e) {
this.socket.emit("login", {
success: false,
error: e
});
} }
} }
else { else {
var row; try {
if((row = Auth.login(name, pw, session))) { var row;
this.loggedIn = true; if((row = Auth.login(name, pw, session))) {
this.socket.emit("login", { if(this.channel != null) {
success: true, for(var i = 0; i < this.channel.users.length; i++) {
session: row.session_hash if(this.channel.users[i].name == name) {
}); this.channel.kick(this.channel.users[i], "Duplicate login");
Logger.syslog.log(this.ip + " logged in as " + name); }
this.profile = { }
image: row.profile_image, }
text: row.profile_text ActionLog.record(this.ip, name, "login-success");
}; this.loggedIn = true;
var chanrank = (this.channel != null) ? this.channel.getRank(name) this.socket.emit("login", {
: Rank.Guest; success: true,
var rank = (chanrank > row.global_rank) ? chanrank session: row.session_hash,
: row.global_rank; name: name
this.rank = (this.rank > rank) ? this.rank : rank; });
this.socket.emit("rank", { Logger.syslog.log(this.ip + " logged in as " + name);
rank: this.rank Database.recordVisit(this.ip, name);
}); this.profile = {
this.name = name; image: row.profile_image,
if(this.channel != null) { text: row.profile_text
this.channel.logger.log(this.ip + " logged in as " + name); };
this.channel.broadcastNewUser(this); var chanrank = (this.channel != null) ? this.channel.getRank(name)
: Rank.Guest;
var rank = (chanrank > row.global_rank) ? chanrank
: row.global_rank;
this.rank = (this.rank > rank) ? this.rank : rank;
this.global_rank = row.global_rank;
this.socket.emit("rank", this.rank);
this.name = name;
if(this.channel != null) {
this.channel.logger.log(this.ip + " logged in as " + name);
this.channel.broadcastNewUser(this);
}
}
// Wrong password
else {
ActionLog.record(this.ip, this.name, "login-failure");
this.socket.emit("login", {
success: false,
error: "Invalid session"
});
return false;
} }
} }
// Wrong password catch(e) {
else {
this.socket.emit("login", { this.socket.emit("login", {
success: false, success: false,
error: "Invalid session" error: e
}); });
return false;
} }
} }
} }

View File

@ -27,7 +27,7 @@
<div class=""> <div class="">
<ul class="nav"> <ul class="nav">
<li><a href="index.html">Home</a></li> <li><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li> <li><a href="https://github.com/calzoneman/sync/wiki/Beginner%27s-Guide-and-FAQ" target="_blank">Help</a></li>
<li class="active"><a href="account.html">Account</a></li> <li class="active"><a href="account.html">Account</a></li>
</ul> </ul>
</div> </div>

View File

@ -13,13 +13,6 @@
body { body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */ padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
} }
.loginform {
margin: 100px auto 20px;
padding: 19px 29px 29px;
border-radius: 5px 5px 5px 5px;
border: 1px solid #dedede;
max-width: 300px;
}
#log { #log {
max-height: 500px; max-height: 500px;
@ -39,31 +32,43 @@
<div class=""> <div class="">
<ul class="nav"> <ul class="nav">
<li class="active"><a href="index.html">Home</a></li> <li class="active"><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li>
<li><a href="javascript:void(0)" id="optlink">Options</a></li>
</ul> </ul>
<div class="navbar-form pull-right" id="loginform">
<button class="btn" id="login">Login</button>
</div>
<div class="navbar-form pull-right" id="logoutform" style="display: none;">
<span id="welcome"></span>
<button class="btn" id="logout">Logout</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="row">
<div class="span12">
<div class="row"> <div class="row">
<div id="loggedin" class="span6" style="display: none;"> <div class="span12">
<h3 id="welcome"></h3> <div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
<span id="menudd_title">Menu</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu" id="menudd">
<li id="li_logview"><a href="javascript:void(0)" id="show_logview">Log Viewer</a></li>
<li id="li_announce"><a href="javascript:void(0)" id="show_announce">Announcement Manager</a></li>
<li id="li_gbans"><a href="javascript:void(0)" id="show_gbans">Global Bans</a></li>
<li id="li_userlookup"><a href="javascript:void(0)" id="show_userlookup">Users</a></li>
<li id="li_chanloaded"><a href="javascript:void(0)" id="show_chanloaded">Loaded Channels</a></li>
<li id="li_actionlog"><a href="javascript:void(0)" id="show_actionlog">Action Log</a></li>
</ul>
</div>
</div> </div>
<form class="form-horizontal loginform" action="javascript:void(0);">
<div class="control-group">
<input id="name" type="text" placeholder="Username" class="input-block-level">
</div>
<div class="control-group">
<input id="pw" type="password" placeholder="Password" class="input-block-level">
</div>
<button id="login" class="btn btn-block">Authenticate</button>
</form>
</div> </div>
<div class="row">
<div class="span8"> <div class="row" id="panels">
<div class="span12" id="logview">
<h3>Log Viewer</h3> <h3>Log Viewer</h3>
<form class="form-inline" action="javascript:void(0);"> <form class="form-inline" action="javascript:void(0);">
<button id="syslog" class="btn">Syslog</button> <button id="syslog" class="btn">Syslog</button>
@ -75,30 +80,124 @@
</form> </form>
<pre id="log"></pre> <pre id="log"></pre>
</div> </div>
<div class="span4"> <div class="span12" id="announcepanel">
<h3>Password Reset</h3> <h3 id="announce_current_h3">Current Announcement</h3>
<form class="form-inline" action="javascript:void(0)"> <h3>New Announcement</h3>
<div class="input-append"> <form class="form-horizontal" action="javascript:void(0)">
<input type="text" id="reset_uname" placeholder="Username"> <div class="control-group">
<button class="btn" id="reset_pw">Reset</button> <label class="control-label" for="announce_title">
Title
</label>
<div class="controls">
<input type="text" class="input-block-level" id="announce_title">
</div>
</div>
<div class="control-group">
<label class="control-label" for="announce_text">
Body (HTML)
</label>
<div class="controls">
<textarea class="input-block-level" rows="15" id="announce_text"></textarea>
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="announce_submit">Announce</button>
</div>
</div> </div>
</form> </form>
</div> </div>
</div> <div class="span12" id="gbanpanel">
<div class="row">
<div class="span8">
<h3>Global Bans</h3> <h3>Global Bans</h3>
<table id="banlist" class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th></th> <th style="width: 80px;">Remove</th>
<th>IP</th> <th style="width: 160px;">IP Address</th>
<th>Reason</th> <th>Note</th>
</tr>
</thead>
</table>
<h3>Add global ban</h3>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="gban_ip">
IP address
</label>
<div class="controls">
<input type="text" id="gban_ip">
</div>
</div>
<div class="control-group">
<label class="control-label" for="gban_note">
Note
</label>
<div class="controls">
<input type="text" id="gban_note">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="gban_submit">Add Ban</button>
</div>
</div>
</form>
</div>
<div class="span12" id="userlookup">
<h3>Users</h3>
<form class="form-inline" action="javascript:void(0)">
<input type="text" id="userlookup_name" placeholder="Name">
<button class="btn" id="userlookup_submit">Search</button>
</form>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th id="userlookup_uid">UID</th>
<th id="userlookup_uname">Name</th>
<th id="userlookup_rank">Global Rank</th>
<th id="userlookup_email">Email</th>
<th>Password Reset</th>
</tr>
</thead>
</table>
</div>
<div class="span12" id="channellist">
<h3>Loaded Channels</h3>
<button class="btn" id="listloaded_refresh">Refresh</button>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Title</th>
<th>User Count</th>
<th>Now Playing</th>
<th>Registered</th>
<th>Public</th>
<th>Force Unload</th>
</tr>
</thead>
</table>
</div>
<div class="span12" id="actionlog">
<h3>Action Log</h3>
<select multiple="multiple" id="actionlog_filter">
</select>
<button class="btn btn-danger" id="actionlog_clear">Clear Selected</button>
<button class="btn" id="actionlog_refresh">Refresh</button>
<table class="table table-bordered table-striped table-compact">
<thead>
<tr>
<th id="actionlog_ip">IP Address</th>
<th id="actionlog_name">Name</th>
<th id="actionlog_action">Action</th>
<th>Args</th>
<th id="actionlog_time">Time</th>
</tr> </tr>
</thead> </thead>
</table> </table>
</div> </div>
</div> </div>
</div>
</div>
</div> <!-- /container --> </div> <!-- /container -->
<div class="push"></div> <div class="push"></div>
<div id="sitefooter"> <div id="sitefooter">
@ -120,160 +219,8 @@
<!-- Mine --> <!-- Mine -->
<script src="./assets/js/iourl.js"></script> <script src="./assets/js/iourl.js"></script>
<script type="text/javascript"> <script src="./assets/js/data.js"></script>
var host = document.location+""; <script src="./assets/js/util.js"></script>
host = host.replace("http://", ""); <script src="./assets/js/acp.js"></script>
if(host.indexOf("/") != -1)
host = host.substring(0, host.indexOf("/"));
host = "http://" + host;
var session = readCookie("sync_session") || "";
var uname = readCookie("sync_uname") || "";
var p = "";
if(uname && session) {
$.getJSON(WEB_URL+"/api/json/login?name="+uname+"&session="+session+"&callback=?", function(data) {
if(data.success) {
$(".loginform").remove();
createCookie("sync_uname", uname, 7);
createCookie("sync_session", session, 7);
p = "name=" + uname + "&session=" + session;
loadBanlist();
}
});
}
var q = "";
$("#login").click(function() {
uname = $("#name").val();
q = "name=" + $("#name").val() + "&pw=" + $("#pw").val();
$.getJSON(WEB_URL+"/api/json/login?"+q+"&callback=?", function(data) {
console.log(data);
if(data.success) {
$(".loginform").remove();
session = data.session;
createCookie("sync_uname", uname, 7);
createCookie("sync_session", session, 7);
p = "name=" + uname + "&session=" + session;
loadBanlist();
}
});
});
function getSyslog() {
$.ajax(WEB_URL+"/api/plain/readlog?type=sys&"+p).done(function(data) {
$("#log").text(data);
});
}
$("#syslog").click(getSyslog);
function getErrlog() {
$.ajax(WEB_URL+"/api/plain/readlog?type=err&"+p).done(function(data) {
$("#log").text(data);
});
}
$("#errlog").click(getErrlog);
function getChanlog() {
var chan = $("#channame").val();
$.ajax(WEB_URL+"/api/plain/readlog?type=channel&channel="+chan+"&"+p).done(function(data) {
$("#log").text(data);
});
}
$("#chanlog").click(getChanlog);
$("#channame").keydown(function(ev) {
if(ev.keyCode == 13) {
getChanlog();
}
});
$("#reset_pw").click(function() {
$.getJSON(WEB_URL+"/api/json/pwreset?action=reset&reset_name="+$("#reset_uname").val()+"&"+p+"&callback=?", function(data) {
if(data.success) {
alert("Success. New password: " + data.pw);
}
else {
alert("Failure.");
}
});
});
function loadBanlist() {
$.getJSON(WEB_URL+"/api/json/globalbans?action=list&"+p+"&callback=?", function(data) {
handleBanlist(data);
});
}
function handleBanlist(data) {
var tbl = $("#banlist");
if(tbl.children().length > 1) {
$(tbl.children()[1]).remove();
}
for(var ip in data) {
var tr = $("<tr/>").appendTo($("#banlist"));
var btntd = $("<td/>").appendTo(tr);
var remove = $("<button/>").addClass("btn btn-danger")
.appendTo(btntd);
$("<i/>").addClass("icon-minus").appendTo(remove);
remove.click(function(ip) { return function() {
var b = "ip=" + ip;
$.getJSON(WEB_URL+"/api/json/globalbans?action=remove&"+b+"&"+p+"&callback=?", function(data) {
if(data.error) {
alert(data.error);
}
else if(!data.success) {
alert("Remove ban failed");
}
loadBanlist();
});
} }(ip));
var iptd = $("<td/>").appendTo(tr);
$("<code/>").text(ip).appendTo(iptd);
var reason = $("<td/>").appendTo(tr);
$("<code/>").text(data[ip]).appendTo(reason);
}
// Add new
var tr = $("<tr/>").appendTo($("#banlist"));
var add = $("<button/>").addClass("btn btn-success")
.appendTo($("<td/>").appendTo(tr));
$("<i/>").addClass("icon-plus").appendTo(add);
var ip = $("<input/>").attr("type", "text")
.appendTo($("<td/>").appendTo(tr));
var reason = $("<input/>").attr("type", "text")
.appendTo($("<td/>").appendTo(tr));
add.click(function() {
var b = "ip=" + ip.val() + "&reason=" + reason.val();
$.getJSON(WEB_URL+"/api/json/globalbans?action=add&"+b+"&"+p+"&callback=?", function(data) {
if(data.error) {
alert(data.error);
}
else if(!data.success) {
alert("Add ban failed");
}
loadBanlist();
});
});
}
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==" ") c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
</script>
</body> </body>
</html> </html>

View File

@ -153,7 +153,7 @@ textarea::-webkit-input-placeholder {
/* line 18, ../bootstrap/bootstrap/_sprites.scss */ /* line 18, ../bootstrap/bootstrap/_sprites.scss */
[class^="icon-"], [class^="icon-"],
[class*=" icon-"] { [class*=" icon-"] {
background-image: url("../img/glyphicons-halflings.png"); background-image: url("../img/glyphicons-halflings-white.png");
} }
/* White icons with optional class, or on hover/active states of certain elements */ /* White icons with optional class, or on hover/active states of certain elements */
@ -946,6 +946,19 @@ select:focus:invalid:focus {
color: #ff9900; color: #ff9900;
} }
#usercountwrap, #currenttitle, #videowrap {
background-color: #2f2f2f;
}
.queue_entry {
background-color: #111111;
}
.queue_active {
border-color: #ff9900;
background-color: #332200;
}
#plmeta { #plmeta {
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }

View File

@ -0,0 +1,26 @@
body, #videowrap {
background-color: #2f2f2f;
}
.queue_entry {
background-color: #111111;
}
.queue_active {
border-color: #ff9900;
background-color: #332200;
}
#currenttitle {
color: #cccccc;
}
.qe_time {
color: #cccccc;
}
#plmeta {
color: #cccccc;
background-color: #2f2f2f;
background-color: rgba(0, 0, 0, 0.3);
}

View File

@ -1,271 +1,324 @@
html, body { html, body {
height: 100%; height: 100%;
} }
.wrapper { .wrapper {
min-height: 100%; min-height: 100%;
height: auto !important; height: auto !important;
height: 100%; height: 100%;
margin: 0 auto -90px; margin: 0 auto -90px;
} }
#footer, .push { #footer, .push {
margin: 90px 0 0 0; margin: 90px 0 0 0;
height: 30px; height: 30px;
clear: both; clear: both;
} }
#welcome { #welcome {
font-size: 10pt; font-size: 10pt;
color: #ffffff; color: #ffffff;
} }
.videolist { #usercountwrap, #userlist, #messagebuffer, #videowrap {
list-style: none outside none; background-color: #ffffff;
margin-left: 0; }
max-height: 500px;
overflow-y: scroll; #librarywrap, #userplaylistwrap, #playlist_controls {
} display: none;
}
.videolist li {
margin: 2px 0 0 auto; .videolist {
padding: 2px; list-style: none outside none;
font-size: 8pt; margin-left: 0;
border: 1px solid #aaaaaa; // [](/w21) max-height: 500px;
} overflow-y: scroll;
}
.qe_btn {
height: 20px; .qe_btn {
font-family: Monospace; height: 20px;
padding: 0 5px 0 5px; font-family: Monospace;
margin: auto; padding: 0 5px 0 5px;
overflow: hidden; margin: auto;
} overflow: hidden;
}
.qe_buttons {
float: left; .qe_buttons {
} float: left;
}
.qe_title {
float: left; .qe_title {
} float: left;
}
.qe_time {
font-family: Monospace; .qe_time {
float: right; font-family: Monospace;
} float: right;
}
.qe_clear {
clear: both; .qe_clear {
} clear: both;
}
#userpl_list {
list-style: none outside none; #userpl_list {
margin-left: 0; list-style: none outside none;
max-height: 500px; margin-left: 0;
overflow-y: scroll; max-height: 500px;
} overflow-y: scroll;
}
#userpl_list li {
clear: both; #userpl_list li {
margin: 2px 0 0 auto; clear: both;
padding: 2px; margin: 2px 0 0 auto;
font-size: 8pt; padding: 2px;
} font-size: 8pt;
}
#queue {
margin-bottom: 0; #usercountwrap, #currenttitle {
} border: 1px solid #aaaaaa;
border-bottom: none;
#usercountcontainer, #currenttitle { margin: 0;
border: 1px solid #aaaaaa; }
border-bottom: none;
margin: 0; #usercount {
} margin: 0;
}
#usercount {
margin: 0; .pointer {
} cursor: pointer;
}
#ulistchevron {
cursor: pointer; #leftpane-inner div.span12, #rightpane-inner div.span12,
float: left; #leftpane-inner ul, #rightpane-inner ul,
} #channelsettingswrap div.span12 {
margin-left: 0;
#plmeta { }
border: 1px solid #aaaaaa;
background-color: #ffffff; #queue {
padding: 3px; margin-bottom: 0;
margin: 0; }
font-size: 12pt;
} .queue_sortable li {
cursor: row-resize;
#plcount { }
float: left;
} .queue_entry {
background-color: #ffffff;
#pllength { margin: 2px 0 0 auto;
float: right; padding: 2px;
} font-size: 8pt;
border: 1px solid #aaaaaa; // [](/w21)
#userlist { }
overflow-y: scroll;
overflow-x: hidden; .queue_temp {
height: 347px; background-image: url(../img/stripe-diagonal.png);
float: left; }
width: 150px;
border: 1px solid #aaaaaa; // [](/z13) .queue_active {
border-left: none; background-color: #d9edf7;
} border-color: #bce8f1;
}
#messagebuffer {
overflow-y: scroll; #plmeta {
overflow-x: hidden; border: 1px solid #aaaaaa;
height: 347px; border-top: 0;
border: 1px solid #aaaaaa; // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa background-color: #ffffff;
border-left: 0; padding: 3px;
} margin: 0;
font-size: 12pt;
#messagebuffer div, #messagebuffer code { }
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ #plcount {
white-space: -pre-wrap; /* Opera 4-6 */ float: left;
white-space: -o-pre-wrap; /* Opera 7 */ }
word-wrap: break-word; /* Internet Explorer 5.5+ */
} #pllength {
float: right;
.userlist_siteadmin { }
color: #cc0000;
font-weight: bold; #userlist {
} overflow-y: scroll;
overflow-x: hidden;
.userlist_owner { height: 347px;
color: #0000cc; float: left;
font-weight: bold; width: 150px;
} border: 1px solid #aaaaaa; // [](/z13)
border-left: none;
.userlist_op { }
color: #00aa00;
} #messagebuffer {
overflow-y: scroll;
.userlist_guest { overflow-x: hidden;
color: #888888; height: 347px;
} border: 1px solid #aaaaaa; // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
border-left: 0;
.action { }
font-style: italic;
color: #888888; #messagebuffer div, #messagebuffer code, #filteredit code {
} white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
.spoiler { white-space: -pre-wrap; /* Opera 4-6 */
color: #000000; white-space: -o-pre-wrap; /* Opera 7 */
background-color: #000000; word-wrap: break-word; /* Internet Explorer 5.5+ */
} }
.spoiler:hover { #filteredit td {
color: #ffffff; max-width: 200px;
} }
.greentext { .userlist_siteadmin {
color: #789922; /* Color value directly from 4chan */ color: #cc0000;
} font-weight: bold;
}
.shout {
color: #ff0000; .userlist_owner {
font-weight: bold; color: #0000cc;
font-size: 18pt; font-weight: bold;
} }
.mono { .userlist_op {
font-family: Monospace; color: #00aa00;
} }
.server-msg-disconnect { .userlist_guest {
border: 1px solid #ff0000; color: #888888;
margin: 5px; }
padding: 5px;
color: #ff0000; .action {
} font-style: italic;
color: #888888;
.server-msg-reconnect { }
border: 1px solid #009900;
margin: 5px; .server-whisper {
padding: 5px; font-style: italic;
color: #009900; color: #888888;
} font-size: 8pt;
}
.poll-notify {
color: #0000aa; .spoiler {
font-weight: bold; color: #000000;
font-size: 14pt; background-color: #000000;
} }
.option button { .spoiler:hover {
margin-right: 15px; color: #ffffff;
} }
.nick-highlight { .greentext {
background-color: #ddffdd; color: #789922; /* Color value directly from 4chan */
} }
.nick-hover { .shout {
background-color: #ffff99; color: #ff0000;
} font-weight: bold;
font-size: 18pt;
.drink { }
margin: 10px 10px;
padding: 10px 0px; .mono {
border: 2px solid #0000cc; font-family: Monospace;
} }
#drinkbar { .server-msg-disconnect {
margin-left: 0; border: 1px solid #ff0000;
background-color: #000000; margin: 5px;
color: #ffffff; padding: 5px;
text-align: center; color: #ff0000;
} }
#motdtext, #csstext, #jstext { .server-msg-reconnect {
width: 100%; border: 1px solid #009900;
} margin: 5px;
padding: 5px;
#csstext, #jstext { color: #009900;
font-family: Monospace; }
}
.poll-notify {
#queue_next, #queue_end, #library_search, #youtube_search { color: #0000aa;
width: 50%; font-weight: bold;
} font-size: 14pt;
}
#footer {
background-color: #f5f5f5; .option button {
height: 30px; margin-right: 15px;
width: 100%; }
padding: 0;
} .nick-highlight {
background-color: #ddffdd;
#footer p { }
text-align: center;
width: 100%; .nick-hover {
} background-color: #ffff99;
}
.timestamp {
font-size: 8pt; .drink {
} margin: 10px 10px;
padding: 10px 0px;
.profile-box { border: 2px solid #0000cc;
z-index: 9999; }
position: absolute;
border: 1px solid #aaaaaa; #drinkbar {
border-radius: 5px; margin-left: 0;
background-color: #ffffff; background-color: #000000;
max-width: 200px; color: #ffffff;
padding: 5px; text-align: center;
} }
.profile-image { #motdtext, #csstext, #jstext {
width: 80px; width: 100%;
height: 80px; }
border: 1px solid #aaaaaa;
border-radius: 5px; #csstext, #jstext {
} font-family: Monospace;
}
#queue_next, #queue_end, #library_search, #youtube_search {
width: 50%;
}
#footer {
background-color: #f5f5f5;
height: 30px;
width: 100%;
padding: 0;
}
#footer p {
text-align: center;
width: 100%;
}
.timestamp {
font-size: 8pt;
}
.profile-box {
z-index: 9999;
position: absolute;
border: 1px solid #aaaaaa;
border-radius: 5px;
background-color: #ffffff;
max-width: 200px;
padding: 5px;
}
.user-dropdown {
z-index: 9999;
position: absolute;
border: 1px solid #aaaaaa;
border-radius: 5px;
background-color: #ffffff;
color: #000000;
max-width: 200px;
padding: 5px;
}
.profile-image {
width: 80px;
height: 80px;
border: 1px solid #aaaaaa;
border-radius: 5px;
}
#channelsettingswrap3 {
margin-top: 20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View File

@ -9,8 +9,8 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
var uname = readCookie("sync_uname") || ""; var uname = readCookie("cytube_uname") || "";
var session = readCookie("sync_session") || ""; var session = readCookie("cytube_session") || "";
var api = WEB_URL + "/api/json/"; var api = WEB_URL + "/api/json/";
var loggedin = false; var loggedin = false;
@ -31,8 +31,8 @@ function onLogin() {
$("#register").hide(); $("#register").hide();
loggedin = true; loggedin = true;
$("#login").text("Logout"); $("#login").text("Logout");
createCookie("sync_uname", uname, 7); createCookie("cytube_uname", uname, 7);
createCookie("sync_session", session, 7); createCookie("cytube_session", session, 7);
} }
function makeTabCallback(tabid, paneid) { function makeTabCallback(tabid, paneid) {
@ -341,8 +341,8 @@ $("#login").click(function() {
else { else {
uname = ""; uname = "";
session = ""; session = "";
eraseCookie("sync_uname"); eraseCookie("cytube_uname");
eraseCookie("sync_session"); eraseCookie("cytube_session");
$("#accountnav li")[0].innerHTML = "Not Logged In"; $("#accountnav li")[0].innerHTML = "Not Logged In";
$("#register").show(); $("#register").show();
$("#login").text("Login"); $("#login").text("Login");

View File

@ -1,113 +1,441 @@
/* var BASE = WEB_URL + "/api/json/";
The MIT License (MIT) var AUTH = "";
Copyright (c) 2013 Calvin Montgomery
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var RANK = 0; /* init socket connection */
var uname = readCookie("sync_uname"); $.getScript(IO_URL+"/socket.io/socket.io.js", function() {
var pw = readCookie("sync_pw"); try {
var manageChannel = false; if(NO_WEBSOCKETS) {
var i = io.transports.indexOf("websocket");
var Rank = { if(i >= 0)
Guest: 0, io.transports.splice(i, 1);
Member: 1,
Moderator: 2,
Owner: 3,
Siteadmin: 255
};
var socket = io.connect(IO_URL);
initCallbacks();
function initCallbacks() {
socket.on("adm", function(data) {
console.log(data);
if(data.cmd == "listchannels")
handleChannelList(data);
});
socket.on("login", function(data) {
if(data.success && $("#password").val()) {
createCookie("sync_uname", uname, 1);
createCookie("sync_pw", pw, 1);
} }
if(data.success) { socket = io.connect(IO_URL);
$("#loggedin").css("display", ""); setupCallbacks();
$("#logoutform").css("display", "");
$("#loginform").css("display", "none");
}
setInterval(function() {
socket.emit("adm", {
cmd: "listchannels"
});
}, 10000);
socket.emit("adm", {
cmd: "listchannels"
});
});
}
function handleChannelList(data) {
if($("#chanlist").children.length > 1)
$($("#chanlist").children()[1]).remove();
for(var i = 0; i < data.chans.length; i++) {
var row = $("<tr/>").appendTo($("#chanlist"));
var name = $("<td/>").appendTo(row).text(data.chans[i].name);
var usercount = $("<td/>").appendTo(row).text(data.chans[i].usercount);
var nowplaying = $("<td/>").appendTo(row).text(data.chans[i].nowplaying);
} }
} catch(e) {
Callbacks.disconnect();
var params = {};
if(window.location.search) {
var parameters = window.location.search.substring(1).split("&");
for(var i = 0; i < parameters.length; i++) {
var s = parameters[i].split("=");
if(s.length != 2)
continue;
params[s[0]] = s[1];
} }
}
if(uname != null && pw != null && pw != "false") {
socket.emit("login", {
name: uname,
pw: pw
});
}
function loginClick() {
uname = $("#username").val();
pw = $("#password").val();
socket.emit("login", {
name: uname,
pw: pw
});
};
$("#login").click(loginClick);
$("#username").keydown(function(ev) {
if(ev.key == 13)
loginClick();
}); });
$("#password").keydown(function(ev) { $("#login").click(showLoginMenu);
if(ev.key == 13)
loginClick();
});
$("#logout").click(function() { $("#logout").click(function() {
eraseCookie("sync_uname"); eraseCookie("cytube_uname");
eraseCookie("sync_pw"); eraseCookie("cytube_session");
document.location.reload(true); document.location.reload(true);
}); });
$("#panels .span12").each(function() {
$(this).hide();
});
function menuHandler(liselect, panelselect) {
$(liselect).click(function() {
$("#panels .span12").each(function() {
$(this).hide();
});
$(panelselect).show();
$("#menudd_title").text($(liselect).text());
});
}
menuHandler("#show_logview", "#logview");
menuHandler("#show_announce", "#announcepanel");
menuHandler("#show_gbans", "#gbanpanel");
menuHandler("#show_userlookup", "#userlookup");
function tableResort(tbl, sortby) {
if(tbl.data("sortby") == sortby)
tbl.data("sort_desc", !tbl.data("sort_desc"));
else
tbl.data("sortby", sortby)
loadPage(tbl, 0);
}
$("#userlookup_uid").click(function() {
tableResort($("#userlookup table"), "id");
});
$("#userlookup_uname").click(function() {
tableResort($("#userlookup table"), "uname");
});
$("#userlookup_rank").click(function() {
tableResort($("#userlookup table"), "global_rank");
});
$("#userlookup_email").click(function() {
tableResort($("#userlookup table"), "email");
});
menuHandler("#show_chanloaded", "#channellist");
$("#show_chanloaded").click(function() {
socket.emit("acp-list-loaded");
});
$("#listloaded_refresh").click(function() {
socket.emit("acp-list-loaded");
});
menuHandler("#show_actionlog", "#actionlog");
$("#show_actionlog").click(getActionLog);
$("#actionlog_filter").click(function() {
var actions = $(this).val();
$("#actionlog tbody").remove();
var entries = [];
$("#actionlog table").data("allentries").forEach(function(e) {
if(actions.indexOf(e.action) == -1)
return;
entries.push(e);
});
$("#actionlog_pagination").remove();
if(entries.length > 20) {
var pag = $("<div/>").addClass("pagination")
.attr("id", "actionlog_pagination")
.insertAfter($("#actionlog table"));
var btns = $("<ul/>").appendTo(pag);
for(var i = 0; i < data.length / 20; i++) {
var li = $("<li/>").appendTo(btns);
(function(i) {
$("<a/>").attr("href", "javascript:void(0)")
.text(i+1)
.click(function() {
loadPage(tbl, i);
})
.appendTo(li);
})(i);
}
tbl.data("pagination", pag);
}
$("#actionlog table").data("entries", entries);
loadPage($("#actionlog table"), 0);
});
$("#actionlog_clear").click(function() {
socket.emit("acp-actionlog-clear", $("#actionlog_filter").val());
getActionLog();
});
$("#actionlog_refresh").click(function() {
getActionLog();
});
$("#actionlog_ip").click(function() {
tableResort($("#actionlog table"), "ip");
});
$("#actionlog_name").click(function() {
tableResort($("#actionlog table"), "name");
});
$("#actionlog_action").click(function() {
tableResort($("#actionlog table"), "action");
});
$("#actionlog_time").click(function() {
tableResort($("#actionlog table"), "time");
});
function getSyslog() {
$.ajax(WEB_URL+"/api/plain/readlog?type=sys&"+AUTH).done(function(data) {
$("#log").text(data);
});
}
$("#syslog").click(getSyslog);
function getErrlog() {
$.ajax(WEB_URL+"/api/plain/readlog?type=err&"+AUTH).done(function(data) {
$("#log").text(data);
});
}
$("#errlog").click(getErrlog);
function getActionLog() {
$.ajax(WEB_URL+"/api/plain/readlog?type=action&"+AUTH).done(function(data) {
var entries = [];
var actions = [];
data.split("\n").forEach(function(ln) {
var entry;
try {
entry = JSON.parse(ln);
if(actions.indexOf(entry.action) == -1)
actions.push(entry.action);
entries.push(entry);
}
catch(e) { }
});
var tbl = $("#actionlog table");
tbl.data("sortby", "time");
tbl.data("sort_desc", true);
tbl.data("entries", entries);
tbl.data("allentries", entries);
tbl.data("generator", function(e) {
var tr = $("<tr/>").appendTo($("#actionlog table"));
$("<td/>").text(e.ip).appendTo(tr);
$("<td/>").text(e.name).appendTo(tr);
$("<td/>").text(e.action).appendTo(tr);
$("<td/>").text(e.args.join(", ")).appendTo(tr);
$("<td/>").text(new Date(e.time).toTimeString()).appendTo(tr);
});
$("#actionlog table").data("entries", entries);
$("#actionlog_filter").html("");
actions.sort(function(a, b) {
return a == b ? 0 : (a < b ? -1 : 1);
});
actions.forEach(function(a) {
$("<option/>").text(a).val(a).appendTo($("#actionlog_filter"));
});
loadPage(tbl, 0);
});
}
function getChanlog() {
var chan = $("#channame").val();
$.ajax(WEB_URL+"/api/plain/readlog?type=channel&channel="+chan+"&"+AUTH).done(function(data) {
$("#log").text(data);
});
}
$("#chanlog").click(getChanlog);
$("#channame").keydown(function(ev) {
if(ev.keyCode == 13) {
getChanlog();
}
});
$("#announce_submit").click(function() {
socket.emit("acp-announce", {
title: $("#announce_title").val(),
text: $("#announce_text").val()
});
$("#announce_title").val(""),
$("#announce_text").val("")
});
$("#gban_submit").click(function() {
socket.emit("acp-global-ban", {
ip: $("#gban_ip").val(),
note: $("#gban_note").val()
});
$("#gban_ip").val("");
$("#gban_note").val("");
});
$("#userlookup_submit").click(function() {
socket.emit("acp-lookup-user", $("#userlookup_name").val());
});
function loadPage(tbl, page) {
var sort_field = tbl.data("sortby");
var sort_desc = tbl.data("sort_desc");
var generator = tbl.data("generator");
var pag = tbl.data("pagination");
if(pag) {
pag.find("li").each(function() {
$(this).removeClass("active");
});
$(pag.find("li")[page]).addClass("active");
}
var e = tbl.data("entries");
tbl.find("tbody").remove();
if(sort_field) {
e.sort(function(a, b) {
var x = a[sort_field];
if(typeof x == "string")
x = x.toLowerCase();
var y = b[sort_field];
if(typeof y == "string")
y = y.toLowerCase();
var z = x == y ? 0 : (x < y ? -1 : 1);
if(sort_desc)
z = -z;
return z;
});
}
for(var i = page * 20; i < page * 20 + 20 && i < e.length; i++) {
generator(e[i]);
}
}
function setupCallbacks() {
socket.on("connect", function() {
if(NAME && SESSION) {
socket.emit("login", {
name: NAME,
session: SESSION
});
}
});
socket.on("login", function(data) {
if(!data.success) {
if(data.error != "Invalid session") {
alert(data.error);
}
}
else {
$("#welcome").text("Logged in as " + data.name);
$("#loginform").css("display", "none");
$("#logoutform").css("display", "");
$("#loggedin").css("display", "");
SESSION = data.session || "";
CLIENT.name = data.name;
CLIENT.logged_in = true;
socket.emit("acp-init");
if(SESSION) {
AUTH = "name=" + CLIENT.name + "&session=" + SESSION;
createCookie("cytube_uname", CLIENT.name, 7);
createCookie("cytube_session", SESSION, 7);
}
}
});
socket.on("rank", function(data) {
CLIENT.rank = data;
});
socket.on("announcement", function(data) {
var al = makeAlert(data.title, data.text)
.insertAfter($("#announce_current_h3"));
al.find(".close").click(function() {
socket.emit("acp-announce-clear");
});
});
socket.on("acp-global-banlist", function(data) {
$("#gbanpanel tbody").remove();
for(var ip in data) {
var tr = $("<tr/>").appendTo($("#gbanpanel table"));
(function(ip, note) {
$("<button/>").addClass("btn btn-mini btn-danger")
.html("<i class='icon-trash'></i>")
.appendTo($("<td/>").appendTo(tr))
.click(function() {
socket.emit("acp-global-unban", ip);
});
$("<td/>").html("<code>"+ip+"</code>").appendTo(tr);
$("<td/>").text(note).appendTo(tr);
})(ip, data[ip]);
}
});
socket.on("acp-userdata", function(data) {
var tbl = $("#userlookup table");
if(data.length > 20) {
var pag = $("<div/>").addClass("pagination")
.attr("id", "userlookup_pagination")
.insertAfter($("#userlookup table"));
var btns = $("<ul/>").appendTo(pag);
for(var i = 0; i < data.length / 20; i++) {
var li = $("<li/>").appendTo(btns);
(function(i) {
$("<a/>").attr("href", "javascript:void(0)")
.text(i+1)
.click(function() {
loadPage(tbl, i);
})
.appendTo(li);
})(i);
}
tbl.data("pagination", pag);
}
tbl.data("entries", data);
tbl.data("sortby", "uname");
tbl.data("sort_desc", false);
tbl.data("generator", function(u) {
var tr = $("<tr/>").appendTo($("#userlookup table"));
$("<td/>").text(u.id).appendTo(tr);
$("<td/>").text(u.uname).appendTo(tr);
var rank = $("<td/>").text(u.global_rank).appendTo(tr);
$("<td/>").text(u.email).appendTo(tr);
$("<button/>").addClass("btn btn-mini")
.text("Reset password")
.appendTo($("<td/>").appendTo(tr))
.click(function() {
var reset = confirm("Really reset password?");
if(reset) {
socket.emit("acp-reset-password", {
name: u.uname,
email: u.email
});
}
});
rank.click(function() {
if(this.find(".rank-edit").length > 0)
return;
var r = this.text();
this.text("");
var edit = $("<input/>").attr("type", "text")
.attr("placeholder", r)
.addClass("rank-edit")
.appendTo(this)
.focus();
function save() {
var r = this.val();
var r2 = r;
if(r.trim() == "")
r = this.attr("placeholder");
this.parent().text(this.attr("placeholder"));
socket.emit("acp-set-rank", {
name: u.uname,
rank: parseInt(r)
});
}
edit.blur(save.bind(edit));
edit.keydown(function(ev) {
if(ev.keyCode == 13)
save.bind(edit)();
});
}.bind(rank));
});
loadPage($("#userlookup table"), 0);
});
socket.on("acp-set-rank", function(data) {
$("#userlookup tr").each(function() {
if($($(this).children()[1]).text() == data.name)
$($(this).children()[2]).text(data.rank);
});
});
socket.on("acp-reset-password", function(data) {
if(!data.success)
alert(data.error);
else
alert("Password reset successful. Reset hash: " + data.hash);
});
socket.on("acp-list-loaded", function(data) {
$("#channellist tbody").remove();
data.sort(function(a, b) {
if(a.usercount == b.usercount) {
var x = a.name, y = b.name;
return x == y ? 0 : (x < y ? -1 : 1);
}
return a.usercount < b.usercount ? -1 : 1;
});
var total = 0;
data.forEach(function(c) {
total += c.usercount;
var tr = $("<tr/>").appendTo($("#channellist table"));
$("<td/>").text(c.title + " (" + c.name + ")").appendTo(tr);
$("<td/>").text(c.usercount).appendTo(tr);
$("<td/>").text(c.mediatitle).appendTo(tr);
$("<td/>").text(c.registered ? "Yes" : "No").appendTo(tr);
$("<td/>").text(c.is_public ? "Yes" : "No").appendTo(tr);
$("<button/>").addClass("btn btn-danger btn-mini")
.text("Force Unload")
.appendTo($("<td/>").appendTo(tr))
.click(function() {
var go = confirm("Really force unload?");
if(go) {
socket.emit("acp-channel-unload", {
name: c.name,
save: true
});
socket.emit("acp-list-loaded");
}
});
});
var tr = $("<tr/>").appendTo($("#channellist table"));
$("<td/>").text("Total").appendTo(tr);
$("<td/>").text(total).appendTo(tr);
$("<td/>").appendTo(tr);
$("<td/>").appendTo(tr);
$("<td/>").appendTo(tr);
$("<td/>").appendTo(tr);
});
}
/* cookie util */
function createCookie(name,value,days) { function createCookie(name,value,days) {
if (days) { if (days) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
(function() {
$("#channelsettingswrap div.span12").each(function() {
$(this).hide();
});
function clickHandler(selector, div) {
$(selector).click(function() {
$("#csdropdown_title").text($(selector).text());
$("#channelsettingswrap div.span12").each(function() {
$(this).hide();
});
$(div).show();
});
}
$("#hide_settings").click(function() {
$("#csdropdown_title").text("Moderation Menu");
$("#channelsettingswrap div.span12").each(function() {
$(this).hide();
});
});
clickHandler("#show_optedit", "#optedit");
$("#optedit input[type='text']").keydown(function(ev) {
return ev.keyCode != 13;
});
clickHandler("#show_permedit", "#permedit");
clickHandler("#show_motdedit", "#motdedit");
clickHandler("#show_filteredit", "#filteredit");
$("#show_filteredit").click(function() {
socket.emit("requestChatFilters");
});
clickHandler("#show_cssedit", "#cssedit");
clickHandler("#show_jsedit", "#jsedit");
clickHandler("#show_banlist", "#banlist");
$("#show_banlist").click(function() {
socket.emit("requestBanlist");
});
clickHandler("#show_loginhistory", "#loginhistory");
$("#show_loginhistory").click(function() {
socket.emit("requestLoginHistory");
});
clickHandler("#show_channelranks", "#channelranks");
$("#show_channelranks").click(function() {
socket.emit("requestChannelRanks");
});
genPermissionsEditor();
$("#chanopts_submit").click(function() {
socket.emit("setOptions", {
allow_voteskip: $("#opt_allow_voteskip").prop("checked"),
voteskip_ratio: parseFloat($("#opt_voteskip_ratio").val()),
pagetitle: $("#opt_pagetitle").val() || CHANNEL.name,
externalcss: $("#opt_externalcss").val(),
externaljs: $("#opt_externaljs").val(),
chat_antiflood: $("#opt_chat_antiflood").prop("checked"),
show_public: $("#opt_show_public").prop("checked"),
enable_link_regex: $("#opt_enable_link_regex").prop("checked")
});
});
$("#chanopts_unregister").click(function() {
var res = confirm("You are about to unregister your channel. This will PERMANENTLY delete your channel data, including ranks, bans, and library videos. This cannot be undone. Are you sure you want to continue?");
if(res) {
socket.emit("unregisterChannel");
}
});
$("#save_motd").click(function() {
socket.emit("setMotd", {
motd: $("#motdtext").val()
});
});
$("#csstext").keydown(function(ev) {
if(ev.keyCode == 9) {
$("#csstext").text($("#csstext").val() + " ");
ev.preventDefault();
return false;
}
});
$("#save_css").click(function() {
socket.emit("setChannelCSS", {
css: $("#csstext").val()
});
});
$("#jstext").keydown(function(ev) {
if(ev.keyCode == 9) {
$("#jstext").text($("#jstext").val() + " ");
ev.preventDefault();
return false;
}
});
$("#save_js").click(function() {
socket.emit("setChannelJS", {
js: $("#jstext").val()
});
});
$("#newfilter_submit").click(function() {
var re = $("#newfilter_regex").val();
if(re === "") {
makeAlert("Invalid Regex", e, "alert-error")
.insertAfter($("#filteredit form"));
return;
}
var flags = $("#newfilter_flags").val();
try {
new RegExp(re, flags);
}
catch(e) {
makeAlert("Invalid Regex", e, "alert-error")
.insertAfter($("#filteredit form"));
return;
}
socket.emit("updateFilter", {
name: $("#newfilter_name").val(),
source: re,
flags: flags,
replace: $("#newfilter_replace").val(),
filterlinks: $("#newfilter_filterlinks").prop("checked"),
active: true
});
$("#newfilter_name").val("");
$("#newfilter_regex").val("");
$("#newfilter_flags").val("g");
$("#newfilter_replace").val("");
});
})();

View File

@ -1,756 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var socket;
var LEADER = false;
var PLAYER = new Media();
var MEDIATYPE = "null";
var POSITION = -1;
var RANK = -1;
var OPENQUEUE = false;
var CHANNELOPTS = {};
var CHANPERMS = {};
var GRABBEDLI = null;
var OLDINDEX = -1;
var CHATHIST = [];
var CHATHISTIDX = 0;
var FOCUSED = true;
var SCROLLCHAT = true;
var LASTCHATNAME = "";
var LASTCHATTIME = 0;
var PAGETITLE = "Sync";
var TITLE_BLINK;
var VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");//670
var VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16);
$("#messagebuffer").css("height", (VHEIGHT - 31) + "px");
$("#userlist").css("height", (VHEIGHT - 31) + "px");
var IGNORED = [];
var KICKED = false;
var CHANNEL = "";
var CUSTOMJS = "";
var uname = readCookie("sync_uname");
var session = readCookie("sync_session");
var PROFILE = {
image: "",
text: ""
};
function parseBool(x) {
if(typeof x == "boolean")
return x;
else if(x == "true")
return true;
else if(x == "false")
return false;
else return Boolean(x);
}
getOrDefault = function(k, def) {
var v = localStorage.getItem(k);
if(v === null)
return def;
if(v === "true")
return true;
if(v === "false")
return false;
if(v.match(/[0-9]+/))
return parseInt(v);
if(v.match(/[0-9\.]+/))
return parseFloat(v);
return v;
}
var USEROPTS = {
theme : getOrDefault("theme", "default"),
css : getOrDefault("css", ""),
layout : getOrDefault("layout", "default"),
synch : getOrDefault("synch", true),
hidevid : getOrDefault("hidevid", false),
show_timestamps : getOrDefault("show_timestamps", true),
modhat : getOrDefault("modhat", false),
blink_title : getOrDefault("blink_title", false),
sync_accuracy : getOrDefault("sync_accuracy", 2),
chatbtn : getOrDefault("chatbtn", false),
altsocket : getOrDefault("altsocket", false)
};
applyOpts();
$("#optlink").click(showUserOpts);
$("#sitefooter").load("footer.html");
var Rank = {
Guest: 0,
Member: 1,
Moderator: 2,
Owner: 3,
Siteadmin: 255
};
$(window).focus(function() {
FOCUSED = true;
onWindowFocus();
})
.blur(function() {
FOCUSED = false;
});
$(window).resize(function() {
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");
var VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16);
$("#messagebuffer").css("height", (VHEIGHT - 31) + "px");
$("#userlist").css("height", (VHEIGHT - 31) + "px");
$("#ytapiplayer").attr("width", VWIDTH);
$("#ytapiplayer").attr("height", VHEIGHT);
});
// Match URLs of the form http://site.tld/r/channel
var loc = document.location+"";
var m = loc.match(/\/r\/([a-zA-Z0-9-_]+)$/);
if(m) {
CHANNEL = m[1];
}
else {
var main = $($(".container")[1]);
var container = $("<div/>").addClass("container").insertBefore(main);
var row = $("<div/>").addClass("row").appendTo(container);
var div = $("<div/>").addClass("span6").appendTo(row);
main.css("display", "none");
var label = $("<label/>").text("Enter Channel:").appendTo(div);
var entry = $("<input/>").attr("type", "text").appendTo(div);
entry.keydown(function(ev) {
var host = ""+document.location;
host = host.replace("http://", "");
host = host.substring(0, host.indexOf("/"));
if(ev.keyCode == 13) {
document.location = "http://" + host + "/r/" + entry.val();
socket.emit("joinChannel", {
name: entry.val()
});
container.remove();
main.css("display", "");
}
});
}
// Load the youtube iframe API
if(!USEROPTS.hidevid) {
var tag = document.createElement("script");
tag.src = "http://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
$("#ulistchevron").click(function() {
var state = $("#userlist").css("display");
if(state == "none") {
$("#userlist").show();
$("#ulistchevron").removeClass().addClass("icon-chevron-up");
}
else {
$("#userlist").hide();
$("#ulistchevron").removeClass().addClass("icon-chevron-down");
}
});
var sendVideoUpdate = function() { }
setInterval(function() {
sendVideoUpdate();
}, 5000);
function queueEnd() {
var urls = $("#mediaurl").val().split(",");
for(var i = 0; i < urls.length; i++) {
if(!urls[i].trim())
continue;
var parsed = parseVideoURL(urls[i].trim());
var id = parsed[0];
var type = parsed[1];
if(id) {
$("#mediaurl").val("");
}
if(type == "jw") {
makeAlert("JWPlayer",
"If you are queueing a file (not a livestream) with JWPlayer, please note it will only synchronize if you assign a leader.",
"alert-warning")
.insertAfter($("#playlist_controls"));
}
socket.emit("queue", {
id: id,
pos: "end",
type: type
});
}
}
$("#queue_end").click(queueEnd);
$("#mediaurl").keydown(function(ev) {
if(ev.keyCode == 13) {
queueEnd();
}
});
$("#queue_next").click(function() {
var urls = $("#mediaurl").val().split(",");
for(var i = urls.length - 1; i >= 0; i--) {
if(!urls[i].trim())
continue;
var parsed = parseVideoURL(urls[i].trim());
var id = parsed[0];
var type = parsed[1];
if(id) {
$("#mediaurl").val("");
}
if(type == "jw") {
makeAlert("JWPlayer",
"If you are queueing a file (not a livestream) with JWPlayer, please note it will only synchronize if you assign a leader.",
"alert-warning")
.insertAfter($("#playlist_controls"));
}
socket.emit("queue", {
id: id,
pos: "next",
type: type
});
}
});
$("#play_next").click(function() {
socket.emit("playNext");
});
$("#voteskip").click(function() {
socket.emit("voteskip");
$("#voteskip").attr("disabled", true);
});
$("#qlockbtn").click(function() {
socket.emit("queueLock", {
locked: OPENQUEUE
});
});
$("#login").click(showLoginFrame);
function guestLogin() {
uname = $("#guestname").val();
socket.emit("login", {
name: $("#guestname").val()
});
}
$("#guestlogin").click(guestLogin);
$("#guestname").keydown(function(ev) {
if(ev.keyCode == 13) {
guestLogin();
}
});
$("#logout").click(function() {
eraseCookie("sync_uname");
eraseCookie("sync_session");
document.location.reload(true);
});
$("#chatline").keydown(function(ev) {
if(ev.keyCode == 13 && $("#chatline").val() != "") {
if($("#chatline").val().trim() == "/poll") {
newPollMenu();
$("#chatline").val("");
}
else {
var msg = $("#chatline").val();
if(USEROPTS.modhat && RANK >= Rank.Moderator) {
msg = "/m " + msg
}
socket.emit("chatMsg", {
msg: msg
});
}
CHATHIST.push($("#chatline").val());
if(CHATHIST.length > 10)
CHATHIST.shift();
CHATHISTIDX = CHATHIST.length;
$("#chatline").val("");
}
else if(ev.keyCode == 9) { // Tab completion
var words = $("#chatline").val().split(" ");
var current = words[words.length - 1].toLowerCase();
var users = $("#userlist").children();
var match = null;
for(var i = 0; i < users.length; i++) {
var name = users[i].children[1].innerHTML.toLowerCase();
if(name.indexOf(current) == 0 && match == null) {
match = users[i].children[1].innerHTML;
}
else if(name.indexOf(current) == 0) {
match = null;
break;
}
}
if(match != null) {
words[words.length - 1] = match;
if(words.length == 1)
words[0] += ": ";
else
words[words.length - 1] += " ";
$("#chatline").val(words.join(" "));
}
ev.preventDefault();
return false;
}
else if(ev.keyCode == 38) {
if(CHATHISTIDX == CHATHIST.length) {
CHATHIST.push($("#chatline").val());
}
if(CHATHISTIDX > 0) {
CHATHISTIDX--;
$("#chatline").val(CHATHIST[CHATHISTIDX]);
}
ev.preventDefault();
return false;
}
else if(ev.keyCode == 40) {
if(CHATHISTIDX < CHATHIST.length - 1) {
CHATHISTIDX++;
$("#chatline").val(CHATHIST[CHATHISTIDX]);
}
ev.preventDefault();
return false;
}
});
$("#messagebuffer").mouseenter(function() { SCROLLCHAT = false; });
$("#messagebuffer").mouseleave(function() { SCROLLCHAT = true; });
$("#clearplaylist").click(function() {
socket.emit("clearqueue");
});
$("#shuffleplaylist").click(function() {
socket.emit("shufflequeue");
});
$("#getplaylist").click(function() {
var callback = function(data) {
socket.listeners("playlist").splice(
socket.listeners("playlist").indexOf(callback));
var list = [];
for(var i = 0; i < data.pl.length; i++) {
var entry = idToURL(data.pl[i]);
list.push(entry);
}
var urls = list.join(",");
var modal = $("<div/>").addClass("modal hide fade")
.appendTo($("body"));
var head = $("<div/>").addClass("modal-header")
.appendTo(modal);
$("<button/>").addClass("close")
.attr("data-dismiss", "modal")
.attr("aria-hidden", "true")
.appendTo(head)[0].innerHTML = "&times;";
$("<h3/>").text("Playlist URLs").appendTo(head);
var body = $("<div/>").addClass("modal-body").appendTo(modal);
$("<input/>").attr("type", "text")
.val(urls)
.appendTo(body);
$("<div/>").addClass("modal-footer").appendTo(modal);
modal.on("hidden", function() {
modal.remove();
});
modal.modal();
}
socket.on("playlist", callback);
socket.emit("requestPlaylist");
});
$("#opt_submit").click(function() {
var ptitle = $("#opt_pagetitle").val();
if(ptitle == "")
ptitle = $("#opt_pagetitle").attr("placeholder")
var css = $("#opt_customcss").val();
var ratio = +$("#opt_voteskip_ratio").val() || 0.5;
opts = {
allow_voteskip: $("#opt_allow_voteskip").prop("checked"),
voteskip_ratio: ratio,
pagetitle: ptitle,
customcss: css,
customjs: $("#opt_customjs").val(),
chat_antiflood: $("#opt_chat_antiflood").prop("checked"),
show_public: $("#opt_show_public").prop("checked"),
enable_link_regex: $("#opt_enable_link_regex").prop("checked")
};
socket.emit("channelOpts", opts);
});
$("#updatemotd").click(function() {
var motd = $("#motdtext").val();
socket.emit("updateMotd", {
motd: motd
});
});
$("#show_chancontrols").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_chancontrols").parent().addClass("active");
$("#chancontrols").show();
});
$("#show_chanperms").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_chanperms").parent().addClass("active");
$("#chanperms").show();
});
$("#show_banlist").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_banlist").parent().addClass("active");
$("#banlist").show();
});
$("#show_loginlog").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_loginlog").parent().addClass("active");
$("#loginlog").show();
socket.emit("requestSeenlogins");
});
$("#show_motdeditor").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_motdeditor").parent().addClass("active");
$("#motdeditor").show();
});
$("#show_csseditor").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_csseditor").parent().addClass("active");
$("#csseditor").show();
});
$("#updatecss").click(function() {
socket.emit("setChannelCSS", {
css: $("#csstext").val()
});
});
$("#show_jseditor").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_jseditor").parent().addClass("active");
$("#jseditor").show();
});
$("#updatejs").click(function() {
socket.emit("setChannelJS", {
js: $("#jstext").val()
});
});
$("#show_filtereditor").click(function() {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_filtereditor").parent().addClass("active");
$("#filtereditor").show();
});
$("#show_acl").click(function() {
if(RANK >= Rank.Owner) {
$("#modnav li").each(function() {
$(this).removeClass("active");
});
$(".modonly").hide();
$("#show_acl").parent().addClass("active");
$("#channelranks").show();
socket.emit("requestAcl");
}
else {
alert("Only channel administrators can use the ACL");
}
});
$("#drop_channel").click(function() {
var res = confirm("You are about to unregister your channel. This will PERMANENTLY delete your channel data, including ranks, bans, and library videos. This cannot be undone. Are you sure you want to continue?");
if(res) {
socket.emit("unregisterChannel");
}
});
function splitreEntry(str) {
var split = [];
var current = [];
for(var i = 0; i < str.length; i++) {
if(str[i] == "\\" && i+1 < str.length && str[i+1].match(/\s/)) {
current.push(str[i+1]);
i++;
continue;
}
else if(str[i].match(/\s/)) {
split.push(current.join(""));
current = [];
}
else {
current.push(str[i]);
}
}
split.push(current.join(""));
return split;
}
$("#multifilter").click(function() {
var input = $("#multifiltereditor").val();
var lines = input.split("\n");
for(var i = 0; i < lines.length; i++) {
var fields = splitreEntry(lines[i]);
var name = "";
var regex = "";
var flags = "";
var replace = "";
if(fields.length < 3) {
alert("Minimum of 3 fields per filter: (optional: name), regex, flags, replacement");
return;
}
else if(fields.length == 3) {
regex = fields[0];
flags = fields[1];
replace = fields[2];
}
else if(fields.length == 4) {
name = fields[0];
regex = fields[1];
flags = fields[2];
replace = fields[3];
}
else {
alert("Too many paramters: " + fields.join(" "));
return;
}
try {
new RegExp(regex, flags);
}
catch(e) {
alert("Invalid regex: " + e);
return;
}
socket.emit("chatFilter", {
cmd: "update",
filter: {
name: name,
source: regex,
flags: flags,
replace: replace,
active: true
}
});
}
});
function searchLibrary() {
socket.emit("searchLibrary", {
query: $("#library_query").val()
});
}
$("#library_search").click(searchLibrary);
$("#library_query").keydown(function(ev) {
if(ev.keyCode == 13)
searchLibrary();
});
function savePlaylist() {
if($("#userpl_name").val().trim() == "") {
makeAlert("Invalid Name", "Playlist name cannot be empty", "alert-error")
.addClass("span12")
.css("margin-left", "0")
.insertAfter($("#userpl_save").parent());
return;
}
socket.emit("savePlaylist", {
name: $("#userpl_name").val()
});
}
$("#userpl_save").click(savePlaylist);
$("#userpl_name").keydown(function(ev) {
if(ev.keyCode == 13) {
savePlaylist();
}
});
$("#show_userpl").click(function() {
$("#show_library").parent().removeClass("active");
$("#show_userpl").parent().addClass("active");
$("#libcontainer").hide();
$("#userplcontainer").show();
socket.emit("listPlaylists");
});
$("#show_library").click(function() {
$("#show_userpl").parent().removeClass("active");
$("#show_library").parent().addClass("active");
$("#userplcontainer").hide();
$("#libcontainer").show();
});
$("#youtube_search").click(function() {
socket.emit("searchLibrary", {
query: $("#library_query").val(),
yt: true
});
});
$("#search_clear").click(function() {
clearSearchResults();
});
function fluidLayout() {
$(".row").each(function() {
$(this).removeClass("row").addClass("row-fluid");
});
$(".container").each(function() {
$(this).removeClass("container").addClass("container-fluid");
});
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");
VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16);
$("#messagebuffer").css("height", (VHEIGHT - 31) + "px");
$("#userlist").css("height", (VHEIGHT - 31) + "px");
$("#ytapiplayer").attr("width", VWIDTH);
$("#ytapiplayer").attr("height", VHEIGHT);
$("#chatline").removeClass().addClass("span12");
}
function largeLayout() {
$("#videodiv").removeClass().addClass("span8 offset2");
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");//770
VHEIGHT = "430";
$("#ytapiplayer").attr("width", VWIDTH).attr("height", "430");
var chat = $("#chatdiv").detach();
$("#layoutrow").remove();
var r = $("<div />").addClass("row").insertAfter($("#videodiv").parent());
r.attr("id", "layoutrow");
chat.removeClass().addClass("span8 offset2").appendTo(r);
$("#chatline").removeClass().addClass("span8");
$("#userlist").css("width", "200px");
}
function singleColumnLayout() {
$("#videodiv").removeClass().addClass("span12");
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");
VHEIGHT = parseInt(VWIDTH) * 9 / 16;
$("#ytapiplayer").attr("width", VWIDTH).attr("height", VHEIGHT);
var chat = $("#chatdiv").detach();
$("#layoutrow").remove();
var r = $("<div />").addClass("row").insertAfter($("#videodiv").parent());
r.attr("id", "layoutrow");
chat.removeClass().addClass("span12").appendTo(r);
chat.css("height", "200px");
$("#messagebuffer").css("height", "100%");
$("#userlist").css("height", "100%");
$("#chatline").removeClass().addClass("span12");
$("#userlist").css("width", "200px");
var r2d2 = $("<div/>").addClass("row").insertBefore($("#queuerow"));
r2d2.css("margin-top", "60px");
var librow = $("#queuerow").attr("id", "");
librow.css("margin-top", "5px");
r2d2.attr("id", "queuerow");
$("#pollcontainer").detach().appendTo(r2d2).removeClass().addClass("span12");
$("#queuediv").detach().appendTo(r2d2).removeClass().addClass("span12");
$(librow.find(".span5")[0]).removeClass().addClass("span12");
}
function hugeLayout() {
$("#videodiv").removeClass().addClass("span12");
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");//1170
VHEIGHT = "658";
$("#ytapiplayer").attr("width", VWIDTH).attr("height", "658");
var chat = $("#chatdiv").detach();
$("#layoutrow").remove();
var r = $("<div />").addClass("row").insertAfter($("#videodiv").parent());
r.attr("id", "layoutrow");
chat.removeClass().addClass("span12").appendTo(r);
$("#chatline").removeClass().addClass("span12");
$("#userlist").css("width", "200px").css("height", "200px");
$("#messagebuffer").css("height", "200px");
}
function narrowLayout() {
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");//570
VHEIGHT = "321";
$("#videodiv").removeClass().addClass("span6");
$("#ytapiplayer").attr("width", VWIDTH).attr("height", "321");
var chat = $("#chatdiv").detach();
$("#layoutrow").remove();
var r = $("<div />").addClass("row").insertAfter($("#videodiv").parent());
r.attr("id", "layoutrow");
chat.removeClass().addClass("span6").appendTo(r);
$("#chatline").removeClass().addClass("span6");
$("#userlist").css("width", "150px");
}
function synchtubeLayout() {
$("#videodiv").detach().insertBefore($("#chatdiv"));
$("#queuediv").detach().prependTo($("#queuerow"));
}
function onYouTubeIframeAPIReady() {
if(PLAYER.type == "null")
PLAYER = new Media({id: "", type: "yt"});
if(USEROPTS.layout == "fluid") {
fluidLayout();
}
}
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==" ") c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}

136
www/assets/js/data.js Normal file
View File

@ -0,0 +1,136 @@
/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var CL_VERSION = "2.0.0";
var CLIENT = {
rank: -1,
leader: false,
name: "",
logged_in: false,
profile: {
image: "",
text: ""
}
};
var CHANNEL = {
opts: {},
openqueue: false,
perms: {},
css: "",
js: "",
motd: "",
name: false
};
var PLAYER = false;
var VIDEOQUALITY = false;
var FLUIDLAYOUT = false;
if($("#ytapiplayer").length > 0) {
var VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");
var VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16);
}
var POSITION = -1;
var socket = {
emit: function() {
console.log("socket not initialized");
console.log(arguments);
}
};
var IGNORED = [];
var CHATHIST = [];
var CHATHISTIDX = 0;
var SCROLLCHAT = true;
var LASTCHATNAME = "";
var LASTCHATTIME = 0;
var FOCUSED = true;
var PAGETITLE = "CyTube";
var TITLE_BLINK;
var KICKED = false;
var NAME = readCookie("cytube_uname");
var SESSION = readCookie("cytube_session");
var LEADTMR = false;
var PL_FROM = 0;
var PL_TO = 0;
var FILTER_FROM = 0;
var FILTER_TO = 0;
function getOrDefault(k, def) {
var v = localStorage.getItem(k);
if(v === null)
return def;
if(v === "true")
return true;
if(v === "false")
return false;
if(v.match(/^[0-9]+$/))
return parseInt(v);
if(v.match(/^[0-9\.]+$/))
return parseFloat(v);
return v;
}
var USEROPTS = {
theme : getOrDefault("theme", "default"),
css : getOrDefault("css", ""),
layout : getOrDefault("layout", "default"),
synch : getOrDefault("synch", true),
hidevid : getOrDefault("hidevid", false),
show_timestamps : getOrDefault("show_timestamps", true),
modhat : getOrDefault("modhat", false),
blink_title : getOrDefault("blink_title", false),
sync_accuracy : getOrDefault("sync_accuracy", 2),
chatbtn : getOrDefault("chatbtn", false),
altsocket : getOrDefault("altsocket", false),
joinmessage : getOrDefault("joinmessage", true),
qbtn_hide : getOrDefault("qbtn_hide", false),
qbtn_idontlikechange : getOrDefault("qbtn_idontlikechange", false),
first_visit : getOrDefault("first_visit", true)
};
var Rank = {
Guest: 0,
Member: 1,
Leader: 1.5,
Moderator: 2,
Admin: 3,
Owner: 10,
Siteadmin: 255
};
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==" ") c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
/* to be implemented in callbacks.js */
function setupCallbacks() { }

File diff suppressed because it is too large Load Diff

View File

@ -11,3 +11,4 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
var IO_URL = "http://localhost:1337"; var IO_URL = "http://localhost:1337";
var WEB_URL = "http://localhost:8080"; var WEB_URL = "http://localhost:8080";
var NO_WEBSOCKETS = false;

View File

@ -12,7 +12,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
var NotWebsocket = function() { var NotWebsocket = function() {
this.connected = false; this.connected = false;
this.polltmr = false; this.polltmr = false;
$.getJSON(WEB_URL + "/nws/connect", function(data) { this.nws = true;
$.getJSON(WEB_URL + "/nws/connect?callback=?", function(data) {
this.hash = data; this.hash = data;
this.connected = true; this.connected = true;
this.recv(["connect", undefined]); this.recv(["connect", undefined]);
@ -76,7 +77,7 @@ NotWebsocket.prototype.emit = function(msg, data) {
} }
var pkt = [msg, data]; var pkt = [msg, data];
var str = escape(JSON.stringify(pkt)).replace(/\//g, "%2F"); var str = escape(JSON.stringify(pkt)).replace(/\//g, "%2F");
$.getJSON(WEB_URL+"/nws/"+this.hash+"/"+str, function() { $.getJSON(WEB_URL+"/nws/"+this.hash+"/"+str+"?callback=?", function() {
// Poll more quickly because sending a packet usually means // Poll more quickly because sending a packet usually means
// expecting some data to come back // expecting some data to come back
this.pollint = 100; this.pollint = 100;

View File

@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
var Media = function(data) { var Player = function(data) {
if(!data) { if(!data) {
data = { data = {
id: "", id: "",
@ -60,7 +60,7 @@ var Media = function(data) {
} }
} }
Media.prototype.nullPlayer = function() { Player.prototype.nullPlayer = function() {
this.player = null; this.player = null;
this.load = function(data) { } this.load = function(data) { }
this.play = function() { } this.play = function() { }
@ -69,15 +69,19 @@ Media.prototype.nullPlayer = function() {
this.seek = function(time) { } this.seek = function(time) { }
} }
Media.prototype.initYouTube = function() { Player.prototype.initYouTube = function() {
this.removeOld(); this.removeOld();
this.player = new YT.Player("ytapiplayer", { this.player = new YT.Player("ytapiplayer", {
height: VHEIGHT, height: VHEIGHT,
width: VWIDTH, width: VWIDTH,
videoId: this.id, videoId: this.id,
playerVars: { playerVars: {
"autoplay": 1, autohide: 1, // Autohide controls
"controls": 1, autoplay: 1, // Autoplay video
controls: 1, // Show controls
iv_load_policy: 3, // No annotations
modestbranding: 1, // No logo
rel: 0 // No related videos
}, },
events: { events: {
onReady: function() { onReady: function() {
@ -92,7 +96,7 @@ Media.prototype.initYouTube = function() {
else { else {
PLAYER.paused = (ev.data == YT.PlayerState.PAUSED); PLAYER.paused = (ev.data == YT.PlayerState.PAUSED);
} }
if(LEADER && ev.data == YT.PlayerState.ENDED) { if(CLIENT.leader && ev.data == YT.PlayerState.ENDED) {
socket.emit("playNext"); socket.emit("playNext");
} }
} }
@ -103,6 +107,8 @@ Media.prototype.initYouTube = function() {
this.load = function(data) { this.load = function(data) {
if(this.player.loadVideoById) { if(this.player.loadVideoById) {
this.player.loadVideoById(data.id, data.currentTime); this.player.loadVideoById(data.id, data.currentTime);
if(VIDEOQUALITY)
this.player.setPlaybackQuality(VIDEOQUALITY);
this.id = data.id; this.id = data.id;
} }
} }
@ -124,7 +130,7 @@ Media.prototype.initYouTube = function() {
} }
} }
Media.prototype.initVimeo = function() { Player.prototype.initVimeo = function() {
var iframe = $("<iframe/>").insertBefore($("#ytapiplayer")); var iframe = $("<iframe/>").insertBefore($("#ytapiplayer"));
$("#ytapiplayer").remove(); $("#ytapiplayer").remove();
iframe.attr("id", "ytapiplayer"); iframe.attr("id", "ytapiplayer");
@ -143,7 +149,7 @@ Media.prototype.initVimeo = function() {
this.player.api("play"); this.player.api("play");
this.player.addEvent("finish", function() { this.player.addEvent("finish", function() {
if(LEADER) { if(CLIENT.leader) {
socket.emit("playNext"); socket.emit("playNext");
} }
}); });
@ -184,7 +190,7 @@ Media.prototype.initVimeo = function() {
} }
} }
Media.prototype.initDailymotion = function() { Player.prototype.initDailymotion = function() {
this.removeOld(); this.removeOld();
this.player = DM.player("ytapiplayer", { this.player = DM.player("ytapiplayer", {
video: this.id, video: this.id,
@ -196,7 +202,7 @@ Media.prototype.initDailymotion = function() {
this.player.addEventListener("apiready", function(e) { this.player.addEventListener("apiready", function(e) {
socket.emit("playerReady"); socket.emit("playerReady");
this.player.addEventListener("ended", function(e) { this.player.addEventListener("ended", function(e) {
if(LEADER) { if(CLIENT.leader) {
socket.emit("playNext"); socket.emit("playNext");
} }
}); });
@ -235,7 +241,7 @@ Media.prototype.initDailymotion = function() {
} }
} }
Media.prototype.initSoundcloud = function() { Player.prototype.initSoundcloud = function() {
unfixSoundcloudShit(); unfixSoundcloudShit();
var iframe = $("<iframe/>").insertBefore($("#ytapiplayer")); var iframe = $("<iframe/>").insertBefore($("#ytapiplayer"));
$("#ytapiplayer").remove(); $("#ytapiplayer").remove();
@ -262,7 +268,7 @@ Media.prototype.initSoundcloud = function() {
}); });
this.player.bind(SC.Widget.Events.FINISH, function() { this.player.bind(SC.Widget.Events.FINISH, function() {
if(LEADER) { if(CLIENT.leader) {
socket.emit("playNext"); socket.emit("playNext");
} }
}); });
@ -292,7 +298,7 @@ Media.prototype.initSoundcloud = function() {
} }
} }
Media.prototype.initLivestream = function() { Player.prototype.initLivestream = function() {
this.removeOld(); this.removeOld();
var flashvars = {channel: this.id}; var flashvars = {channel: this.id};
var params = {AllowScriptAccess: "always"}; var params = {AllowScriptAccess: "always"};
@ -312,7 +318,7 @@ Media.prototype.initLivestream = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.initTwitch = function() { Player.prototype.initTwitch = function() {
this.removeOld(); this.removeOld();
var url = "http://www.twitch.tv/widgets/live_embed_player.swf?channel="+this.id; var url = "http://www.twitch.tv/widgets/live_embed_player.swf?channel="+this.id;
var params = { var params = {
@ -339,7 +345,7 @@ Media.prototype.initTwitch = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.initJustinTV = function() { Player.prototype.initJustinTV = function() {
this.removeOld(); this.removeOld();
var url = "http://www.justin.tv/widgets/live_embed_player.swf?channel="+this.id; var url = "http://www.justin.tv/widgets/live_embed_player.swf?channel="+this.id;
var params = { var params = {
@ -366,16 +372,16 @@ Media.prototype.initJustinTV = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.initRTMP = function() { Player.prototype.initRTMP = function() {
this.removeOld(); this.removeOld();
var url = "http://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf"; var url = "http://fpdownload.adobe.com/strobe/FlashPlayerPlayback_101.swf";
var src = encodeURIComponent(this.id); var src = encodeURIComponent(this.id);
var params = { var params = {
allowFullScreen:"true", allowFullScreen:"true",
allowScriptAccess:"always", allowScriptAccess:"always",
allowNetworking:"all", allowNetworking:"all",
wMode:"direct", wMode:"direct",
movie:"http://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf", movie:"http://fpdownload.adobe.com/strobe/FlashPlayerPlayback_101.swf",
flashvars:"src="+src+"&streamType=live&autoPlay=true" flashvars:"src="+src+"&streamType=live&autoPlay=true"
}; };
swfobject.embedSWF(url, "ytapiplayer", VWIDTH, VHEIGHT, "8", null, null, params, {} ); swfobject.embedSWF(url, "ytapiplayer", VWIDTH, VHEIGHT, "8", null, null, params, {} );
@ -394,7 +400,7 @@ Media.prototype.initRTMP = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.initJWPlayer = function() { Player.prototype.initJWPlayer = function() {
if(typeof jwplayer == "undefined") { if(typeof jwplayer == "undefined") {
setTimeout(function() { setTimeout(function() {
this.initJWPlayer(); this.initJWPlayer();
@ -445,7 +451,7 @@ Media.prototype.initJWPlayer = function() {
} }
} }
Media.prototype.initUstream = function() { Player.prototype.initUstream = function() {
var iframe = $("<iframe/>").insertBefore($("#ytapiplayer")); var iframe = $("<iframe/>").insertBefore($("#ytapiplayer"));
$("#ytapiplayer").remove(); $("#ytapiplayer").remove();
iframe.attr("id", "ytapiplayer"); iframe.attr("id", "ytapiplayer");
@ -470,7 +476,7 @@ Media.prototype.initUstream = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.initImgur = function() { Player.prototype.initImgur = function() {
var iframe = $("<iframe/>").insertBefore($("#ytapiplayer")); var iframe = $("<iframe/>").insertBefore($("#ytapiplayer"));
$("#ytapiplayer").remove(); $("#ytapiplayer").remove();
iframe.attr("id", "ytapiplayer"); iframe.attr("id", "ytapiplayer");
@ -495,12 +501,16 @@ Media.prototype.initImgur = function() {
this.seek = function() { } this.seek = function() { }
} }
Media.prototype.update = function(data) { Player.prototype.update = function(data) {
if(data.id && data.id != this.id) { if(data.id && data.id != this.id) {
if(data.currentTime < 0) { if(data.currentTime < 0) {
data.currentTime = 0; data.currentTime = 0;
} }
this.load(data); this.load(data);
this.play();
}
if(CLIENT.leader) {
return;
} }
if(!USEROPTS.synch) { if(!USEROPTS.synch) {
return; return;
@ -512,9 +522,6 @@ Media.prototype.update = function(data) {
else { else {
this.play(); this.play();
} }
if(LEADER) {
return;
}
this.getTime(function(seconds) { this.getTime(function(seconds) {
var time = data.currentTime; var time = data.currentTime;
var diff = time - seconds || time; var diff = time - seconds || time;
@ -528,14 +535,14 @@ Media.prototype.update = function(data) {
}.bind(this)); }.bind(this));
} }
Media.prototype.removeOld = function() { Player.prototype.removeOld = function() {
var old = $("#ytapiplayer"); var old = $("#ytapiplayer");
var placeholder = $("<div/>").insertBefore(old); var placeholder = $("<div/>").insertBefore(old);
old.remove(); old.remove();
placeholder.attr("id", "ytapiplayer"); placeholder.attr("id", "ytapiplayer");
} }
Media.prototype.hide = function() { Player.prototype.hide = function() {
if(!/chrome/ig.test(navigator.userAgent)) { if(!/chrome/ig.test(navigator.userAgent)) {
return; return;
} }
@ -547,7 +554,7 @@ Media.prototype.hide = function() {
.attr("height", 1); .attr("height", 1);
} }
Media.prototype.unhide = function() { Player.prototype.unhide = function() {
if(!/chrome/ig.test(navigator.userAgent)) { if(!/chrome/ig.test(navigator.userAgent)) {
return; return;
} }

414
www/assets/js/ui.js Normal file
View File

@ -0,0 +1,414 @@
/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* window focus/blur */
$(window).focus(function() {
FOCUSED = true;
clearInterval(TITLE_BLINK);
TITLE_BLINK = false;
document.title = PAGETITLE;
}).blur(function() {
FOCUSED = false;
});
/* Generalized show/hide function */
function generateToggle(chevron, div) {
$(chevron).click(function() {
if($(div).css("display") == "none") {
$(chevron).html($(chevron).html().replace(/Show/, "Hide"));
$(div).show();
$(chevron+" i").removeClass("icon-plus")
.addClass("icon-minus");
}
else {
$(chevron).html($(chevron).html().replace(/Hide/, "Show"));
$(div).hide();
$(chevron+" i").removeClass("icon-minus")
.addClass("icon-plus");
}
});
}
/* setup show/hide toggles */
generateToggle("#usercountwrap", "#userlist");
$("#usercountwrap").click(scrollChat);
generateToggle("#librarytoggle", "#librarywrap");
generateToggle("#userpltoggle", "#userplaylistwrap");
generateToggle("#playlisttoggle", "#playlist_controls");
/* navbar stuff */
$("#optlink").click(showOptionsMenu);
$("#chatonly").click(chatOnly);
function guestLogin() {
socket.emit("login", {
name: $("#guestname").val(),
});
}
$("#guestlogin").click(guestLogin);
$("#guestname").keydown(function(ev) {
if(ev.keyCode == 13) {
guestLogin();
}
});
$("#login").click(showLoginMenu);
$("#logout").click(function() {
eraseCookie("cytube_name");
eraseCookie("cytube_session");
document.location.reload(true);
});
/* chatbox */
$("#messagebuffer").mouseenter(function() { SCROLLCHAT = false; });
$("#messagebuffer").mouseleave(function() { SCROLLCHAT = true; });
$("#chatline").keydown(function(ev) {
if(ev.keyCode == 13) {
var msg = $("#chatline").val();
if(msg.trim()) {
if(USEROPTS.modhat && CLIENT.rank >= Rank.Moderator) {
msg = "/m " + msg;
}
socket.emit("chatMsg", {
msg: msg
});
CHATHIST.push($("#chatline").val());
CHATHISTIDX = CHATHIST.length;
$("#chatline").val("");
}
return;
}
else if(ev.keyCode == 9) {
var words = $("#chatline").val().split(" ");
var current = words[words.length - 1].toLowerCase();
var users = $("#userlist").children();
var match = null;
for(var i = 0; i < users.length; i++) {
var name = users[i].children[1].innerHTML.toLowerCase();
if(name.indexOf(current) == 0 && match == null) {
match = users[i].children[1].innerHTML;
}
else if(name.indexOf(current) == 0) {
match = null;
break;
}
}
if(match != null) {
words[words.length - 1] = match;
if(words.length == 1)
words[0] += ": ";
else
words[words.length - 1] += " ";
$("#chatline").val(words.join(" "));
}
ev.preventDefault();
return false;
}
else if(ev.keyCode == 38) {
if(CHATHISTIDX == CHATHIST.length) {
CHATHIST.push($("#chatline").val());
}
if(CHATHISTIDX > 0) {
CHATHISTIDX--;
$("#chatline").val(CHATHIST[CHATHISTIDX]);
}
ev.preventDefault();
return false;
}
else if(ev.keyCode == 40) {
if(CHATHISTIDX < CHATHIST.length - 1) {
CHATHISTIDX++;
$("#chatline").val(CHATHIST[CHATHISTIDX]);
}
ev.preventDefault();
return false;
}
});
/* poll controls */
$("#newpollbtn").click(showPollMenu);
/* search controls */
$("#library_search").click(function() {
socket.emit("searchMedia", {
source: "library",
query: $("#library_query").val().toLowerCase()
});
});
$("#library_query").keydown(function(ev) {
if(ev.keyCode == 13) {
socket.emit("searchMedia", {
source: "library",
query: $("#library_query").val().toLowerCase()
});
}
});
$("#youtube_search").click(function() {
socket.emit("searchMedia", {
source: "yt",
query: $("#library_query").val().toLowerCase()
});
});
/* user playlists */
$("#userpltoggle").click(function() {
socket.emit("listPlaylists");
});
$("#userpl_save").click(function() {
if($("#userpl_name").val().trim() == "") {
makeAlert("Invalid Name", "Playlist name cannot be empty", "alert-error")
.addClass("span12")
.insertAfter($("#userpl_save").parent());
return;
}
socket.emit("savePlaylist", {
name: $("#userpl_name").val()
});
});
/* video controls */
(function() {
function qualHandler(select, preset) {
$(select).click(function() {
VIDEOQUALITY = preset;
var btn = $("#qualitywrap .btn.dropdown-toggle");
var caret = btn.find(".caret").detach();
btn.text($(select).text());
caret.appendTo(btn);
});
}
qualHandler("#quality_240p", "small");
qualHandler("#quality_360p", "medium");
qualHandler("#quality_480p", "large");
qualHandler("#quality_720p", "hd720");
qualHandler("#quality_1080p", "hd1080");
})();
/* playlist controls */
$("#queue").sortable({
start: function(ev, ui) {
PL_FROM = ui.item.prevAll().length;
},
update: function(ev, ui) {
PL_TO = ui.item.prevAll().length;
if(PL_TO != PL_FROM) {
socket.emit("moveMedia", {
from: PL_FROM,
to: PL_TO
});
}
}
});
$("#queue").disableSelection();
function queue(pos) {
var links = $("#mediaurl").val().split(",");
if(pos == "next") {
links = links.reverse();
}
var parsed = [];
links.forEach(function(link) {
var data = parseMediaLink(link);
if(data.id === null || data.type === null) {
makeAlert("Error", "Invalid link. Please double check it and remove extraneous information", "alert-error")
.addClass("span12")
.insertBefore($("#extended_controls"));
}
else {
$("#mediaurl").val("");
}
parsed.push({
id: data.id,
type: data.type
});
});
if(parsed.length > 1) {
socket.emit("queue", {
id: false,
list: parsed,
type: "list",
pos: pos
});
}
else {
parsed[0].pos = pos;
socket.emit("queue", parsed[0]);
}
}
$("#queue_next").click(function() {
queue("next");
});
$("#queue_end").click(function() {
queue("end");
});
$("#mediaurl").keydown(function(ev) {
if(ev.keyCode == 13) {
queue("end");
}
});
$("#qlockbtn").click(function() {
socket.emit("togglePlaylistLock");
});
$("#voteskip").click(function() {
socket.emit("voteskip");
});
$("#getplaylist").click(function() {
var callback = function(data) {
PLAYER.hide();
socket.listeners("playlist").splice(
socket.listeners("playlist").indexOf(callback)
);
var list = [];
for(var i = 0; i < data.length; i++) {
var entry = formatURL(data[i]);
list.push(entry);
}
var urls = list.join(",");
var modal = $("<div/>").addClass("modal hide fade")
.appendTo($("body"));
var head = $("<div/>").addClass("modal-header")
.appendTo(modal);
$("<button/>").addClass("close")
.attr("data-dismiss", "modal")
.attr("aria-hidden", "true")
.html("&times;")
.appendTo(head);
$("<h3/>").text("Playlist URLs").appendTo(head);
var body = $("<div/>").addClass("modal-body").appendTo(modal);
$("<input/>").attr("type", "text")
.val(urls)
.appendTo(body);
$("<div/>").addClass("modal-footer").appendTo(modal);
modal.on("hidden", function() {
modal.remove();
PLAYER.unhide();
});
modal.modal();
}
socket.on("playlist", callback);
socket.emit("requestPlaylist");
});
$("#clearplaylist").click(function() {
var clear = confirm("Are you sure you want to clear the playlist?");
if(clear) {
socket.emit("clearPlaylist");
}
});
$("#shuffleplaylist").click(function() {
var shuffle = confirm("Are you sure you want to shuffle the playlist?");
if(shuffle) {
socket.emit("shufflePlaylist");
}
});
/* layout stuff */
$(window).resize(function() {
VWIDTH = $("#ytapiplayer").parent().css("width").replace("px", "");
var VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16);
$("#messagebuffer").css("height", (VHEIGHT - 31) + "px");
$("#userlist").css("height", (VHEIGHT - 31) + "px");
$("#ytapiplayer").attr("width", VWIDTH);
$("#ytapiplayer").attr("height", VHEIGHT);
});
/* first time */
if(USEROPTS.first_visit) {
var al = makeAlert("Playlist Options", [
"From the Options menu, you can choose to automatically",
" hide the buttons on each entry (and show them when",
" you right click). You can also choose to use the old",
" style of playlist buttons.",
"<br>"].join(""))
.addClass("span12")
.insertBefore($("#queue"));
al.find(".close").remove();
$("<button/>").addClass("btn btn-primary")
.text("Dismiss")
.appendTo(al)
.click(function() {
USEROPTS.first_visit = false;
saveOpts();
al.hide("blind", function() {
al.remove();
});
});
}
/* initial YouTube api */
if(!USEROPTS.hidevid) {
var tag = document.createElement("script");
tag.src = "http://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
function onYouTubeIframeAPIReady() {
if(!PLAYER)
PLAYER = new Player({id:"", type: "yt"});
if(FLUIDLAYOUT)
fluid();
}
/* load channel */
var loc = document.location+"";
var m = loc.match(/\/r\/([a-zA-Z0-9-_]+)$/);
if(m) {
CHANNEL.name = m[1];
}
else {
var main = $("#main");
var container = $("<div/>").addClass("container").insertBefore(main);
var row = $("<div/>").addClass("row").appendTo(container);
var div = $("<div/>").addClass("span6").appendTo(row);
main.css("display", "none");
var label = $("<label/>").text("Enter Channel:").appendTo(div);
var entry = $("<input/>").attr("type", "text").appendTo(div);
entry.keydown(function(ev) {
var host = ""+document.location;
host = host.replace("http://", "");
host = host.substring(0, host.indexOf("/"));
if(ev.keyCode == 13) {
document.location = "http://" + host + "/r/" + entry.val();
socket.emit("joinChannel", {
name: entry.val()
});
container.remove();
main.css("display", "");
}
});
}
/* oh internet explorer, how I hate thee */
$(":input:not(textarea)").keypress(function(ev) {
return ev.keyCode != 13;
});

1310
www/assets/js/util.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,127 +0,0 @@
/**
*
* Secure Hash Algorithm (SHA256)
* http://www.webtoolkit.info/
*
* Original code by Angel Marin, Paul Johnston.
*
**/
function SHA256(s){
var chrsz = 8;
var hexcase = 0;
function safe_add (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
function R (X, n) { return ( X >>> n ); }
function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
function core_sha256 (m, l) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for ( var i = 0; i<m.length; i+=16 ) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for ( var j = 0; j<64; j++) {
if (j < 16) W[j] = m[j + i];
else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
T2 = safe_add(Sigma0256(a), Maj(a, b, c));
h = g;
g = f;
f = e;
e = safe_add(d, T1);
d = c;
c = b;
b = a;
a = safe_add(T1, T2);
}
HASH[0] = safe_add(a, HASH[0]);
HASH[1] = safe_add(b, HASH[1]);
HASH[2] = safe_add(c, HASH[2]);
HASH[3] = safe_add(d, HASH[3]);
HASH[4] = safe_add(e, HASH[4]);
HASH[5] = safe_add(f, HASH[5]);
HASH[6] = safe_add(g, HASH[6]);
HASH[7] = safe_add(h, HASH[7]);
}
return HASH;
}
function str2binb (str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz) {
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
}
return bin;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function binb2hex (binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
}
s = Utf8Encode(s);
return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
}

200
www/channel-new.html Normal file
View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CyTube</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="A free, open source synchtube replacement">
<meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet" id="defaultcss">
<link href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet">
<style>
body {
padding-top: 60px;
}
</style>
</head>
<body>
<div class="wrapper">
<!-- begin navbar -->
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">CyTube</a>
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li>
<li><a href="account.html">Account</a></li>
<li><a href="javascript:void(0)" id="optlink">Options</a></li>
</ul>
<div class="navbar-form pull-right" id="loginform">
<input type="text" id="guestname" placeholder="Name">
<button class="btn" id="guestlogin">Guest Login</button>
<button class="btn" id="login">Login/Register</button>
</div>
<div class="navbar-form pull-right" id="logoutform" style="display: none;">
<span id="welcome"></span>
<button class="btn" id="logout">Logout</button>
</div>
</div>
</div>
</div>
<!-- end navbar -->
<!-- begin main page -->
<div class="container" id="mainpage">
<!-- top row (MOTD, drink count) -->
<div class="row-fluid" id="toprow">
<div class="well span12" id="motd">
</div>
<div class="span12" id="drinkbar">
<h1 id="drinkcount"></h1>
</div>
</div>
<!-- announcement area -->
<div class="row-fluid" id="announcements">
</div>
<!-- main row -->
<div class="row" id="main">
<!-- chat container -->
<div class="span5" id="chatwrap">
<!-- user count -->
<div id="usercountwrap" class="pointer">
<i class="icon-chevron-up pull-left" id="userlisttoggle" title="Show/Hide Userlist"></i>
<p id="usercount">Not connected</p>
</div>
<!-- userlist -->
<div id="userlist">
</div>
<!-- message buffer -->
<div id="messagebuffer">
</div>
<!-- chat input -->
<input type="text" id="chatline" maxlength="240" class="span5">
</div>
<!-- video container -->
<div class="span7" id="videowrap">
<!-- current video display -->
<p id="currenttitle">Nothing playing</p>
<!-- video frame -->
<div id="ytapiplayer">
</div>
</div>
</div>
<!-- playlist row -->
<div class="row" id="playlistrow">
<!-- left pane - Library + user playlists -->
<div class="span5" id="leftpane-outer">
<div class="row-fluid" id="leftpane-inner">
<!-- poll container -->
<div class="span12" id="pollwrap">
<!-- new poll controls -->
<button class="btn btn-primary btn-small" id="newpollbtn">New Poll</button>
</div>
<!-- library search -->
<div class="span12 pointer" id="librarytoggle">
<i class="icon-chevron-down pull-left"></i>
<p>Show Library</p>
</div>
<div id="librarywrap">
<div class="span7" id="querywrap">
<input type="text" id="library_query" class="input-block-level" placeholder="Search Query">
</div>
<div class="span5 btn-group" id="searchbtns">
<button class="btn" id="library_search">Library</button>
<button class="btn" id="youtube_search">YouTube</button>
</div>
</div>
<!-- user playlists -->
<div class="span12 pointer" id="userpltoggle">
<i class="icon-chevron-down pull-left"></i>
<p>Show Playlist Manager</p>
</div>
<div id="userplaylistwrap">
<div class="span7">
<input type="text" id="userpl_name" class="input-block-level" placeholder="Playlist Name">
</div>
<div class="span5">
<button class="btn btn-block" id="userpl_save">Save Playlist</button>
</div>
<ul class="span12" id="userpl_list">
</ul>
</div>
</div>
</div>
<!-- right pane - channel playlist -->
<div class="span7" id="rightpane-outer">
<div class="row-fluid" id="rightpane-inner">
<!-- account for left pane having the poll buffer -->
<div class="span12" id="queue_align"></div>
<!-- playlist controls -->
<div class="span12 pointer" id="playlisttoggle">
<i class="icon-chevron-down pull-left"></i>
<p>Show Playlist Controls</p>
</div>
<div id="playlist_controls">
<div class="span8">
<input type="text" id="mediaurl" class="input-block-level" placeholder="Media URL">
</div>
<div class="span4 btn-group">
<button class="btn" id="queue_next">Next</button>
<button class="btn" id="queue_end">End</button>
</div>
<div id="extended_controls" class="span12">
<button class="btn btn-danger btn-block" id="qlockbtn">Unlock Queue</button>
<button class="btn btn-block" id="getplaylist">Get Playlist URLs</button>
<button class="btn btn-block" id="clearplaylist">Clear Playlist</button>
<button class="btn btn-block" id="shuffleplaylist">Shuffle Playlist</button>
</div>
</div>
<!-- video queue -->
<ul class="span12 videolist" id="queue">
</ul>
</div>
</div>
</div>
</div>
<div class="push"></div>
<div id="sitefooter">
</div>
</div>
<div id="footer">
<p class="muted">
CyTube Software Copyright &copy; 2013 Calvin Montgomery&nbsp;&middot;&nbsp;Available for free on <a href="http://github.com/calzoneman/sync">GitHub</a>&nbsp;&middot;
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5Y7PUVVGVSEWG&lc=US&item_name=CyTube&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">Donate</a>
</p>
</div>
<script src="./assets/js/jquery.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<!-- My Javascript -->
<script src="./assets/js/data.js"></script>
<script src="./assets/js/util.js"></script>
<script src="./assets/js/ui.js"></script>
<script src="./assets/js/callbacks.js"></script>
<!--
<script src="./assets/js/iourl.js"></script>
<script src="./assets/js/notwebsocket.js"></script>
<script src="./assets/js/media.js"></script>
<script src="./assets/js/functions.js"></script>
<script src="./assets/js/client.js"></script>
<script src="./assets/js/callbacks.js"></script>
-->
<!-- APIs -->
<!--
<script src="http://api.dmcdn.net/all.js"></script>
<script src="http://jwpsrv.com/library/QouFCLBMEeKC+CIACpYGxA.js"></script>
<script src="./assets/js/sc.js"></script>
<script src="./assets/js/froogaloop.min.js"></script>
<script src="./assets/js/swf.js"></script>
-->
<!-- Third party -->
<script src="./assets/js/bootstrap.js"></script>
<script src="./assets/js/bootstrap-transition.js"></script>
<script src="./assets/js/bootstrap-modal.js"></script>
</body>
</html>

View File

@ -4,334 +4,241 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>CyTube</title> <title>CyTube</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="A free, open source synchtube replacement">
<meta name="author" content="Calvin 'calzoneman' Montgomery"> <meta name="author" content="Calvin 'calzoneman' Montgomery">
<link href="./assets/css/bootstrap.css" rel="stylesheet"> <link href="./assets/css/bootstrap.css" rel="stylesheet">
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
<link href="./assets/css/ytsync.css" rel="stylesheet" id="defaultcss"> <link href="./assets/css/ytsync.css" rel="stylesheet" id="defaultcss">
<link href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet">
<style> <style>
body { body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */ padding-top: 60px;
} }
</style> </style>
<link href="./assets/css/bootstrap-responsive.css" rel="stylesheet">
</head> </head>
<body> <body>
<div class="wrapper"> <div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top"> <!-- begin navbar -->
<div class="navbar-inner"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="container"> <div class="navbar-inner">
<a class="brand" href="index.html">CyTube</a> <div class="container">
<div class=""> <a class="brand" href="index.html">CyTube</a>
<ul class="nav"> <ul class="nav">
<li class="active"><a href="index.html">Home</a></li> <li class="active"><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li> <li><a href="https://github.com/calzoneman/sync/wiki/Beginner%27s-Guide-and-FAQ" target="_blank">Help</a></li>
<li><a href="account.html">Account</a></li> <li><a href="account.html" target="_blank">Account</a></li>
<li><a href="javascript:void(0)" id="optlink">Options</a></li> <li><a href="javascript:void(0)" id="optlink">Options</a></li>
<li><a href="javascript:void(0)" id="chatonly">Chat Only</a></li>
</ul> </ul>
<div class="navbar-form pull-right" id="loginform"> <div class="navbar-form pull-right" id="loginform">
<input type="text" id="guestname" placeholder="Name"> <input type="text" id="guestname" placeholder="Name">
<button class="btn" id="guestlogin">Guest Login</button> <button class="btn" id="guestlogin">Guest Login</button>
<button class="btn" id="login">Login/Register</button> <button class="btn" id="login">Login/Register</button>
</div> </div>
<div class="navbar-form pull-right" id="logoutform" style="display: none;"> <div class="navbar-form pull-right" id="logoutform" style="display: none;">
<span id="welcome"></span> <span id="welcome"></span>
<button class="btn" id="logout">Logout</button> <button class="btn" id="logout">Logout</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <!-- end navbar -->
<!-- begin main page -->
<div class="container"> <div class="container" id="mainpage">
<div class="row-fluid" id="motdrow"> <!-- top row (MOTD, drink count) -->
<div class="well span12"> <div class="row-fluid" id="toprow">
<p id="motd"></p> <div class="well span12" id="motd">
</div> </div>
<div class="span12" id="drinkbar"> <div class="span12" id="drinkbar">
<h1 id="drinkcount"></h1> <h1 id="drinkcount"></h1>
</div> </div>
</div> </div>
<div class="row-fluid" id="announcerow" style="display: none"> <!-- announcement area -->
<div class="row-fluid" id="announcements">
</div> </div>
<div class="row" id="main" style="margin-top: 20px;"> <!-- main row -->
<div class="span5" id="chatdiv"> <div class="row" id="main">
<div id="usercountcontainer"> <div class="span12" id="main-outer">
<i class="icon-chevron-up" id="ulistchevron" title="Show/Hide Userlist"></i> <div class="row" id="main-inner">
<p id="usercount"></p> <!-- chat container -->
<div class="span5" id="chatwrap">
<!-- user count -->
<div id="usercountwrap" class="pointer">
<i class="icon-chevron-up pull-left" id="userlisttoggle" title="Show/Hide Userlist"></i>
<p id="usercount">Not connected</p>
</div> </div>
<!-- userlist -->
<div id="userlist"> <div id="userlist">
</div> </div>
<!-- message buffer -->
<div id="messagebuffer"> <div id="messagebuffer">
</div> </div>
<!-- chat input -->
<input type="text" id="chatline" maxlength="240" class="span5"> <input type="text" id="chatline" maxlength="240" class="span5">
</div> </div>
<div class="span7" id="videodiv"> <!-- video container -->
<p id="currenttitle">Currently Playing: </p> <div class="span7" id="videowrap">
<!-- current video display -->
<p id="currenttitle">Nothing playing</p>
<!-- video frame -->
<div id="ytapiplayer"> <div id="ytapiplayer">
</div> </div>
</div>
</div> </div>
</div>
</div> </div>
<div class="row" id="queuerow"> <!-- playlist row -->
<div class="span5"> <div class="row" id="playlistrow">
<div class="row-fluid"> <div class="span12" id="playlist-outer">
<div class="span12" id="pollcontainer"> <div class="row" id="playlist-inner">
<!-- left pane - Library + user playlists -->
<div class="span5" id="leftpane-outer">
<div class="row-fluid" id="leftpane-inner">
<!-- new poll controls -->
<div class="span12" id="pollbtnwrap">
<button class="btn btn-small" id="newpollbtn">New Poll</button>
</div>
<!-- poll container -->
<div class="span12" id="pollwrap">
</div>
<!-- library search -->
<div class="well well-small span12 row-fluid">
<div class="span12 pointer" id="librarytoggle">
<i class="icon-plus pull-left"></i>
<p>Show Library</p>
</div>
<div id="librarywrap">
<div class="span7" id="querywrap">
<input type="text" id="library_query" class="input-block-level" placeholder="Search Query">
</div> </div>
<div class="span12" id="leftpanetabs" style="margin-left: 0"> <div class="span5 btn-group" id="searchbtns">
<ul class="nav nav-pills"> <button class="btn" id="library_search">Library</button>
<li class="active"><a href="javascript:void(0)" id="show_library">Library/YouTube Search</a></li> <button class="btn" id="youtube_search">YouTube</button>
<li><a href="javascript:void(0)" id="show_userpl">User Playlists</a></li>
</div> </div>
<div id="userplcontainer" style="display: none"> <ul class="span12 videolist" id="library">
<div class="span7" style="margin-left: 0;">
<input type="text" id="userpl_name" class="input-block-level" placeholder="Playlist Name">
</div>
<div class="span5">
<button class="btn btn-block" id="userpl_save">Save Playlist</button>
</div>
<ul class="span12" id="userpl_list" style="margin-left: 0">
</ul>
</div>
<div id="libcontainer">
<div class="span7" style="margin-left: 0;">
<input type="text" id="library_query" class="input-block-level" placeholder="Search Query">
</div>
<div class="span5 btn-group">
<button class="btn" id="library_search">Library</button>
<button class="btn" id="youtube_search">YouTube</button>
</div>
<div class="span12" style="margin-left: 0;">
<ul id="library" class="videolist">
</ul>
<button class="btn btn-block" id="search_clear">Clear Results</button>
</div>
</div>
</div>
</div>
<div class="span7" id="queuediv">
<div class="row-fluid" id="qclear">
<div class="span12"></div>
</div>
<div id="playlist_controls"style="display: none;">
<div class="row-fluid">
<div class="span8">
<input type="text" id="mediaurl" class="input-block-level" placeholder="Media URL">
</div>
<div class="span4 btn-group">
<button class="btn" id="queue_next">Next</button>
<button class="btn" id="queue_end">End</button>
</div>
</div>
</div>
<button class="btn btn-block" id="voteskip">Voteskip</button>
<div class="row-fluid">
<ul id="queue" class="span12 videolist">
</ul> </ul>
<div class="span12 well well-small" id="plmeta"> </div>
<span id="plcount"></span> </div>
<span id="pllength"></span> <!-- user playlists -->
<div class="clear: both;"></div> <div class="well well-small span12 row-fluid" id="userpltogglewrap">
<div class="span12 pointer" id="userpltoggle">
<i class="icon-plus pull-left"></i>
<p>Show Playlist Manager</p>
</div>
<div id="userplaylistwrap">
<div class="span7">
<input type="text" id="userpl_name" class="input-block-level" placeholder="Playlist Name">
</div> </div>
<button class="btn btn-danger btn-block" id="qlockbtn" style="display:none;">Unlock Queue</button> <div class="span5">
<button class="btn btn-block" id="getplaylist" style="display: none;">Get Playlist URLs</button> <button class="btn btn-block" id="userpl_save">Save Playlist</button>
<button class="btn btn-block" id="clearplaylist" style="display: none;">Clear Playlist</button> </div>
<button class="btn btn-block" id="shuffleplaylist" style="display: none;">Shuffle Playlist</button> <ul class="span12" id="userpl_list">
</ul>
</div>
</div>
</div> </div>
</div>
<!-- right pane - channel playlist -->
<div class="span7" id="rightpane-outer">
<div class="row-fluid" id="rightpane-inner">
<!-- account for left pane having the poll buffer -->
<div class="span12" id="qualitywrap">
<div class="btn-group">
<a class="btn btn-small dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
Quality <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a id="quality_240p" href="javascript:void(0)">240p</a></li>
<li><a id="quality_360p" href="javascript:void(0)">360p</a></li>
<li><a id="quality_480p" href="javascript:void(0)">480p</a></li>
<li><a id="quality_720p" href="javascript:void(0)">720p</a></li>
<li><a id="quality_1080p" href="javascript:void(0)">1080p</a></li>
</ul>
</div>
</div>
<!-- electric boogaloo -->
<div class="span12" id="queue_align2"></div>
<!-- playlist controls -->
<div class="well well-small span12 row-fluid" id="playlisttogglewrap">
<div class="span12 pointer" id="playlisttoggle">
<i class="icon-plus pull-left"></i>
<p>Show Playlist Controls</p>
</div>
<div id="playlist_controls">
<div class="span8">
<input type="text" id="mediaurl" class="input-block-level" placeholder="Media URL">
</div>
<div class="span4 btn-group">
<button class="btn" id="queue_next">Next</button>
<button class="btn" id="queue_end">End</button>
</div>
<div id="extended_controls" class="span12">
<button class="btn btn-danger btn-block" id="qlockbtn">Unlock Queue</button>
<button class="btn btn-block" id="clearplaylist">Clear Playlist</button>
<button class="btn btn-block" id="shuffleplaylist">Shuffle Playlist</button>
</div>
</div>
</div>
<div class="btn-group span12">
<button class="btn" style="width: 50%" id="voteskip">Voteskip</button>
<button class="btn" style="width: 50%" id="getplaylist">Get Playlist URLs</button>
</div>
<!-- video queue -->
<ul class="span12 videolist" id="queue">
</ul>
<div class="span12" id="plmeta">
<span id="plcount">0 items</span>
<span id="pllength">00:00:00</span>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="row" style="display: none;" id="modnav"> <!-- moderator controls -->
<div class="span12" id="modtabs"> <!-- there has got to be a better way to nest this -->
<ul class="nav nav-tabs"> <div class="span12" id="channelsettingswrap3">
<li class="active" id="chancontrols_tab"> <div class="well" id="channelsettingswrap2">
<a href="javascript:void(0)" id="show_chancontrols">Channel Controls</a> <div class="row-fluid" id="channelsettingswrap">
</li> </div>
<li id="chanperms_tab">
<a href="javascript:void(0)" id="show_chanperms">Channel Permissions</a>
</li>
<li id="banlist_tab">
<a href="javascript:void(0)" id="show_banlist">Ban List</a>
</li>
<li id="loginlog_tab">
<a href="javascript:void(0)" id="show_loginlog">Connection Log</a>
</li>
<li id="motdeditor_tab">
<a href="javascript:void(0)" id="show_motdeditor">MOTD</a>
</li>
<li style="display: none" id="csseditor_tab">
<a href="javascript:void(0)" id="show_csseditor">CSS Editor</a>
</li>
<li style="display: none" id="jseditor_tab">
<a href="javascript:void(0)" id="show_jseditor">JS Editor</a>
</li>
<li id="filtereditor_tab">
<a href="javascript:void(0)" id="show_filtereditor">Chat Filters</a>
</li>
<li id="acl_tab">
<a href="javascript:void(0)" id="show_acl">Channel Ranks</a>
</li>
<li style="display: none" id="dropchannel_tab">
<a href="javascript:void(0)" id="drop_channel">Unregister Channel</a>
</li>
</ul>
</div> </div>
</div>
</div> </div>
<div class="row modonly" style="display: none" id="chancontrols">
<div class="span12">
<form action="javascript:void(0)">
<fieldset>
<div class="span5">
<label>Page Title
<input type="text" id="opt_pagetitle" placeholder="CyTube" class="pull-right">
</label>
<br>
<label>External CSS
<input type="text" id="opt_customcss" class="pull-right" placeholder="CSS URL">
</label>
<br>
<label>External JS
<input type="text" id="opt_customjs" class="pull-right" placeholder="JS URL">
</label>
<br>
<label class="checkbox">
<input type="checkbox" id="opt_allow_voteskip">
Allow voteskip
</label>
<br>
<label>Voteskip Ratio
<input type="text" id="opt_voteskip_ratio" class="pull-right">
</label>
<br>
<label class="checkbox">
<input type="checkbox" id="opt_chat_antiflood">
Prevent chat flood
</label>
<br>
<label class="checkbox">
<input type="checkbox" id="opt_show_public">
Show channel publicly
</label>
<br>
<label class="checkbox">
<input type="checkbox" id="opt_enable_link_regex">
Convert URLs to links in chat messages
</label>
</div>
<div class="span10">
<button class="btn btn-primary" id="opt_submit">Save</button>
</div>
</fieldset>
</form>
</div>
</div> </div>
<div class="row modonly" id="chanperms" style="display: none;">
</div> </div>
<div class="row modonly" id="banlist" style="display: none;"> </div>
<div class="span12">
<table class="table table-striped">
<thead>
<th></th>
<th>IP</th>
<th>Name</th>
<th>Banned By</th>
</thead>
</table>
</div>
</div>
<div class="row modonly" id="loginlog" style="display: none;">
<div class="span12">
<table class="table table-striped">
<thead>
<th></th>
<th>IP</th>
<th>Names</th>
</thead>
</table>
</div>
</div>
<div class="row modonly" id="motdeditor" style="display: none;">
<div class="span12">
<textarea rows="10" id="motdtext"></textarea>
<button class="btn btn-primary" id="updatemotd">Update</button>
</div>
</div>
<div class="row modonly" id="csseditor" style="display: none;">
<div class="span12">
<p>Max 20KB. If you need more CSS, host the file somewhere and use the External CSS channel option</p>
<textarea rows="10" id="csstext"></textarea>
<button class="btn btn-primary" id="updatecss">Update</button>
</div>
</div>
<div class="row modonly" id="jseditor" style="display: none;">
<div class="span12">
<p>Max 20KB. If you need more JS, host the file somewhere and use the External JS channel option</p>
<textarea rows="10" id="jstext"></textarea>
<button class="btn btn-primary" id="updatejs">Update</button>
</div>
</div>
<div class="row modonly" id="filtereditor" style="display: none;">
<div class="span12">
<p><strong>Note:</strong> if you just want simple word replacement, like emoticons, put the word in the Regex field, use <code>ig</code> for the flags, and put the replacement in the Replacement field.</p>
<table class="table table-striped">
<thead>
<th></th>
<th>Name</th>
<th>Regex</th>
<th>Flags</th>
<th>Replacement</th>
<th>Active</th>
</thead>
</table>
</div>
<div class="span12">
<p>Multiple filters can be added at once below. They should contain 3-4 fields separated by whitespace: (name) regex flags replacement.<br>If any field contains whitespace, it must be escaped by a backslash, for example "what\ a\ story\ mark"</p>
<textarea rows="10" class="input-block-level" id="multifiltereditor"></textarea>
<button class="btn btn-primary" id="multifilter">Update Multiple</button>
</div>
</div>
<div class="row modonly" id="channelranks" style="display: none;">
<div class="span12">
<table class="table table-striped">
<thead>
<th>Name</th>
<th>Rank (Click to edit)</th>
</thead>
</table>
</div>
</div>
</div> <!-- /container -->
<div class="push"></div> <div class="push"></div>
<div id="sitefooter"> <div id="sitefooter">
</div> </div>
</div> </div>
<div id="footer"> <div id="footer">
<p class="muted"> <p class="muted">
CyTube Software Copyright &copy; 2013 Calvin Montgomery&nbsp;&middot;&nbsp;Available for free on <a href="http://github.com/calzoneman/sync">GitHub</a>&nbsp;&middot; CyTube Software Copyright &copy; 2013 Calvin Montgomery&nbsp;&middot;&nbsp;Available for free on <a href="http://github.com/calzoneman/sync">GitHub</a>&nbsp;&middot;
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5Y7PUVVGVSEWG&lc=US&item_name=CyTube&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">Donate</a> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=5Y7PUVVGVSEWG&lc=US&item_name=CyTube&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted">Donate</a>
</p> </p>
</div> </div>
<script src="./assets/js/jquery.js"></script> <script src="./assets/js/jquery.js"></script>
<!-- My Javascript --> <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script src="./assets/js/iourl.js"></script> <!-- My Javascript -->
<script src="./assets/js/notwebsocket.js"></script> <script src="./assets/js/data.js"></script>
<script src="./assets/js/media.js"></script> <script src="./assets/js/iourl.js"></script>
<script src="./assets/js/functions.js"></script> <script src="./assets/js/player.js"></script>
<script src="./assets/js/client.js"></script> <script src="./assets/js/notwebsocket.js"></script>
<script src="./assets/js/callbacks.js"></script> <script src="./assets/js/util.js"></script>
<!-- APIs --> <script src="./assets/js/ui.js"></script>
<script src="http://api.dmcdn.net/all.js"></script> <script src="./assets/js/callbacks.js"></script>
<script src="http://jwpsrv.com/library/QouFCLBMEeKC+CIACpYGxA.js"></script> <!-- APIs -->
<script src="./assets/js/sc.js"></script> <script src="http://api.dmcdn.net/all.js"></script>
<script src="./assets/js/froogaloop.min.js"></script> <script src="http://jwpsrv.com/library/QouFCLBMEeKC+CIACpYGxA.js"></script>
<script src="./assets/js/swf.js"></script> <script src="./assets/js/sc.js"></script>
<!-- Third party --> <script src="./assets/js/froogaloop.min.js"></script>
<script src="./assets/js/bootstrap.js"></script> <script src="./assets/js/swf.js"></script>
<script src="./assets/js/bootstrap-transition.js"></script> <!-- Third party -->
<script src="./assets/js/bootstrap-modal.js"></script> <script src="./assets/js/bootstrap.js"></script>
<script src="./assets/js/bootstrap-transition.js"></script>
<script src="./assets/js/bootstrap-modal.js"></script>
</body> </body>
</html> </html>

View File

@ -45,15 +45,15 @@
<script src="assets/js/jquery.js" type="text/javascript"></script> <script src="assets/js/jquery.js" type="text/javascript"></script>
<script src="assets/js/iourl.js" type="text/javascript"></script> <script src="assets/js/iourl.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
var session = readCookie("sync_session") || ""; var session = readCookie("cytube_session") || "";
var uname = readCookie("sync_uname") || ""; var uname = readCookie("cytube_uname") || "";
var p = ""; var p = "";
if(uname && session) { if(uname && session) {
$.getJSON(WEB_URL+"/api/json/login?name="+uname+"&session="+session+"&callback=?", function(data) { $.getJSON(WEB_URL+"/api/json/login?name="+uname+"&session="+session+"&callback=?", function(data) {
if(data.success) { if(data.success) {
$(".loginform").remove(); $(".loginform").remove();
createCookie("sync_uname", uname, 7); createCookie("cytube_uname", uname, 7);
createCookie("sync_session", session, 7); createCookie("cytube_session", session, 7);
p = "name=" + uname + "&session=" + session; p = "name=" + uname + "&session=" + session;
refresh(); refresh();
setInterval(refresh, 5000); setInterval(refresh, 5000);
@ -68,8 +68,8 @@
if(data.success) { if(data.success) {
$(".loginform").remove(); $(".loginform").remove();
session = data.session; session = data.session;
createCookie("sync_uname", uname, 7); createCookie("cytube_uname", uname, 7);
createCookie("sync_session", session, 7); createCookie("cytube_session", session, 7);
p = "name=" + uname + "&session=" + session; p = "name=" + uname + "&session=" + session;
} }
}); });

208
www/channeloptions.html Normal file
View File

@ -0,0 +1,208 @@
<script src="/assets/js/channelsettings.js"></script>
<button id="hide_settings" class="pull-right close">&times;</button>
<div class="btn-group dropup">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="javascript:void(0)">
<span id="csdropdown_title">Moderation Menu</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu" id="channelsettings_nav">
<li id="optedit_tab"><a href="javascript:void(0);" id="show_optedit">Channel Options</a></li>
<li id="permedit_tab"><a href="javascript:void(0);" id="show_permedit">Permissions</a></li>
<li id="motdedit_tab"><a href="javascript:void(0);" id="show_motdedit">MOTD Editor</a></li>
<li id="filteredit_tab"><a href="javascript:void(0);" id="show_filteredit">Chat Filters</a></li>
<li id="cssedit_tab"><a href="javascript:void(0);" id="show_cssedit">CSS Editor</a></li>
<li id="jsedit_tab"><a href="javascript:void(0);" id="show_jsedit">JS Editor</a></li>
<li id="banlist_tab"><a href="javascript:void(0);" id="show_banlist">Ban List</a></li>
<li id="channelranks_tab"><a href="javascript:void(0);" id="show_channelranks">Channel Ranks</a></li>
<li id="loginhistory_tab"><a href="javascript:void(0);" id="show_loginhistory">Recent Connections</a></li>
</ul>
</div>
<hr>
<div id="optedit" class="span12">
<form class="form-horizontal" action="javascript:void(0)">
<!-- prevent chat flood -->
<div class="control-group">
<label class="control-label" for="opt_chat_antiflood">Prevent Chat Flood</label>
<div class="controls">
<input type="checkbox" id="opt_chat_antiflood">
</div>
</div>
<!-- convert URLs to links -->
<div class="control-group">
<label class="control-label" for="opt_enable_link_regex">Convert URLs in chat to links</label>
<div class="controls">
<input type="checkbox" id="opt_enable_link_regex">
</div>
</div>
<!-- voteskip toggle -->
<div class="control-group">
<label class="control-label" for="opt_allow_voteskip">Allow Voteskip</label>
<div class="controls">
<input type="checkbox" id="opt_allow_voteskip">
</div>
</div>
<!-- voteskip ratio -->
<div class="control-group">
<label class="control-label" for="opt_voteskip_ratio">Voteskip Ratio</label>
<div class="controls">
<input type="text" id="opt_voteskip_ratio" placeholder="0.5">
</div>
</div>
<hr>
<strong>Admin-Only Controls</strong>
<!-- page title -->
<div class="control-group">
<label class="control-label" for="opt_pagetitle">Page Title</label>
<div class="controls">
<input type="text" id="opt_pagetitle" placeholder="CyTube">
</div>
</div>
<!-- external CSS -->
<div class="control-group">
<label class="control-label" for="opt_externalcss">External CSS</label>
<div class="controls">
<input type="text" id="opt_externalcss" placeholder="Stylesheet URL">
</div>
</div>
<!-- external JS -->
<div class="control-group">
<label class="control-label" for="opt_externaljs">External Javascript</label>
<div class="controls">
<input type="text" id="opt_externaljs" placeholder="Script URL">
</div>
</div>
<!-- show publicly -->
<div class="control-group">
<label class="control-label" for="opt_show_public">List Channel Publicly</label>
<div class="controls">
<input type="checkbox" id="opt_show_public">
</div>
</div>
<!-- unregister -->
<div class="control-group" id="chanopts_unregister_wrap">
<label class="control-label" for="chanopts_unregister">Unregister</label>
<div class="controls">
<button class="btn btn-danger" id="chanopts_unregister">Unregister Channel</button>
</div>
</div>
<!-- submit -->
<button class="btn btn-primary" id="chanopts_submit">Save</button>
</form>
</div>
<br>
</div>
<div id="permedit" class="span12">
</div>
<div id="motdedit" class="span12">
<textarea rows="10" id="motdtext"></textarea>
<button class="btn btn-primary" id="save_motd">Save</button>
</div>
<div id="filteredit" class="span12">
<p>Filters will be processed in the order that they are listed here. Click and drag a row to rearrange the order. Click a regex, flags, or replacement field to edit a filter. Changes are automatically saved when you finish editing.</p>
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Delete</th>
<th>Name</th>
<th>Regex</th>
<th>Flags</th>
<th>Replacement</th>
<th>Affects Links</th>
<th>Active</th>
</tr>
</thead>
</table>
<strong>Add Filter</strong>
<form class="form-horizontal" action="javascript:void(0)">
<div class="control-group">
<label class="control-label" for="newfilter_name">
Name
</label>
<div class="controls">
<input type="text" id="newfilter_name">
</div>
</div>
<div class="control-group">
<label class="control-label" for="newfilter_regex">
Regex
</label>
<div class="controls">
<input type="text" id="newfilter_regex">
</div>
</div>
<div class="control-group">
<label class="control-label" for="newfilter_flags">
Flags
</label>
<div class="controls">
<input type="text" id="newfilter_flags" value="g">
</div>
</div>
<div class="control-group">
<label class="control-label" for="newfilter_replace">
Replacement
</label>
<div class="controls">
<input type="text" id="newfilter_replace">
</div>
</div>
<div class="control-group">
<label class="control-label" for="newfilter_filterlinks">
Filter Links
</label>
<div class="controls">
<input type="checkbox" id="newfilter_filterlinks">
</div>
</div>
<div class="control-group">
<div class="controls">
<button class="btn btn-primary" id="newfilter_submit">New Filter</button>
</div>
</div>
</form>
</div>
<div id="cssedit" class="span12">
<p>Max 20KB. If you need more space, host the file externally and use the External CSS option</p>
<textarea rows="10" id="csstext"></textarea>
<button class="btn btn-primary" id="save_css">Save</button>
</div>
<div id="jsedit" class="span12">
<p>Max 20KB. If you need more space, host the file externally and use the External JS option</p>
<textarea rows="10" id="jstext"></textarea>
<button class="btn btn-primary" id="save_js">Save</button>
</div>
<div id="banlist" class="span12">
<table class="table table-striped">
<thead>
<tr>
<th>Unban</th>
<th>IP</th>
<th>Name</th>
<th>Aliases</th>
<th>Banned By</th>
</tr>
</thead>
</table>
</div>
<div id="loginhistory" class="span12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Aliases</th>
<th>Time</th>
</tr>
</thead>
</table>
</div>
<div id="channelranks" class="span12">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Rank (click to edit)</th>
</tr>
</thead>
</table>
</div>

View File

@ -31,7 +31,7 @@
<div class=""> <div class="">
<ul class="nav"> <ul class="nav">
<li class="active"><a href="index.html">Home</a></li> <li class="active"><a href="index.html">Home</a></li>
<li><a href="help.html">Help</a></li> <li><a href="https://github.com/calzoneman/sync/wiki/Beginner%27s-Guide-and-FAQ" target="_blank">Help</a></li>
<li><a href="account.html">Account</a></li> <li><a href="account.html">Account</a></li>
<li><a href="javascript:void(0)" id="optlink">Options</a></li> <li><a href="javascript:void(0)" id="optlink">Options</a></li>
</ul> </ul>